mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 22:55:23 +08:00
Compare commits
110 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66559b4087 | |||
| d8e835549b | |||
| f2f26b1559 | |||
| 710592283f | |||
| f38f5570d5 | |||
| 3620e69404 | |||
| 34681a1a2a | |||
| 5898845c87 | |||
| 1ef888f9b8 | |||
| 14fb86d1ba | |||
| 65a3e21af4 | |||
| b3c68d8de9 | |||
| 673ada8225 | |||
| 4f9c0fd083 | |||
| db536ee045 | |||
| be81609b43 | |||
| 0c7f455f85 | |||
| d5dfa8c6fe | |||
| e91ec64cac | |||
| 082eb04200 | |||
| fe63899d1a | |||
| 0bd4dcde6b | |||
| 844a35108b | |||
| d6e016be6d | |||
| fefacd33e3 | |||
| b3a6fb7e86 | |||
| 5645207758 | |||
| dc1c90c4e3 | |||
| 9454e744ed | |||
| 7c7df1850a | |||
| efd035164b | |||
| f934a0af46 | |||
| 1eee99fc8c | |||
| 0b81efed1d | |||
| ae82d91492 | |||
| 63e68a4e77 | |||
| b7db7eae26 | |||
| f241d082b6 | |||
| f3bcac39a6 | |||
| 0ad9ee30ce | |||
| 25e31d1a94 | |||
| f337542948 | |||
| 3fc2eaf561 | |||
| 7ea26abb8b | |||
| e03f8a0c12 | |||
| 8ce7cea93f | |||
| c3d7e16252 | |||
| ad32646e18 | |||
| 0b8f885939 | |||
| 49623f9c8e | |||
| 633b7f15d5 | |||
| 3ae9c5309b | |||
| 60ceb8d0e2 | |||
| a180c5cc3a | |||
| 04623e7cd4 | |||
| 1a1c5bda23 | |||
| 6534c6dea4 | |||
| 9cd47ce554 | |||
| 36bc3da97f | |||
| ec77a92882 | |||
| f68706409d | |||
| d4159c8046 | |||
| cd59b139fc | |||
| fcd32e687b | |||
| 190d5c1318 | |||
| bcabee737f | |||
| f246e56d0a | |||
| 2876e6a13b | |||
| bfea9e7c02 | |||
| 6eafdf135a | |||
| 76383bea5d | |||
| bbcf7b192c | |||
| de9df6e818 | |||
| e55018a8ce | |||
| 105d7f0c7c | |||
| fd66005628 | |||
| 60106b91ac | |||
| d5c34fa8f4 | |||
| 5a085d789d | |||
| 40e3f30a28 | |||
| 5dbdb197f1 | |||
| f61df6524a | |||
| b6ad0d958f | |||
| a5e5130556 | |||
| 33f8a08af2 | |||
| 37de4e1d08 | |||
| e2cb9aa27c | |||
| b83263bbe4 | |||
| 1a130fa3c1 | |||
| 98130c5a26 | |||
| 81d1cdca7f | |||
| 409635cb2a | |||
| cad809978a | |||
| dec144f172 | |||
| 8ae0c33a7a | |||
| f219e3d5d8 | |||
| 862304a1fc | |||
| 18463f8c00 | |||
| d583d23d86 | |||
| 727d11ceb4 | |||
| c18c4a081c | |||
| e0c84aa0dc | |||
| 29bec740f5 | |||
| 6904fd0c86 | |||
| 91687d70ea | |||
| d71e5a5beb | |||
| 3bd150c442 | |||
| f07eec1bf8 | |||
| 63a284a6ad | |||
| 8102690a52 |
+18
-18
@@ -2,7 +2,6 @@ version: 2.1
|
||||
|
||||
orbs:
|
||||
win: circleci/windows@2.4.0
|
||||
slack: circleci/slack@3.4.2
|
||||
|
||||
aliases:
|
||||
- ¬ify-on-main-failure
|
||||
@@ -57,7 +56,6 @@ commands:
|
||||
|
||||
post-steps:
|
||||
steps:
|
||||
- slack/status: *notify-on-main-failure
|
||||
- store_test_results: # store test result if there's any
|
||||
path: /tmp/test-results
|
||||
- store_artifacts: # store LOG for debugging if there's any
|
||||
@@ -102,10 +100,16 @@ commands:
|
||||
|
||||
install-benchmark:
|
||||
steps:
|
||||
- run:
|
||||
name: Install ninja build
|
||||
command: sudo apt-get update -y && sudo apt-get install -y ninja-build
|
||||
- run:
|
||||
name: Install benchmark
|
||||
command: |
|
||||
sudo apt-get update -y && sudo apt-get install -y libbenchmark-dev
|
||||
git clone --depth 1 --branch v1.6.1 https://github.com/google/benchmark.git ~/benchmark
|
||||
cd ~/benchmark && mkdir build && cd build
|
||||
cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DBENCHMARK_ENABLE_GTEST_TESTS=0
|
||||
ninja && sudo ninja install
|
||||
|
||||
upgrade-cmake:
|
||||
steps:
|
||||
@@ -362,7 +366,7 @@ jobs:
|
||||
- run: CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 CLANG_ANALYZER="/usr/bin/clang++-10" CLANG_SCAN_BUILD=scan-build-10 USE_CLANG=1 make V=1 -j32 analyze # aligned new doesn't work for reason we haven't figured out. For unknown, reason passing "clang++-10" as CLANG_ANALYZER doesn't work, and we need a full path.
|
||||
- post-steps
|
||||
|
||||
build-linux-cmake:
|
||||
build-linux-cmake-with-folly:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: 2xlarge
|
||||
@@ -370,10 +374,11 @@ jobs:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- upgrade-cmake
|
||||
- run: (mkdir build && cd build && cmake -DWITH_GFLAGS=1 .. && make V=1 -j20 && ctest -j20)
|
||||
- run: make checkout_folly
|
||||
- run: (mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 .. && make V=1 -j20 && ctest -j20)
|
||||
- post-steps
|
||||
|
||||
build-linux-cmake-ubuntu-20:
|
||||
build-linux-cmake-with-benchmark:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: 2xlarge
|
||||
@@ -395,14 +400,15 @@ jobs:
|
||||
- run: make V=1 -j8 -k check-headers # could be moved to a different build
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-7:
|
||||
build-linux-gcc-7-with-folly:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test && sudo apt-get update -y && sudo apt-get install gcc-7 g++-7 libgflags-dev
|
||||
- run: CC=gcc-7 CXX=g++-7 V=1 make -j32 check
|
||||
- run: make checkout_folly
|
||||
- run: USE_FOLLY=1 CC=gcc-7 CXX=g++-7 V=1 make -j32 check
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-8-no_test_run:
|
||||
@@ -431,8 +437,8 @@ jobs:
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-benchmark
|
||||
- run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test && sudo apt-get update -y && sudo apt-get install gcc-11 g++-11 libgflags-dev
|
||||
- install-benchmark
|
||||
- run: CC=gcc-11 CXX=g++-11 V=1 make -j16 all microbench
|
||||
- post-steps
|
||||
|
||||
@@ -553,9 +559,6 @@ jobs:
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Shared Library"
|
||||
command: make V=1 J=8 -j8 rocksdbjava
|
||||
- run:
|
||||
name: "Test RocksDBJava"
|
||||
command: make V=1 J=8 -j8 jtest
|
||||
@@ -601,9 +604,6 @@ jobs:
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Shared Library"
|
||||
command: make V=1 J=16 -j16 rocksdbjava
|
||||
- run:
|
||||
name: "Test RocksDBJava"
|
||||
command: make V=1 J=16 -j16 jtest
|
||||
@@ -799,8 +799,8 @@ workflows:
|
||||
- build-linux
|
||||
build-linux-cmake:
|
||||
jobs:
|
||||
- build-linux-cmake
|
||||
- build-linux-cmake-ubuntu-20
|
||||
- build-linux-cmake-with-folly
|
||||
- build-linux-cmake-with-benchmark
|
||||
build-linux-encrypted-env:
|
||||
jobs:
|
||||
- build-linux-encrypted-env
|
||||
@@ -871,7 +871,7 @@ workflows:
|
||||
jobs:
|
||||
- build-linux-clang-no_test_run
|
||||
- build-linux-clang-13-no_test_run
|
||||
- build-linux-gcc-7
|
||||
- build-linux-gcc-7-with-folly
|
||||
- build-linux-gcc-8-no_test_run
|
||||
- build-linux-gcc-10-cxx20-no_test_run
|
||||
- build-linux-gcc-11-no_test_run
|
||||
|
||||
@@ -95,3 +95,4 @@ fuzz/proto/gen/
|
||||
fuzz/crash-*
|
||||
|
||||
cmake-build-*
|
||||
third-party/folly/
|
||||
|
||||
+2
-2
@@ -239,10 +239,10 @@ script:
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=only make -j$MK_PARALLEL all_but_some_tests check_some
|
||||
;;
|
||||
1)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_END=backupable_db_test make -j$MK_PARALLEL check_some
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_END=backup_engine_test make -j$MK_PARALLEL check_some
|
||||
;;
|
||||
2)
|
||||
OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" LIB_MODE=shared V=1 make -j$MK_PARALLEL tools && OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=backupable_db_test ROCKSDBTESTS_END=db_universal_compaction_test make -j$MK_PARALLEL check_some
|
||||
OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" LIB_MODE=shared V=1 make -j$MK_PARALLEL tools && OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=backup_engine_test ROCKSDBTESTS_END=db_universal_compaction_test make -j$MK_PARALLEL check_some
|
||||
;;
|
||||
3)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=db_universal_compaction_test ROCKSDBTESTS_END=table_properties_collector_test make -j$MK_PARALLEL check_some
|
||||
|
||||
+89
-70
@@ -2,7 +2,7 @@
|
||||
# This cmake build is for Windows 64-bit only.
|
||||
#
|
||||
# Prerequisites:
|
||||
# You must have at least Visual Studio 2015 Update 3. Start the Developer Command Prompt window that is a part of Visual Studio installation.
|
||||
# You must have at least Visual Studio 2019. Start the Developer Command Prompt window that is a part of Visual Studio installation.
|
||||
# Run the build commands from within the Developer Command Prompt window to have paths to the compiler and runtime libraries set.
|
||||
# You must have git.exe in your %PATH% environment variable.
|
||||
#
|
||||
@@ -78,19 +78,6 @@ if ($ENV{CIRCLECI})
|
||||
add_definitions(-DCIRCLECI)
|
||||
endif()
|
||||
|
||||
# third-party/folly is only validated to work on Linux and Windows for now.
|
||||
# So only turn it on there by default.
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux|Windows")
|
||||
if(MSVC AND MSVC_VERSION LESS 1910)
|
||||
# Folly does not compile with MSVC older than VS2017
|
||||
option(WITH_FOLLY_DISTRIBUTED_MUTEX "build with folly::DistributedMutex" OFF)
|
||||
else()
|
||||
option(WITH_FOLLY_DISTRIBUTED_MUTEX "build with folly::DistributedMutex" ON)
|
||||
endif()
|
||||
else()
|
||||
option(WITH_FOLLY_DISTRIBUTED_MUTEX "build with folly::DistributedMutex" OFF)
|
||||
endif()
|
||||
|
||||
if( NOT DEFINED CMAKE_CXX_STANDARD )
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
@@ -182,26 +169,6 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
string(TIMESTAMP TS "%Y-%m-%d %H:%M:%S" UTC)
|
||||
set(BUILD_DATE "${TS}" CACHE STRING "the time we first built rocksdb")
|
||||
|
||||
find_package(Git)
|
||||
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_SHA COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD )
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE GIT_MOD COMMAND "${GIT_EXECUTABLE}" diff-index HEAD --quiet)
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_DATE COMMAND "${GIT_EXECUTABLE}" log -1 --date=format:"%Y-%m-%d %T" --format="%ad")
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_TAG RESULT_VARIABLE rv COMMAND "${GIT_EXECUTABLE}" symbolic-ref -q --short HEAD OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (rv AND NOT rv EQUAL 0)
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_TAG COMMAND "${GIT_EXECUTABLE}" describe --tags --exact-match OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
else()
|
||||
set(GIT_SHA 0)
|
||||
set(GIT_MOD 1)
|
||||
endif()
|
||||
string(REGEX REPLACE "[^0-9a-fA-F]+" "" GIT_SHA "${GIT_SHA}")
|
||||
string(REGEX REPLACE "[^0-9: /-]+" "" GIT_DATE "${GIT_DATE}")
|
||||
|
||||
option(WITH_MD_LIBRARY "build with MD" ON)
|
||||
if(WIN32 AND MSVC)
|
||||
if(WITH_MD_LIBRARY)
|
||||
@@ -211,9 +178,6 @@ if(WIN32 AND MSVC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(BUILD_VERSION_CC ${CMAKE_BINARY_DIR}/build_version.cc)
|
||||
configure_file(util/build_version.cc.in ${BUILD_VERSION_CC} @ONLY)
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W4 /wd4127 /wd4800 /wd4996 /wd4351 /wd4100 /wd4204 /wd4324")
|
||||
@@ -456,30 +420,32 @@ if (ASSERT_STATUS_CHECKED)
|
||||
add_definitions(-DROCKSDB_ASSERT_STATUS_CHECKED)
|
||||
endif()
|
||||
|
||||
if(DEFINED USE_RTTI)
|
||||
if(USE_RTTI)
|
||||
message(STATUS "Enabling RTTI")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DROCKSDB_USE_RTTI")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DROCKSDB_USE_RTTI")
|
||||
else()
|
||||
if(MSVC)
|
||||
message(STATUS "Disabling RTTI in Release builds. Always on in Debug.")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DROCKSDB_USE_RTTI")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GR-")
|
||||
else()
|
||||
message(STATUS "Disabling RTTI in Release builds")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-rtti")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-rtti")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
|
||||
# RTTI is by default AUTO which enables it in debug and disables it in release.
|
||||
set(USE_RTTI AUTO CACHE STRING "Enable RTTI in builds")
|
||||
set_property(CACHE USE_RTTI PROPERTY STRINGS AUTO ON OFF)
|
||||
if(USE_RTTI STREQUAL "AUTO")
|
||||
message(STATUS "Enabling RTTI in Debug builds only (default)")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DROCKSDB_USE_RTTI")
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GR-")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GR-")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-rtti")
|
||||
endif()
|
||||
elseif(USE_RTTI)
|
||||
message(STATUS "Enabling RTTI in all builds")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DROCKSDB_USE_RTTI")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DROCKSDB_USE_RTTI")
|
||||
else()
|
||||
if(MSVC)
|
||||
message(STATUS "Disabling RTTI in Release builds. Always on in Debug.")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DROCKSDB_USE_RTTI")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GR-")
|
||||
else()
|
||||
message(STATUS "Disabling RTTI in all builds")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-rtti")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-rtti")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Used to run CI build and tests so we can run faster
|
||||
@@ -615,8 +581,9 @@ endif()
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
if(WITH_FOLLY_DISTRIBUTED_MUTEX)
|
||||
if(USE_FOLLY)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third-party/folly)
|
||||
add_definitions(-DUSE_FOLLY -DFOLLY_NO_CONFIG)
|
||||
endif()
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
@@ -628,8 +595,8 @@ set(SOURCES
|
||||
cache/cache_key.cc
|
||||
cache/cache_reservation_manager.cc
|
||||
cache/clock_cache.cc
|
||||
cache/compressed_secondary_cache.cc
|
||||
cache/lru_cache.cc
|
||||
cache/lru_secondary_cache.cc
|
||||
cache/sharded_cache.cc
|
||||
db/arena_wrapped_db_iter.cc
|
||||
db/blob/blob_fetcher.cc
|
||||
@@ -849,7 +816,8 @@ set(SOURCES
|
||||
util/thread_local.cc
|
||||
util/threadpool_imp.cc
|
||||
util/xxhash.cc
|
||||
utilities/backupable/backupable_db.cc
|
||||
utilities/agg_merge/agg_merge.cc
|
||||
utilities/backup/backup_engine.cc
|
||||
utilities/blob_db/blob_compaction_filter.cc
|
||||
utilities/blob_db/blob_db.cc
|
||||
utilities/blob_db/blob_db_impl.cc
|
||||
@@ -998,13 +966,12 @@ else()
|
||||
env/io_posix.cc)
|
||||
endif()
|
||||
|
||||
if(WITH_FOLLY_DISTRIBUTED_MUTEX)
|
||||
if(USE_FOLLY)
|
||||
list(APPEND SOURCES
|
||||
third-party/folly/folly/detail/Futex.cpp
|
||||
third-party/folly/folly/synchronization/AtomicNotification.cpp
|
||||
third-party/folly/folly/synchronization/DistributedMutex.cpp
|
||||
third-party/folly/folly/synchronization/ParkingLot.cpp
|
||||
third-party/folly/folly/synchronization/WaitOptions.cpp)
|
||||
third-party/folly/folly/container/detail/F14Table.cpp
|
||||
third-party/folly/folly/lang/SafeAssert.cpp
|
||||
third-party/folly/folly/lang/ToAscii.cpp
|
||||
third-party/folly/folly/ScopeGuard.cpp)
|
||||
endif()
|
||||
|
||||
set(ROCKSDB_STATIC_LIB rocksdb${ARTIFACT_SUFFIX})
|
||||
@@ -1019,6 +986,60 @@ else()
|
||||
set(SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
set(ROCKSDB_PLUGIN_EXTERNS "")
|
||||
set(ROCKSDB_PLUGIN_BUILTINS "")
|
||||
message(STATUS "ROCKSDB PLUGINS TO BUILD ${ROCKSDB_PLUGINS}")
|
||||
list(APPEND PLUGINS ${ROCKSDB_PLUGINS})
|
||||
foreach(PLUGIN IN LISTS PLUGINS)
|
||||
set(PLUGIN_ROOT "${CMAKE_SOURCE_DIR}/plugin/${PLUGIN}/")
|
||||
message("including rocksb plugin ${PLUGIN_ROOT}")
|
||||
set(PLUGINMKFILE "${PLUGIN_ROOT}${PLUGIN}.mk")
|
||||
if (NOT EXISTS ${PLUGINMKFILE})
|
||||
message(FATAL_ERROR "Missing plugin makefile: ${PLUGINMKFILE}")
|
||||
endif()
|
||||
file(READ ${PLUGINMKFILE} PLUGINMK)
|
||||
string(REGEX MATCH "SOURCES = ([^\n]*)" FOO ${PLUGINMK})
|
||||
set(MK_SOURCES ${CMAKE_MATCH_1})
|
||||
separate_arguments(MK_SOURCES)
|
||||
foreach(MK_FILE IN LISTS MK_SOURCES)
|
||||
list(APPEND SOURCES "${PLUGIN_ROOT}${MK_FILE}")
|
||||
endforeach()
|
||||
string(REGEX MATCH "_FUNC = ([^\n]*)" FOO ${PLUGINMK})
|
||||
if (NOT ${CMAKE_MATCH_1} STREQUAL "")
|
||||
string(APPEND ROCKSDB_PLUGIN_BUILTINS "{\"${PLUGIN}\", " ${CMAKE_MATCH_1} "},")
|
||||
string(APPEND ROCKSDB_PLUGIN_EXTERNS "int " ${CMAKE_MATCH_1} "(ROCKSDB_NAMESPACE::ObjectLibrary&, const std::string&); ")
|
||||
endif()
|
||||
string(REGEX MATCH "_LIBS = ([^\n]*)" FOO ${PLUGINMK})
|
||||
if (NOT ${CMAKE_MATCH_1} STREQUAL "")
|
||||
list(APPEND THIRDPARTY_LIBS "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
message("THIRDPARTY_LIBS=${THIRDPARTY_LIBS}")
|
||||
#TODO: We need to set any compile/link-time flags and add any link libraries
|
||||
endforeach()
|
||||
|
||||
string(TIMESTAMP TS "%Y-%m-%d %H:%M:%S" UTC)
|
||||
set(BUILD_DATE "${TS}" CACHE STRING "the time we first built rocksdb")
|
||||
|
||||
find_package(Git)
|
||||
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_SHA COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD )
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE GIT_MOD COMMAND "${GIT_EXECUTABLE}" diff-index HEAD --quiet)
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_DATE COMMAND "${GIT_EXECUTABLE}" log -1 --date=format:"%Y-%m-%d %T" --format="%ad")
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_TAG RESULT_VARIABLE rv COMMAND "${GIT_EXECUTABLE}" symbolic-ref -q --short HEAD OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (rv AND NOT rv EQUAL 0)
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_TAG COMMAND "${GIT_EXECUTABLE}" describe --tags --exact-match OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
else()
|
||||
set(GIT_SHA 0)
|
||||
set(GIT_MOD 1)
|
||||
endif()
|
||||
string(REGEX REPLACE "[^0-9a-fA-F]+" "" GIT_SHA "${GIT_SHA}")
|
||||
string(REGEX REPLACE "[^0-9: /-]+" "" GIT_DATE "${GIT_DATE}")
|
||||
|
||||
set(BUILD_VERSION_CC ${CMAKE_BINARY_DIR}/build_version.cc)
|
||||
configure_file(util/build_version.cc.in ${BUILD_VERSION_CC} @ONLY)
|
||||
|
||||
add_library(${ROCKSDB_STATIC_LIB} STATIC ${SOURCES} ${BUILD_VERSION_CC})
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB} PRIVATE
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
@@ -1157,8 +1178,8 @@ if(WITH_TESTS)
|
||||
list(APPEND TESTS
|
||||
cache/cache_reservation_manager_test.cc
|
||||
cache/cache_test.cc
|
||||
cache/compressed_secondary_cache_test.cc
|
||||
cache/lru_cache_test.cc
|
||||
cache/lru_secondary_cache_test.cc
|
||||
db/blob/blob_counting_iterator_test.cc
|
||||
db/blob/blob_file_addition_test.cc
|
||||
db/blob/blob_file_builder_test.cc
|
||||
@@ -1315,7 +1336,8 @@ if(WITH_TESTS)
|
||||
util/thread_list_test.cc
|
||||
util/thread_local_test.cc
|
||||
util/work_queue_test.cc
|
||||
utilities/backupable/backupable_db_test.cc
|
||||
utilities/agg_merge/agg_merge_test.cc
|
||||
utilities/backup/backup_engine_test.cc
|
||||
utilities/blob_db/blob_db_test.cc
|
||||
utilities/cassandra/cassandra_functional_test.cc
|
||||
utilities/cassandra/cassandra_format_test.cc
|
||||
@@ -1346,14 +1368,11 @@ if(WITH_TESTS)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_FOLLY_DISTRIBUTED_MUTEX)
|
||||
list(APPEND TESTS third-party/folly/folly/synchronization/test/DistributedMutexTest.cpp)
|
||||
endif()
|
||||
|
||||
set(TESTUTIL_SOURCE
|
||||
db/db_test_util.cc
|
||||
monitoring/thread_status_updater_debug.cc
|
||||
table/mock_table.cc
|
||||
utilities/agg_merge/test_agg_merge.cc
|
||||
utilities/cassandra/test_utils.cc
|
||||
)
|
||||
enable_testing()
|
||||
|
||||
+45
-1
@@ -1,4 +1,48 @@
|
||||
# Rocksdb Change Log
|
||||
## 7.2.2 (04/28/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a bug in async_io path where incorrect length of data is read by FilePrefetchBuffer if data is consumed from two populated buffers and request for more data is sent.
|
||||
|
||||
## 7.2.1 (04/26/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a bug where RocksDB could corrupt DBs with `avoid_flush_during_recovery == true` by removing valid WALs, leading to `Status::Corruption` with message like "SST file is ahead of WALs" when attempting to reopen.
|
||||
* RocksDB calls FileSystem::Poll API during FilePrefetchBuffer destruction which impacts performance as it waits for read requets completion which is not needed anymore. Calling FileSystem::AbortIO to abort those requests instead fixes that performance issue.
|
||||
|
||||
## 7.2.0 (04/15/2022)
|
||||
### Bug Fixes
|
||||
* Fixed bug which caused rocksdb failure in the situation when rocksdb was accessible using UNC path
|
||||
* Fixed a race condition when 2PC is disabled and WAL tracking in the MANIFEST is enabled. The race condition is between two background flush threads trying to install flush results, causing a WAL deletion not tracked in the MANIFEST. A future DB open may fail.
|
||||
* Fixed a heap use-after-free race with DropColumnFamily.
|
||||
* Fixed a bug that `rocksdb.read.block.compaction.micros` cannot track compaction stats (#9722).
|
||||
* Fixed `file_type`, `relative_filename` and `directory` fields returned by `GetLiveFilesMetaData()`, which were added in inheriting from `FileStorageInfo`.
|
||||
* Fixed a bug affecting `track_and_verify_wals_in_manifest`. Without the fix, application may see "open error: Corruption: Missing WAL with log number" while trying to open the db. The corruption is a false alarm but prevents DB open (#9766).
|
||||
* Fix segfault in FilePrefetchBuffer with async_io as it doesn't wait for pending jobs to complete on destruction.
|
||||
* Fix ERROR_HANDLER_AUTORESUME_RETRY_COUNT stat whose value was set wrong in portal.h
|
||||
* Fixed a bug for non-TransactionDB with avoid_flush_during_recovery = true and TransactionDB where in case of crash, min_log_number_to_keep may not change on recovery and persisting a new MANIFEST with advanced log_numbers for some column families, results in "column family inconsistency" error on second recovery. As a solution the corrupted WALs whose numbers are larger than the corrupted wal and smaller than the new WAL will be moved to archive folder.
|
||||
* Fixed a bug in RocksDB DB::Open() which may creates and writes to two new MANIFEST files even before recovery succeeds. Now writes to MANIFEST are persisted only after recovery is successful.
|
||||
|
||||
### New Features
|
||||
* For db_bench when --seed=0 or --seed is not set then it uses the current time as the seed value. Previously it used the value 1000.
|
||||
* For db_bench when --benchmark lists multiple tests and each test uses a seed for a RNG then the seeds across tests will no longer be repeated.
|
||||
* Added an option to dynamically charge an updating estimated memory usage of block-based table reader to block cache if block cache available. To enable this feature, set `BlockBasedTableOptions::reserve_table_reader_memory = true`.
|
||||
* Add new stat ASYNC_READ_BYTES that calculates number of bytes read during async read call and users can check if async code path is being called by RocksDB internal automatic prefetching for sequential reads.
|
||||
* Enable async prefetching if ReadOptions.readahead_size is set along with ReadOptions.async_io in FilePrefetchBuffer.
|
||||
* Add event listener support on remote compaction compactor side.
|
||||
* Added a dedicated integer DB property `rocksdb.live-blob-file-garbage-size` that exposes the total amount of garbage in the blob files in the current version.
|
||||
* RocksDB does internal auto prefetching if it notices sequential reads. It starts with readahead size `initial_auto_readahead_size` which now can be configured through BlockBasedTableOptions.
|
||||
* Add a merge operator that allows users to register specific aggregation function so that they can does aggregation using different aggregation types for different keys. See comments in include/rocksdb/utilities/agg_merge.h for actual usage. The feature is experimental and the format is subject to change and we won't provide a migration tool.
|
||||
* Meta-internal / Experimental: Improve CPU performance by replacing many uses of std::unordered_map with folly::F14FastMap when RocksDB is compiled together with Folly.
|
||||
* Experimental: Add CompressedSecondaryCache, a concrete implementation of rocksdb::SecondaryCache, that integrates with compression libraries (e.g. LZ4) to hold compressed blocks.
|
||||
|
||||
### Behavior changes
|
||||
* Disallow usage of commit-time-write-batch for write-prepared/write-unprepared transactions if TransactionOptions::use_only_the_last_commit_time_batch_for_recovery is false to prevent two (or more) uncommitted versions of the same key in the database. Otherwise, bottommost compaction may violate the internal key uniqueness invariant of SSTs if the sequence numbers of both internal keys are zeroed out (#9794).
|
||||
* Make DB::GetUpdatesSince() return NotSupported early for write-prepared/write-unprepared transactions, as the API contract indicates.
|
||||
|
||||
### Public API changes
|
||||
* Exposed APIs to examine results of block cache stats collections in a structured way. In particular, users of `GetMapProperty()` with property `kBlockCacheEntryStats` can now use the functions in `BlockCacheEntryStatsMapKeys` to find stats in the map.
|
||||
* Add `fail_if_not_bottommost_level` to IngestExternalFileOptions so that ingestion will fail if the file(s) cannot be ingested to the bottommost level.
|
||||
* Add output parameter `is_in_sec_cache` to `SecondaryCache::Lookup()`. It is to indicate whether the handle is possibly erased from the secondary cache after the Lookup.
|
||||
|
||||
## 7.1.0 (03/23/2022)
|
||||
### New Features
|
||||
* Allow WriteBatchWithIndex to index a WriteBatch that includes keys with user-defined timestamps. The index itself does not have timestamp.
|
||||
@@ -1658,7 +1702,7 @@ if set to something > 0 user will see 2 changes in iterators behavior 1) only ke
|
||||
* Added a new way to report QPS from db_bench (check out --report_file and --report_interval_seconds)
|
||||
* Added a cache for individual rows. See DBOptions::row_cache for more info.
|
||||
* Several new features on EventListener (see include/rocksdb/listener.h):
|
||||
- OnCompationCompleted() now returns per-compaction job statistics, defined in include/rocksdb/compaction_job_stats.h.
|
||||
- OnCompactionCompleted() now returns per-compaction job statistics, defined in include/rocksdb/compaction_job_stats.h.
|
||||
- Added OnTableFileCreated() and OnTableFileDeleted().
|
||||
* Add compaction_options_universal.enable_trivial_move to true, to allow trivial move while performing universal compaction. Trivial move will happen only when all the input files are non overlapping.
|
||||
|
||||
|
||||
+2
-3
@@ -47,7 +47,7 @@ to build a portable binary, add `PORTABLE=1` before your make commands, like thi
|
||||
|
||||
* If you wish to build the RocksJava static target, then cmake is required for building Snappy.
|
||||
|
||||
* If you wish to run microbench (e.g, `make microbench`, `make ribbon_bench` or `cmake -DWITH_BENCHMARK=1`), Google benchmark < 1.6.0 is needed.
|
||||
* If you wish to run microbench (e.g, `make microbench`, `make ribbon_bench` or `cmake -DWITH_BENCHMARK=1`), Google benchmark >= 1.6.0 is needed.
|
||||
|
||||
## Supported platforms
|
||||
|
||||
@@ -180,8 +180,7 @@ to build a portable binary, add `PORTABLE=1` before your make commands, like thi
|
||||
* **iOS**:
|
||||
* Run: `TARGET_OS=IOS make static_lib`. When building the project which uses rocksdb iOS library, make sure to define two important pre-processing macros: `ROCKSDB_LITE` and `IOS_CROSS_COMPILE`.
|
||||
|
||||
* **Windows**:
|
||||
* For building with MS Visual Studio 13 you will need Update 4 installed.
|
||||
* **Windows** (Visual Studio 2017 to up):
|
||||
* Read and follow the instructions at CMakeLists.txt
|
||||
* Or install via [vcpkg](https://github.com/microsoft/vcpkg)
|
||||
* run `vcpkg install rocksdb:x64-windows`
|
||||
|
||||
@@ -232,14 +232,20 @@ include make_config.mk
|
||||
|
||||
ROCKSDB_PLUGIN_MKS = $(foreach plugin, $(ROCKSDB_PLUGINS), plugin/$(plugin)/*.mk)
|
||||
include $(ROCKSDB_PLUGIN_MKS)
|
||||
ROCKSDB_PLUGIN_SOURCES = $(foreach plugin, $(ROCKSDB_PLUGINS), $(foreach source, $($(plugin)_SOURCES), plugin/$(plugin)/$(source)))
|
||||
ROCKSDB_PLUGIN_HEADERS = $(foreach plugin, $(ROCKSDB_PLUGINS), $(foreach header, $($(plugin)_HEADERS), plugin/$(plugin)/$(header)))
|
||||
ROCKSDB_PLUGIN_PROTO =ROCKSDB_NAMESPACE::ObjectLibrary\&, const std::string\&
|
||||
ROCKSDB_PLUGIN_SOURCES = $(foreach p, $(ROCKSDB_PLUGINS), $(foreach source, $($(p)_SOURCES), plugin/$(p)/$(source)))
|
||||
ROCKSDB_PLUGIN_HEADERS = $(foreach p, $(ROCKSDB_PLUGINS), $(foreach header, $($(p)_HEADERS), plugin/$(p)/$(header)))
|
||||
ROCKSDB_PLUGIN_LIBS = $(foreach p, $(ROCKSDB_PLUGINS), $(foreach lib, $($(p)_LIBS), -l$(lib)))
|
||||
ROCKSDB_PLUGIN_W_FUNCS = $(foreach p, $(ROCKSDB_PLUGINS), $(if $($(p)_FUNC), $(p)))
|
||||
ROCKSDB_PLUGIN_EXTERNS = $(foreach p, $(ROCKSDB_PLUGIN_W_FUNCS), int $($(p)_FUNC)($(ROCKSDB_PLUGIN_PROTO));)
|
||||
ROCKSDB_PLUGIN_BUILTINS = $(foreach p, $(ROCKSDB_PLUGIN_W_FUNCS), {\"$(p)\"\, $($(p)_FUNC)}\,)
|
||||
ROCKSDB_PLUGIN_LDFLAGS = $(foreach plugin, $(ROCKSDB_PLUGINS), $($(plugin)_LDFLAGS))
|
||||
ROCKSDB_PLUGIN_PKGCONFIG_REQUIRES = $(foreach plugin, $(ROCKSDB_PLUGINS), $($(plugin)_PKGCONFIG_REQUIRES))
|
||||
|
||||
CXXFLAGS += $(foreach plugin, $(ROCKSDB_PLUGINS), $($(plugin)_CXXFLAGS))
|
||||
PLATFORM_LDFLAGS += $(ROCKSDB_PLUGIN_LDFLAGS)
|
||||
|
||||
# Patch up the link flags for JNI from the plugins
|
||||
ROCKSDB_PLUGIN_LDFLAGS = $(foreach plugin, $(ROCKSDB_PLUGINS), $($(plugin)_LDFLAGS))
|
||||
PLATFORM_LDFLAGS += $(ROCKSDB_PLUGIN_LDFLAGS)
|
||||
JAVA_LDFLAGS += $(ROCKSDB_PLUGIN_LDFLAGS)
|
||||
JAVA_STATIC_LDFLAGS += $(ROCKSDB_PLUGIN_LDFLAGS)
|
||||
|
||||
@@ -282,7 +288,7 @@ missing_make_config_paths := $(shell \
|
||||
grep "\./\S*\|/\S*" -o $(CURDIR)/make_config.mk | \
|
||||
while read path; \
|
||||
do [ -e $$path ] || echo $$path; \
|
||||
done | sort | uniq)
|
||||
done | sort | uniq | grep -v "/DOES/NOT/EXIST")
|
||||
|
||||
$(foreach path, $(missing_make_config_paths), \
|
||||
$(warning Warning: $(path) does not exist))
|
||||
@@ -394,6 +400,10 @@ ifndef DISABLE_JEMALLOC
|
||||
ifdef JEMALLOC
|
||||
PLATFORM_CXXFLAGS += -DROCKSDB_JEMALLOC -DJEMALLOC_NO_DEMANGLE
|
||||
PLATFORM_CCFLAGS += -DROCKSDB_JEMALLOC -DJEMALLOC_NO_DEMANGLE
|
||||
ifeq ($(USE_FOLLY),1)
|
||||
PLATFORM_CXXFLAGS += -DUSE_JEMALLOC
|
||||
PLATFORM_CCFLAGS += -DUSE_JEMALLOC
|
||||
endif
|
||||
endif
|
||||
ifdef WITH_JEMALLOC_FLAG
|
||||
PLATFORM_LDFLAGS += -ljemalloc
|
||||
@@ -404,8 +414,8 @@ ifndef DISABLE_JEMALLOC
|
||||
PLATFORM_CCFLAGS += $(JEMALLOC_INCLUDE)
|
||||
endif
|
||||
|
||||
ifndef USE_FOLLY_DISTRIBUTED_MUTEX
|
||||
USE_FOLLY_DISTRIBUTED_MUTEX=0
|
||||
ifndef USE_FOLLY
|
||||
USE_FOLLY=0
|
||||
endif
|
||||
|
||||
ifndef GTEST_THROW_ON_FAILURE
|
||||
@@ -425,8 +435,12 @@ else
|
||||
PLATFORM_CXXFLAGS += -isystem $(GTEST_DIR)
|
||||
endif
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
FOLLY_DIR = ./third-party/folly
|
||||
# This provides a Makefile simulation of a Meta-internal folly integration.
|
||||
# It is not validated for general use.
|
||||
ifeq ($(USE_FOLLY),1)
|
||||
ifeq (,$(FOLLY_DIR))
|
||||
FOLLY_DIR = ./third-party/folly
|
||||
endif
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PLATFORM_CCFLAGS += -I$(FOLLY_DIR)
|
||||
@@ -435,6 +449,8 @@ ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
PLATFORM_CCFLAGS += -isystem $(FOLLY_DIR)
|
||||
PLATFORM_CXXFLAGS += -isystem $(FOLLY_DIR)
|
||||
endif
|
||||
PLATFORM_CCFLAGS += -DUSE_FOLLY -DFOLLY_NO_CONFIG
|
||||
PLATFORM_CXXFLAGS += -DUSE_FOLLY -DFOLLY_NO_CONFIG
|
||||
endif
|
||||
|
||||
ifdef TEST_CACHE_LINE_SIZE
|
||||
@@ -521,7 +537,7 @@ LIB_OBJECTS += $(patsubst %.c, $(OBJ_DIR)/%.o, $(LIB_SOURCES_C))
|
||||
LIB_OBJECTS += $(patsubst %.S, $(OBJ_DIR)/%.o, $(LIB_SOURCES_ASM))
|
||||
endif
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
ifeq ($(USE_FOLLY),1)
|
||||
LIB_OBJECTS += $(patsubst %.cpp, $(OBJ_DIR)/%.o, $(FOLLY_SOURCES))
|
||||
endif
|
||||
|
||||
@@ -556,11 +572,6 @@ ALL_SOURCES += $(ROCKSDB_PLUGIN_SOURCES)
|
||||
TESTS = $(patsubst %.cc, %, $(notdir $(TEST_MAIN_SOURCES)))
|
||||
TESTS += $(patsubst %.c, %, $(notdir $(TEST_MAIN_SOURCES_C)))
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
TESTS += folly_synchronization_distributed_mutex_test
|
||||
ALL_SOURCES += third-party/folly/folly/synchronization/test/DistributedMutexTest.cc
|
||||
endif
|
||||
|
||||
# `make check-headers` to very that each header file includes its own
|
||||
# dependencies
|
||||
ifneq ($(filter check-headers, $(MAKECMDGOALS)),)
|
||||
@@ -607,10 +618,13 @@ ROCKSDBTESTS_SUBSET ?= $(TESTS)
|
||||
# env_test - suspicious use of test::TmpDir
|
||||
# deletefile_test - serial because it generates giant temporary files in
|
||||
# its various tests. Parallel can fill up your /dev/shm
|
||||
# db_bloom_filter_test - serial because excessive space usage by instances
|
||||
# of DBFilterConstructionReserveMemoryTestWithParam can fill up /dev/shm
|
||||
NON_PARALLEL_TEST = \
|
||||
c_test \
|
||||
env_test \
|
||||
deletefile_test \
|
||||
db_bloom_filter_test \
|
||||
|
||||
PARALLEL_TEST = $(filter-out $(NON_PARALLEL_TEST), $(TESTS))
|
||||
|
||||
@@ -728,7 +742,7 @@ else
|
||||
git_mod := $(shell git diff-index HEAD --quiet 2>/dev/null; echo $$?)
|
||||
git_date := $(shell git log -1 --date=format:"%Y-%m-%d %T" --format="%ad" 2>/dev/null)
|
||||
endif
|
||||
gen_build_version = sed -e s/@GIT_SHA@/$(git_sha)/ -e s:@GIT_TAG@:"$(git_tag)": -e s/@GIT_MOD@/"$(git_mod)"/ -e s/@BUILD_DATE@/"$(build_date)"/ -e s/@GIT_DATE@/"$(git_date)"/ util/build_version.cc.in
|
||||
gen_build_version = sed -e s/@GIT_SHA@/$(git_sha)/ -e s:@GIT_TAG@:"$(git_tag)": -e s/@GIT_MOD@/"$(git_mod)"/ -e s/@BUILD_DATE@/"$(build_date)"/ -e s/@GIT_DATE@/"$(git_date)"/ -e s/@ROCKSDB_PLUGIN_BUILTINS@/'$(ROCKSDB_PLUGIN_BUILTINS)'/ -e s/@ROCKSDB_PLUGIN_EXTERNS@/"$(ROCKSDB_PLUGIN_EXTERNS)"/ util/build_version.cc.in
|
||||
|
||||
# Record the version of the source that we are compiling.
|
||||
# We keep a record of the git revision in this file. It is then built
|
||||
@@ -785,7 +799,7 @@ endif # PLATFORM_SHARED_EXT
|
||||
.PHONY: blackbox_crash_test check clean coverage crash_test ldb_tests package \
|
||||
release tags tags0 valgrind_check whitebox_crash_test format static_lib shared_lib all \
|
||||
dbg rocksdbjavastatic rocksdbjava gen-pc install install-static install-shared uninstall \
|
||||
analyze tools tools_lib check-headers \
|
||||
analyze tools tools_lib check-headers checkout_folly \
|
||||
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_best_efforts_recovery \
|
||||
@@ -826,8 +840,8 @@ release: clean
|
||||
coverage: clean
|
||||
COVERAGEFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS+="-lgcov" $(MAKE) J=1 all check
|
||||
cd coverage && ./coverage_test.sh
|
||||
# Delete intermediate files
|
||||
$(FIND) . -type f -regex ".*\.\(\(gcda\)\|\(gcno\)\)" -exec rm -f {} \;
|
||||
# Delete intermediate files
|
||||
$(FIND) . -type f \( -name "*.gcda" -o -name "*.gcno" \) -exec rm -f {} \;
|
||||
|
||||
ifneq (,$(filter check parallel_check,$(MAKECMDGOALS)),)
|
||||
# Use /dev/shm if it has the sticky bit set (otherwise, /tmp),
|
||||
@@ -1214,9 +1228,9 @@ clean-rocks:
|
||||
rm -f $(BENCHMARKS) $(TOOLS) $(TESTS) $(PARALLEL_TEST) $(ALL_STATIC_LIBS) $(ALL_SHARED_LIBS) $(MICROBENCHS)
|
||||
rm -rf $(CLEAN_FILES) ios-x86 ios-arm scan_build_report
|
||||
$(FIND) . -name "*.[oda]" -exec rm -f {} \;
|
||||
$(FIND) . -type f -regex ".*\.\(\(gcda\)\|\(gcno\)\)" -exec rm -f {} \;
|
||||
$(FIND) . -type f \( -name "*.gcda" -o -name "*.gcno" \) -exec rm -f {} \;
|
||||
|
||||
clean-rocksjava:
|
||||
clean-rocksjava: clean-rocks
|
||||
rm -rf jl jls
|
||||
cd java && $(MAKE) clean
|
||||
|
||||
@@ -1300,11 +1314,6 @@ trace_analyzer: $(OBJ_DIR)/tools/trace_analyzer.o $(ANALYZE_OBJECTS) $(TOOLS_LIB
|
||||
block_cache_trace_analyzer: $(OBJ_DIR)/tools/block_cache_analyzer/block_cache_trace_analyzer_tool.o $(ANALYZE_OBJECTS) $(TOOLS_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
folly_synchronization_distributed_mutex_test: $(OBJ_DIR)/third-party/folly/folly/synchronization/test/DistributedMutexTest.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
endif
|
||||
|
||||
cache_bench: $(OBJ_DIR)/cache/cache_bench.o $(CACHE_BENCH_OBJECTS) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1371,6 +1380,9 @@ ribbon_test: $(OBJ_DIR)/util/ribbon_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
option_change_migration_test: $(OBJ_DIR)/utilities/option_change_migration/option_change_migration_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
agg_merge_test: $(OBJ_DIR)/utilities/agg_merge/agg_merge_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
stringappend_test: $(OBJ_DIR)/utilities/merge_operators/string_append/stringappend_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1551,7 +1563,7 @@ perf_context_test: $(OBJ_DIR)/db/perf_context_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
prefix_test: $(OBJ_DIR)/db/prefix_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
backupable_db_test: $(OBJ_DIR)/utilities/backupable/backupable_db_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
backup_engine_test: $(OBJ_DIR)/utilities/backup/backup_engine_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
checkpoint_test: $(OBJ_DIR)/utilities/checkpoint/checkpoint_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
@@ -1842,7 +1854,7 @@ statistics_test: $(OBJ_DIR)/monitoring/statistics_test.o $(TEST_LIBRARY) $(LIBRA
|
||||
stats_history_test: $(OBJ_DIR)/monitoring/stats_history_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
lru_secondary_cache_test: $(OBJ_DIR)/cache/lru_secondary_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
compressed_secondary_cache_test: $(OBJ_DIR)/cache/compressed_secondary_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
lru_cache_test: $(OBJ_DIR)/cache/lru_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
@@ -2044,8 +2056,8 @@ ROCKSDB_JAVADOCS_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-javadoc.jar
|
||||
ROCKSDB_SOURCES_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-sources.jar
|
||||
SHA256_CMD = sha256sum
|
||||
|
||||
ZLIB_VER ?= 1.2.11
|
||||
ZLIB_SHA256 ?= c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
ZLIB_VER ?= 1.2.12
|
||||
ZLIB_SHA256 ?= 91844808532e5ce316b3c010929493c0244f3d37593afd6de04f71821d5136d9
|
||||
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.8
|
||||
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
|
||||
@@ -2372,10 +2384,26 @@ jtest: rocksdbjava
|
||||
jdb_bench:
|
||||
cd java;$(MAKE) db_bench;
|
||||
|
||||
commit_prereq: build_tools/rocksdb-lego-determinator \
|
||||
build_tools/precommit_checker.py
|
||||
J=$(J) build_tools/precommit_checker.py unit unit_481 clang_unit release release_481 clang_release tsan asan ubsan lite unit_non_shm
|
||||
$(MAKE) clean && $(MAKE) jclean && $(MAKE) rocksdbjava;
|
||||
commit_prereq:
|
||||
echo "TODO: bring this back using parts of old precommit_checker.py and rocksdb-lego-determinator"
|
||||
false # J=$(J) build_tools/precommit_checker.py unit clang_unit release clang_release tsan asan ubsan lite unit_non_shm
|
||||
# $(MAKE) clean && $(MAKE) jclean && $(MAKE) rocksdbjava;
|
||||
|
||||
# For public CI runs, checkout folly in a way that can build with RocksDB.
|
||||
# This is mostly intended as a test-only simulation of Meta-internal folly
|
||||
# integration.
|
||||
checkout_folly:
|
||||
if [ -e third-party/folly ]; then \
|
||||
cd third-party/folly && git fetch origin; \
|
||||
else \
|
||||
cd third-party && git clone https://github.com/facebook/folly.git; \
|
||||
fi
|
||||
@# Pin to a particular version for public CI, so that PR authors don't
|
||||
@# need to worry about folly breaking our integration. Update periodically
|
||||
cd third-party/folly && git reset --hard 98b9b2c1124e99f50f9085ddee74ce32afffc665
|
||||
@# A hack to remove boost dependency.
|
||||
@# NOTE: this hack is not needed if using FBCODE compiler config
|
||||
perl -pi -e 's/^(#include <boost)/\/\/$$1/' third-party/folly/folly/functional/Invoke.h
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Platform-specific compilation
|
||||
@@ -2429,7 +2457,7 @@ endif
|
||||
ifneq ($(SKIP_DEPENDS), 1)
|
||||
DEPFILES = $(patsubst %.cc, $(OBJ_DIR)/%.cc.d, $(ALL_SOURCES))
|
||||
DEPFILES+ = $(patsubst %.c, $(OBJ_DIR)/%.c.d, $(LIB_SOURCES_C) $(TEST_MAIN_SOURCES_C))
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
ifeq ($(USE_FOLLY),1)
|
||||
DEPFILES +=$(patsubst %.cpp, $(OBJ_DIR)/%.cpp.d, $(FOLLY_SOURCES))
|
||||
endif
|
||||
endif
|
||||
@@ -2477,7 +2505,7 @@ list_all_tests:
|
||||
|
||||
# Remove the rules for which dependencies should not be generated and see if any are left.
|
||||
#If so, include the dependencies; if not, do not include the dependency files
|
||||
ROCKS_DEP_RULES=$(filter-out clean format check-format check-buck-targets check-headers check-sources jclean jtest package analyze tags rocksdbjavastatic% unity.% unity_test, $(MAKECMDGOALS))
|
||||
ROCKS_DEP_RULES=$(filter-out clean format check-format check-buck-targets check-headers check-sources jclean jtest package analyze tags rocksdbjavastatic% unity.% unity_test checkout_folly, $(MAKECMDGOALS))
|
||||
ifneq ("$(ROCKS_DEP_RULES)", "")
|
||||
-include $(DEPFILES)
|
||||
endif
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ RocksDBLite is a project focused on mobile use cases, which don't need a lot of
|
||||
|
||||
Some examples of the features disabled by ROCKSDB_LITE:
|
||||
* compiled-in support for LDB tool
|
||||
* No backupable DB
|
||||
* No backup engine
|
||||
* No support for replication (which we provide in form of TransactionalIterator)
|
||||
* No advanced monitoring tools
|
||||
* No special-purpose memtables that are highly optimized for specific use cases
|
||||
|
||||
+6148
-6394
File diff suppressed because it is too large
Load Diff
+1579
-1651
File diff suppressed because it is too large
Load Diff
Regular → Executable
+8
-2
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
@@ -143,7 +144,8 @@ def generate_targets(repo_path, deps_map):
|
||||
src_mk["LIB_SOURCES"] +
|
||||
# always add range_tree, it's only excluded on ppc64, which we don't use internally
|
||||
src_mk["RANGE_TREE_SOURCES"] +
|
||||
src_mk["TOOL_LIB_SOURCES"])
|
||||
src_mk["TOOL_LIB_SOURCES"],
|
||||
deps=["//folly/container:f14_hash"])
|
||||
# rocksdb_whole_archive_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_whole_archive_lib",
|
||||
@@ -151,7 +153,7 @@ def generate_targets(repo_path, deps_map):
|
||||
# always add range_tree, it's only excluded on ppc64, which we don't use internally
|
||||
src_mk["RANGE_TREE_SOURCES"] +
|
||||
src_mk["TOOL_LIB_SOURCES"],
|
||||
deps=None,
|
||||
deps=["//folly/container:f14_hash"],
|
||||
headers=None,
|
||||
extra_external_deps="",
|
||||
link_whole=True)
|
||||
@@ -183,6 +185,10 @@ def generate_targets(repo_path, deps_map):
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
+ src_mk.get('STRESS_LIB_SOURCES', [])
|
||||
+ ["test_util/testutil.cc"])
|
||||
# db_stress binary
|
||||
TARGETS.add_binary("db_stress",
|
||||
["db_stress_tool/db_stress.cc"],
|
||||
[":rocksdb_stress_lib"])
|
||||
# bench binaries
|
||||
for src in src_mk.get("MICROBENCH_SOURCES", []):
|
||||
name = src.rsplit('/',1)[1].split('.')[0] if '/' in src else src.split('.')[0]
|
||||
|
||||
@@ -63,13 +63,8 @@ if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
|
||||
if [ "$LIB_MODE" == "shared" ]; then
|
||||
PIC_BUILD=1
|
||||
fi
|
||||
if [ -n "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_5xx" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_PLATFORM007" ]; then
|
||||
source "$PWD/build_tools/fbcode_config_platform007.sh"
|
||||
if [ -n "$ROCKSDB_FBCODE_BUILD_WITH_PLATFORM010" ]; then
|
||||
source "$PWD/build_tools/fbcode_config_platform010.sh"
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_PLATFORM009" ]; then
|
||||
source "$PWD/build_tools/fbcode_config_platform009.sh"
|
||||
else
|
||||
@@ -598,7 +593,7 @@ EOF
|
||||
fi
|
||||
if ! test $ROCKSDB_DISABLE_BENCHMARK; then
|
||||
# Test whether google benchmark is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null -lbenchmark 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null -lbenchmark -lpthread 2>/dev/null <<EOF
|
||||
#include <benchmark/benchmark.h>
|
||||
int main() {}
|
||||
EOF
|
||||
@@ -667,13 +662,13 @@ else
|
||||
fi
|
||||
|
||||
if [[ "${PLATFORM}" == "OS_MACOSX" ]]; then
|
||||
# For portability compile for macOS 10.12 (2016) or newer
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mmacosx-version-min=10.12"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -mmacosx-version-min=10.12"
|
||||
# For portability compile for macOS 10.13 (2017) or newer
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mmacosx-version-min=10.13"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -mmacosx-version-min=10.13"
|
||||
# -mmacosx-version-min must come first here.
|
||||
PLATFORM_SHARED_LDFLAGS="-mmacosx-version-min=10.12 $PLATFORM_SHARED_LDFLAGS"
|
||||
PLATFORM_CMAKE_FLAGS="-DCMAKE_OSX_DEPLOYMENT_TARGET=10.12"
|
||||
JAVA_STATIC_DEPS_COMMON_FLAGS="-mmacosx-version-min=10.12"
|
||||
PLATFORM_SHARED_LDFLAGS="-mmacosx-version-min=10.13 $PLATFORM_SHARED_LDFLAGS"
|
||||
PLATFORM_CMAKE_FLAGS="-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13"
|
||||
JAVA_STATIC_DEPS_COMMON_FLAGS="-mmacosx-version-min=10.13"
|
||||
JAVA_STATIC_DEPS_LDFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
JAVA_STATIC_DEPS_CCFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
JAVA_STATIC_DEPS_CXXFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
@@ -885,8 +880,8 @@ if test -n "$WITH_JEMALLOC_FLAG"; then
|
||||
echo "WITH_JEMALLOC_FLAG=$WITH_JEMALLOC_FLAG" >> "$OUTPUT"
|
||||
fi
|
||||
echo "LUA_PATH=$LUA_PATH" >> "$OUTPUT"
|
||||
if test -n "$USE_FOLLY_DISTRIBUTED_MUTEX"; then
|
||||
echo "USE_FOLLY_DISTRIBUTED_MUTEX=$USE_FOLLY_DISTRIBUTED_MUTEX" >> "$OUTPUT"
|
||||
if test -n "$USE_FOLLY"; then
|
||||
echo "USE_FOLLY=$USE_FOLLY" >> "$OUTPUT"
|
||||
fi
|
||||
if test -n "$PPC_LIBC_IS_GNU"; then
|
||||
echo "PPC_LIBC_IS_GNU=$PPC_LIBC_IS_GNU" >> "$OUTPUT"
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/7331085db891a2ef4a88a48a751d834e8d68f4cb/5.x/centos7-native/c447969
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/1bd23f9917738974ad0ff305aa23eb5f93f18305/9.0.0/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/6ace84e956873d53638c738b6f65f3f469cca74c/5.x/gcc-5-glibc-2.23/339d858
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/192b0f42d63dcf6210d6ceae387b49af049e6e0c/2.23/gcc-5-glibc-2.23/ca1d1c0
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/gcc-5-glibc-2.23/9bc6787
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2d9f0b9a4274cc21f61272a9e89bdb859bce8f1f/1.2.8/gcc-5-glibc-2.23/9bc6787
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/gcc-5-glibc-2.23/9bc6787
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0f607f8fc442ea7d6b876931b1898bb573d5e5da/1.9.1/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/ca22bc441a4eb709e9e0b1f9fec9750fed7b31c5/1.4.x/gcc-5-glibc-2.23/03859b5
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/gcc-5-glibc-2.23/9bc6787
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/c26f08f47ac35fc31da2633b7da92d6b863246eb/master/gcc-5-glibc-2.23/0c8f76d
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/3f3fb57a5ccc5fd21c66416c0b83e0aa76a05376/2.0.11/gcc-5-glibc-2.23/9bc6787
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/40c73d874898b386a71847f1b99115d93822d11f/1.4/gcc-5-glibc-2.23/b443de1
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/4ce8e8dba77cdbd81b75d6f0c32fd7a1b76a11ec/2018_U5/gcc-5-glibc-2.23/9bc6787
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/fb251ecd2f5ae16f8671f7014c246e52a748fe0b/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/2e3cb7d119b3cea5f1e738cc13a1ac69f49eb875/2.29.1/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d42d152a15636529b0861ec493927200ebebca8e/3.15.0/gcc-5-glibc-2.23/9bc6787
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.2.3/gcc-5-glibc-2.23/65372bd
|
||||
@@ -1,20 +0,0 @@
|
||||
# shellcheck disable=SC2148
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/cf7d14c625ce30bae1a4661c2319c5a283e4dd22/4.8.1/centos6-native/cc6c9dc
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/8598c375b0e94e1448182eb3df034704144a838d/stable/centos6-native/3f16ddd
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d6e0a7da6faba45f5e5b1638f9edd7afc2f34e7d/4.8.1/gcc-4.8.1-glibc-2.17/8aac7fc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/d282e6e8f3d20f4e40a516834847bdc038e07973/2.17/gcc-4.8.1-glibc-2.17/99df8fc
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/8c38a4c1e52b4c2cc8a9cdc31b9c947ed7dbfcb4/1.1.3/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/0882df3713c7a84f15abe368dc004581f20b39d7/1.2.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/740325875f6729f42d28deaa2147b0854f3a347e/1.0.6/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0e790b441e2d9acd68d51e1d2e028f88c6a79ddf/r131/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/9455f75ff7f4831dc9fda02a6a0f8c68922fad8f/1.0.0/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/f001a51b2854957676d07306ef3abf67186b5c8b/2.1.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/fc8a13ca1fffa4d0765c716c5a0b49f0c107518f/master/gcc-4.8.1-glibc-2.17/8d31e51
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/17c514c4d102a25ca15f4558be564eeed76f4b6a/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/ad576de2a1ea560c4d3434304f0fc4e079bede42/trunk/gcc-4.8.1-glibc-2.17/675d945
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/9d9a554877d0c5bef330fe818ab7178806dd316a/4.0_update2/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/7c111ff27e0c466235163f00f280a9d617c3d2ec/4.0.9-36_fbk5_2933_gd092e3f/gcc-4.8.1-glibc-2.17/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/b7fd454c4b10c6a81015d4524ed06cdeab558490/2.26/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d7f4d4d86674a57668e3a96f76f0e17dd0eb8765/3.8.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/61e4abf5813bbc39bc4f548757ccfcadde175a48/5.2.3/centos6-native/730f94e
|
||||
@@ -1,20 +0,0 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/7331085db891a2ef4a88a48a751d834e8d68f4cb/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/963d9aeda70cc4779885b1277484fe7544a04e3e/9.0.0/platform007/9e92d53/
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/6ace84e956873d53638c738b6f65f3f469cca74c/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/192b0f42d63dcf6210d6ceae387b49af049e6e0c/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2d9f0b9a4274cc21f61272a9e89bdb859bce8f1f/1.2.8/platform007/ca4da3d
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/platform007/ca4da3d
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0f607f8fc442ea7d6b876931b1898bb573d5e5da/1.9.1/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/ca22bc441a4eb709e9e0b1f9fec9750fed7b31c5/1.4.x/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/c26f08f47ac35fc31da2633b7da92d6b863246eb/master/platform007/c26c002
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/3f3fb57a5ccc5fd21c66416c0b83e0aa76a05376/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/40c73d874898b386a71847f1b99115d93822d11f/1.4/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/4ce8e8dba77cdbd81b75d6f0c32fd7a1b76a11ec/2018_U5/platform007/ca4da3d
|
||||
LIBURING_BASE=/mnt/gvfs/third-party2/liburing/79427253fd0d42677255aacfe6d13bfe63f752eb/20190828/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/fb251ecd2f5ae16f8671f7014c246e52a748fe0b/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/ab9f09bba370e7066cafd4eb59752db93f2e8312/2.29.1/platform007/15a3614
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d42d152a15636529b0861ec493927200ebebca8e/3.15.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
@@ -18,4 +18,5 @@ KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/32b8a2407b634df3f8f948
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/08634589372fa5f237bfd374e8c644a8364e78c1/2.32/platform009/ba86d1f/
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/6ae525939ad02e5e676855082fbbc7828dbafeac/3.15.0/platform009/7f3b187
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/162efd9561a3d21f6869f4814011e9cf1b3ff4dc/5.3.4/platform009/a6271c4
|
||||
BENCHMARK_BASE=/mnt/gvfs/third-party2/benchmark/bce8d9564eaf161700aa3a20b1051564acf555fb/1.5.5/platform009/7f3b187
|
||||
BENCHMARK_BASE=/mnt/gvfs/third-party2/benchmark/30bf49ad6414325e17f3425b0edcb64239427ae3/1.6.1/platform009/7f3b187
|
||||
BOOST_BASE=/mnt/gvfs/third-party2/boost/201b7d74941e54b436dfa364a063aa6d2cd7de4c/1.69.0/platform009/8a7ffdf
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# The file is generated using update_dependencies.sh.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/e40bde78650fa91b8405a857e3f10bf336633fb0/11.x/centos7-native/886b5eb
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/2043340983c032915adbb6f78903dc855b65aee8/12/platform010/9520e0f
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/c00dcc6a3e4125c7e8b248e9a79c14b78ac9e0ca/11.x/platform010/5684a5a
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/0b9c8e4b060eda62f3bc1c6127bbe1256697569b/2.34/platform010/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/bc9647f7912b131315827d65cb6189c21f381d05/1.1.3/platform010/76ebdda
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/a6f5f3f1d063d2d00cd02fc12f0f05fc3ab3a994/1.2.11/platform010/76ebdda
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/09703139cfc376bd8a82642385a0e97726b28287/1.0.6/platform010/76ebdda
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/60220d6a5bf7722b9cc239a1368c596619b12060/1.9.1/platform010/76ebdda
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/50eace8143eaaea9473deae1f3283e0049e05633/1.4.x/platform010/64091f4
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/5d27e5919771603da06000a027b12f799e58a4f7/2.2.0/platform010/76ebdda
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/b62912d333ef33f9760efa6219dbe3fe6abb3b0e/master/platform010/f57cc4a
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/6b412770957aa3c8a87e5e0dcd8cc2f45f393bc0/2.0.11/platform010/76ebdda
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/52f69816e936e147664ad717eb71a1a0e9dc973a/1.4/platform010/5074a48
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/c9cc192099fa84c0dcd0ffeedd44a373ad6e4925/2018_U5/platform010/76ebdda
|
||||
LIBURING_BASE=/mnt/gvfs/third-party2/liburing/a98e2d137007e3ebf7f33bd6f99c2c56bdaf8488/20210212/platform010/76ebdda
|
||||
BENCHMARK_BASE=/mnt/gvfs/third-party2/benchmark/780c7a0f9cf0967961e69ad08e61cddd85d61821/trunk/platform010/76ebdda
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/02d9f76aaaba580611cf75e741753c800c7fdc12/fb/platform010/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/938dc3f064ef3a48c0446f5b11d788d50b3eb5ee/2.37/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/429a6b3203eb415f1599bd15183659153129188e/3.15.0/platform010/76ebdda
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/363787fa5cac2a8aa20638909210443278fa138e/5.3.4/platform010/9079c97
|
||||
@@ -162,6 +162,4 @@ else
|
||||
LUA_LIB=" $LUA_PATH/lib/liblua_pic.a"
|
||||
fi
|
||||
|
||||
USE_FOLLY_DISTRIBUTED_MUTEX=1
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ compiler and also
|
||||
# uses jemalloc
|
||||
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies_4.8.1.sh"
|
||||
|
||||
# location of libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# location of glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
# location of snappy headers and libraries
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include"
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP2_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP2_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags.a"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include"
|
||||
JEMALLOC_LIB="$JEMALLOC_BASE/lib/libjemalloc.a"
|
||||
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
|
||||
# location of tbb
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
|
||||
test "$USE_SSE" || USE_SSE=1
|
||||
export USE_SSE
|
||||
test "$PORTABLE" || PORTABLE=1
|
||||
export PORTABLE
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP2_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
CXX="$GCC_BASE/bin/gcc-ar"
|
||||
|
||||
CFLAGS="-B$BINUTILS/gold -m64 -mtune=generic"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
CLANG_BIN="$CLANG_BASE/bin"
|
||||
CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_INCLUDE="$CLANG_LIB/clang/*/include"
|
||||
CC="$CLANG_BIN/clang"
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
AR="$CLANG_BIN/llvm-ar"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include/"
|
||||
|
||||
CFLAGS="-B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.8.1 "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.8.1/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2 -DLZ4 -DZSTD -DNUMA -DTBB"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP2_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP2_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
LUA_PATH="$LUA_BASE"
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE LUA_PATH
|
||||
@@ -58,6 +58,8 @@ CFLAGS+=" -DGFLAGS=gflags"
|
||||
BENCHMARK_INCLUDE=" -I $BENCHMARK_BASE/include/"
|
||||
BENCHMARK_LIBS=" $BENCHMARK_BASE/lib/libbenchmark${MAYBE_PIC}.a"
|
||||
|
||||
BOOST_INCLUDE=" -I $BOOST_BASE/include/"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc${MAYBE_PIC}.a"
|
||||
@@ -89,7 +91,7 @@ BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
AS="$BINUTILS/as"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE $LIBURING_INCLUDE $BENCHMARK_INCLUDE"
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE $LIBURING_INCLUDE $BENCHMARK_INCLUDE $BOOST_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
|
||||
+57
-71
@@ -9,90 +9,77 @@
|
||||
|
||||
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies_platform007.sh"
|
||||
source "$BASEDIR/dependencies_platform010.sh"
|
||||
|
||||
CFLAGS=""
|
||||
# Disallow using libraries from default locations as they might not be compatible with platform010 libraries.
|
||||
CFLAGS=" --sysroot=/DOES/NOT/EXIST"
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/7.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/trunk"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib -B$LIBGCC_BASE/lib/gcc/x86_64-facebook-linux/trunk/"
|
||||
|
||||
# glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
GLIBC_LIBS+=" -B$GLIBC_BASE/lib"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
MAYBE_PIC=
|
||||
else
|
||||
MAYBE_PIC=_pic
|
||||
fi
|
||||
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
else
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy_pic.a"
|
||||
fi
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags.a"
|
||||
else
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags_pic.a"
|
||||
fi
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
|
||||
BENCHMARK_INCLUDE=" -I $BENCHMARK_BASE/include/"
|
||||
BENCHMARK_LIBS=" $BENCHMARK_BASE/lib/libbenchmark${MAYBE_PIC}.a"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc.a"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc${MAYBE_PIC}.a"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
CFLAGS+=" -DNUMA"
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DNUMA"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
fi
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind${MAYBE_PIC}.a"
|
||||
|
||||
# location of TBB
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
else
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb_pic.a"
|
||||
fi
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# location of LIBURING
|
||||
LIBURING_INCLUDE=" -isystem $LIBURING_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
LIBURING_LIBS="$LIBURING_BASE/lib/liburing.a"
|
||||
else
|
||||
LIBURING_LIBS="$LIBURING_BASE/lib/liburing_pic.a"
|
||||
fi
|
||||
LIBURING_LIBS="$LIBURING_BASE/lib/liburing${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DLIBURING"
|
||||
|
||||
test "$USE_SSE" || USE_SSE=1
|
||||
@@ -102,8 +89,9 @@ export PORTABLE
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
AS="$BINUTILS/as"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE $LIBURING_INCLUDE"
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE $LIBURING_INCLUDE $BENCHMARK_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
@@ -112,7 +100,7 @@ CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/tools/clang/tools/scan-build/bin/scan-build"
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/clang/tools/scan-build/bin/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
@@ -120,7 +108,8 @@ if [ -z "$USE_CLANG" ]; then
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
AR="$GCC_BASE/bin/gcc-ar"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
@@ -131,40 +120,37 @@ else
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
AR="$CLANG_BIN/llvm-ar"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -B$BINUTILS -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/trunk "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/trunk/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CFLAGS+=" -Wno-expansion-to-defined "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42 -DROCKSDB_IOURING_PRESENT"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS $LIBURING_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS $LIBURING_LIBS $BENCHMARK_LIBS"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform010/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform007/lib"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform010/lib"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=$GCC_BASE/lib64"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
PLATFORM_LDFLAGS+=" -B$BINUTILS"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS $LIBURING_LIBS"
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS $LIBURING_LIBS $BENCHMARK_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
# lua not supported because it's on track for deprecation, I think
|
||||
LUA_PATH=
|
||||
LUA_LIB=
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
export CC CXX AR AS CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
@@ -1,209 +0,0 @@
|
||||
#!/usr/bin/env python2.7
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
import argparse
|
||||
import commands
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
import time
|
||||
|
||||
|
||||
#
|
||||
# Simple logger
|
||||
#
|
||||
|
||||
class Log:
|
||||
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.f = open(self.filename, 'w+', 0)
|
||||
|
||||
def caption(self, str):
|
||||
line = "\n##### %s #####\n" % str
|
||||
if self.f:
|
||||
self.f.write("%s \n" % line)
|
||||
else:
|
||||
print(line)
|
||||
|
||||
def error(self, str):
|
||||
data = "\n\n##### ERROR ##### %s" % str
|
||||
if self.f:
|
||||
self.f.write("%s \n" % data)
|
||||
else:
|
||||
print(data)
|
||||
|
||||
def log(self, str):
|
||||
if self.f:
|
||||
self.f.write("%s \n" % str)
|
||||
else:
|
||||
print(str)
|
||||
|
||||
#
|
||||
# Shell Environment
|
||||
#
|
||||
|
||||
|
||||
class Env(object):
|
||||
|
||||
def __init__(self, logfile, tests):
|
||||
self.tests = tests
|
||||
self.log = Log(logfile)
|
||||
|
||||
def shell(self, cmd, path=os.getcwd()):
|
||||
if path:
|
||||
os.chdir(path)
|
||||
|
||||
self.log.log("==== shell session ===========================")
|
||||
self.log.log("%s> %s" % (path, cmd))
|
||||
status = subprocess.call("cd %s; %s" % (path, cmd), shell=True,
|
||||
stdout=self.log.f, stderr=self.log.f)
|
||||
self.log.log("status = %s" % status)
|
||||
self.log.log("============================================== \n\n")
|
||||
return status
|
||||
|
||||
def GetOutput(self, cmd, path=os.getcwd()):
|
||||
if path:
|
||||
os.chdir(path)
|
||||
|
||||
self.log.log("==== shell session ===========================")
|
||||
self.log.log("%s> %s" % (path, cmd))
|
||||
status, out = commands.getstatusoutput(cmd)
|
||||
self.log.log("status = %s" % status)
|
||||
self.log.log("out = %s" % out)
|
||||
self.log.log("============================================== \n\n")
|
||||
return status, out
|
||||
|
||||
#
|
||||
# Pre-commit checker
|
||||
#
|
||||
|
||||
|
||||
class PreCommitChecker(Env):
|
||||
|
||||
def __init__(self, args):
|
||||
Env.__init__(self, args.logfile, args.tests)
|
||||
self.ignore_failure = args.ignore_failure
|
||||
|
||||
#
|
||||
# Get commands for a given job from the determinator file
|
||||
#
|
||||
def get_commands(self, test):
|
||||
status, out = self.GetOutput(
|
||||
"RATIO=1 build_tools/rocksdb-lego-determinator %s" % test, ".")
|
||||
return status, out
|
||||
|
||||
#
|
||||
# Run a specific CI job
|
||||
#
|
||||
def run_test(self, test):
|
||||
self.log.caption("Running test %s locally" % test)
|
||||
|
||||
# get commands for the CI job determinator
|
||||
status, cmds = self.get_commands(test)
|
||||
if status != 0:
|
||||
self.log.error("Error getting commands for test %s" % test)
|
||||
return False
|
||||
|
||||
# Parse the JSON to extract the commands to run
|
||||
cmds = re.findall("'shell':'([^\']*)'", cmds)
|
||||
|
||||
if len(cmds) == 0:
|
||||
self.log.log("No commands found")
|
||||
return False
|
||||
|
||||
# Run commands
|
||||
for cmd in cmds:
|
||||
# Replace J=<..> with the local environment variable
|
||||
if "J" in os.environ:
|
||||
cmd = cmd.replace("J=1", "J=%s" % os.environ["J"])
|
||||
cmd = cmd.replace("make ", "make -j%s " % os.environ["J"])
|
||||
# Run the command
|
||||
status = self.shell(cmd, ".")
|
||||
if status != 0:
|
||||
self.log.error("Error running command %s for test %s"
|
||||
% (cmd, test))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
#
|
||||
# Run specified CI jobs
|
||||
#
|
||||
def run_tests(self):
|
||||
if not self.tests:
|
||||
self.log.error("Invalid args. Please provide tests")
|
||||
return False
|
||||
|
||||
self.print_separator()
|
||||
self.print_row("TEST", "RESULT")
|
||||
self.print_separator()
|
||||
|
||||
result = True
|
||||
for test in self.tests:
|
||||
start_time = time.time()
|
||||
self.print_test(test)
|
||||
result = self.run_test(test)
|
||||
elapsed_min = (time.time() - start_time) / 60
|
||||
if not result:
|
||||
self.log.error("Error running test %s" % test)
|
||||
self.print_result("FAIL (%dm)" % elapsed_min)
|
||||
if not self.ignore_failure:
|
||||
return False
|
||||
result = False
|
||||
else:
|
||||
self.print_result("PASS (%dm)" % elapsed_min)
|
||||
|
||||
self.print_separator()
|
||||
return result
|
||||
|
||||
#
|
||||
# Print a line
|
||||
#
|
||||
def print_separator(self):
|
||||
print("".ljust(60, "-"))
|
||||
|
||||
#
|
||||
# Print two colums
|
||||
#
|
||||
def print_row(self, c0, c1):
|
||||
print("%s%s" % (c0.ljust(40), c1.ljust(20)))
|
||||
|
||||
def print_test(self, test):
|
||||
print(test.ljust(40), end="")
|
||||
sys.stdout.flush()
|
||||
|
||||
def print_result(self, result):
|
||||
print(result.ljust(20))
|
||||
|
||||
#
|
||||
# Main
|
||||
#
|
||||
parser = argparse.ArgumentParser(description='RocksDB pre-commit checker.')
|
||||
|
||||
# --log <logfile>
|
||||
parser.add_argument('--logfile', default='/tmp/precommit-check.log',
|
||||
help='Log file. Default is /tmp/precommit-check.log')
|
||||
# --ignore_failure
|
||||
parser.add_argument('--ignore_failure', action='store_true', default=False,
|
||||
help='Stop when an error occurs')
|
||||
# <test ....>
|
||||
parser.add_argument('tests', nargs='+',
|
||||
help='CI test(s) to run. e.g: unit punit asan tsan ubsan')
|
||||
|
||||
args = parser.parse_args()
|
||||
checker = PreCommitChecker(args)
|
||||
|
||||
print("Please follow log %s" % checker.log.filename)
|
||||
|
||||
if not checker.run_tests():
|
||||
print("Error running tests. Please check log file %s"
|
||||
% checker.log.filename)
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -42,7 +42,7 @@ $RunOnly.Add("c_test") | Out-Null
|
||||
$RunOnly.Add("compact_on_deletion_collector_test") | Out-Null
|
||||
$RunOnly.Add("merge_test") | Out-Null
|
||||
$RunOnly.Add("stringappend_test") | Out-Null # Apparently incorrectly written
|
||||
$RunOnly.Add("backupable_db_test") | Out-Null # Disabled
|
||||
$RunOnly.Add("backup_engine_test") | Out-Null # Disabled
|
||||
$RunOnly.Add("timer_queue_test") | Out-Null # Not a gtest
|
||||
|
||||
if($RunAll -and $SuiteRun -ne "") {
|
||||
@@ -491,5 +491,3 @@ if(!$script:success) {
|
||||
}
|
||||
|
||||
exit 0
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ OUTPUT=""
|
||||
function log_header()
|
||||
{
|
||||
echo "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved." >> "$OUTPUT"
|
||||
echo "# The file is generated using update_dependencies.sh." >> "$OUTPUT"
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +19,7 @@ function log_variable()
|
||||
}
|
||||
|
||||
|
||||
TP2_LATEST="/mnt/vol/engshare/fbcode/third-party2"
|
||||
TP2_LATEST="/data/users/$USER/fbsource/fbcode/third-party2/"
|
||||
## $1 => lib name
|
||||
## $2 => lib version (if not provided, will try to pick latest)
|
||||
## $3 => platform (if not provided, will try to pick latest gcc)
|
||||
@@ -50,6 +51,8 @@ function get_lib_base()
|
||||
fi
|
||||
|
||||
result=`ls -1d $result/*/ | head -n1`
|
||||
|
||||
echo Finding link $result
|
||||
|
||||
# lib_name => LIB_NAME_BASE
|
||||
local __res_var=${lib_name^^}"_BASE"
|
||||
@@ -61,10 +64,10 @@ function get_lib_base()
|
||||
}
|
||||
|
||||
###########################################################
|
||||
# platform007 dependencies #
|
||||
# platform010 dependencies #
|
||||
###########################################################
|
||||
|
||||
OUTPUT="$BASEDIR/dependencies_platform007.sh"
|
||||
OUTPUT="$BASEDIR/dependencies_platform010.sh"
|
||||
|
||||
rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
@@ -72,40 +75,42 @@ touch "$OUTPUT"
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/7.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos7-native/*/`
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/12/platform010/*/`
|
||||
|
||||
log_header
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
# Libraries locations
|
||||
get_lib_base libgcc 7.x platform007
|
||||
get_lib_base glibc 2.26 platform007
|
||||
get_lib_base snappy LATEST platform007
|
||||
get_lib_base zlib LATEST platform007
|
||||
get_lib_base bzip2 LATEST platform007
|
||||
get_lib_base lz4 LATEST platform007
|
||||
get_lib_base zstd LATEST platform007
|
||||
get_lib_base gflags LATEST platform007
|
||||
get_lib_base jemalloc LATEST platform007
|
||||
get_lib_base numa LATEST platform007
|
||||
get_lib_base libunwind LATEST platform007
|
||||
get_lib_base tbb LATEST platform007
|
||||
get_lib_base liburing LATEST platform007
|
||||
get_lib_base libgcc 11.x platform010
|
||||
get_lib_base glibc 2.34 platform010
|
||||
get_lib_base snappy LATEST platform010
|
||||
get_lib_base zlib LATEST platform010
|
||||
get_lib_base bzip2 LATEST platform010
|
||||
get_lib_base lz4 LATEST platform010
|
||||
get_lib_base zstd LATEST platform010
|
||||
get_lib_base gflags LATEST platform010
|
||||
get_lib_base jemalloc LATEST platform010
|
||||
get_lib_base numa LATEST platform010
|
||||
get_lib_base libunwind LATEST platform010
|
||||
get_lib_base tbb 2018_U5 platform010
|
||||
get_lib_base liburing LATEST platform010
|
||||
get_lib_base benchmark LATEST platform010
|
||||
|
||||
get_lib_base kernel-headers fb platform007
|
||||
get_lib_base kernel-headers fb platform010
|
||||
get_lib_base binutils LATEST centos7-native
|
||||
get_lib_base valgrind LATEST platform007
|
||||
get_lib_base lua 5.3.4 platform007
|
||||
get_lib_base valgrind LATEST platform010
|
||||
get_lib_base lua 5.3.4 platform010
|
||||
|
||||
git diff $OUTPUT
|
||||
|
||||
|
||||
###########################################################
|
||||
# 5.x dependencies #
|
||||
# platform009 dependencies #
|
||||
###########################################################
|
||||
|
||||
OUTPUT="$BASEDIR/dependencies.sh"
|
||||
OUTPUT="$BASEDIR/dependencies_platform009.sh"
|
||||
|
||||
rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
@@ -113,70 +118,32 @@ touch "$OUTPUT"
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/5.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos7-native/*/`
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/9.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/9.0.0/platform009/*/`
|
||||
|
||||
log_header
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
# Libraries locations
|
||||
get_lib_base libgcc 5.x gcc-5-glibc-2.23
|
||||
get_lib_base glibc 2.23 gcc-5-glibc-2.23
|
||||
get_lib_base snappy LATEST gcc-5-glibc-2.23
|
||||
get_lib_base zlib LATEST gcc-5-glibc-2.23
|
||||
get_lib_base bzip2 LATEST gcc-5-glibc-2.23
|
||||
get_lib_base lz4 LATEST gcc-5-glibc-2.23
|
||||
get_lib_base zstd LATEST gcc-5-glibc-2.23
|
||||
get_lib_base gflags LATEST gcc-5-glibc-2.23
|
||||
get_lib_base jemalloc LATEST gcc-5-glibc-2.23
|
||||
get_lib_base numa LATEST gcc-5-glibc-2.23
|
||||
get_lib_base libunwind LATEST gcc-5-glibc-2.23
|
||||
get_lib_base tbb LATEST gcc-5-glibc-2.23
|
||||
get_lib_base libgcc 9.x platform009
|
||||
get_lib_base glibc 2.30 platform009
|
||||
get_lib_base snappy LATEST platform009
|
||||
get_lib_base zlib LATEST platform009
|
||||
get_lib_base bzip2 LATEST platform009
|
||||
get_lib_base lz4 LATEST platform009
|
||||
get_lib_base zstd LATEST platform009
|
||||
get_lib_base gflags LATEST platform009
|
||||
get_lib_base jemalloc LATEST platform009
|
||||
get_lib_base numa LATEST platform009
|
||||
get_lib_base libunwind LATEST platform009
|
||||
get_lib_base tbb 2018_U5 platform009
|
||||
get_lib_base liburing LATEST platform009
|
||||
get_lib_base benchmark LATEST platform009
|
||||
|
||||
get_lib_base kernel-headers 4.0.9-36_fbk5_2933_gd092e3f gcc-5-glibc-2.23
|
||||
get_lib_base kernel-headers fb platform009
|
||||
get_lib_base binutils LATEST centos7-native
|
||||
get_lib_base valgrind LATEST gcc-5-glibc-2.23
|
||||
get_lib_base lua 5.2.3 gcc-5-glibc-2.23
|
||||
|
||||
git diff $OUTPUT
|
||||
|
||||
###########################################################
|
||||
# 4.8.1 dependencies #
|
||||
###########################################################
|
||||
|
||||
OUTPUT="$BASEDIR/dependencies_4.8.1.sh"
|
||||
|
||||
rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
|
||||
echo "Writing 4.8.1 dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/4.8.1/centos6-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos6-native/*/`
|
||||
|
||||
log_header
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
# Libraries locations
|
||||
get_lib_base libgcc 4.8.1 gcc-4.8.1-glibc-2.17
|
||||
get_lib_base glibc 2.17 gcc-4.8.1-glibc-2.17
|
||||
get_lib_base snappy LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base zlib LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base bzip2 LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base lz4 LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base zstd LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base gflags LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base jemalloc LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base numa LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base libunwind LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base tbb 4.0_update2 gcc-4.8.1-glibc-2.17
|
||||
|
||||
get_lib_base kernel-headers LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base binutils LATEST centos6-native
|
||||
get_lib_base valgrind 3.8.1 gcc-4.8.1-glibc-2.17
|
||||
get_lib_base lua 5.2.3 centos6-native
|
||||
get_lib_base valgrind LATEST platform009
|
||||
get_lib_base lua 5.3.4 platform009
|
||||
|
||||
git diff $OUTPUT
|
||||
|
||||
Vendored
+63
-5
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
std::array<const char*, kNumCacheEntryRoles> kCacheEntryRoleToCamelString{{
|
||||
std::array<std::string, kNumCacheEntryRoles> kCacheEntryRoleToCamelString{{
|
||||
"DataBlock",
|
||||
"FilterBlock",
|
||||
"FilterMetaBlock",
|
||||
@@ -21,10 +21,11 @@ std::array<const char*, kNumCacheEntryRoles> kCacheEntryRoleToCamelString{{
|
||||
"WriteBuffer",
|
||||
"CompressionDictionaryBuildingBuffer",
|
||||
"FilterConstruction",
|
||||
"BlockBasedTableReader",
|
||||
"Misc",
|
||||
}};
|
||||
|
||||
std::array<const char*, kNumCacheEntryRoles> kCacheEntryRoleToHyphenString{{
|
||||
std::array<std::string, kNumCacheEntryRoles> kCacheEntryRoleToHyphenString{{
|
||||
"data-block",
|
||||
"filter-block",
|
||||
"filter-meta-block",
|
||||
@@ -34,19 +35,76 @@ std::array<const char*, kNumCacheEntryRoles> kCacheEntryRoleToHyphenString{{
|
||||
"write-buffer",
|
||||
"compression-dictionary-building-buffer",
|
||||
"filter-construction",
|
||||
"block-based-table-reader",
|
||||
"misc",
|
||||
}};
|
||||
|
||||
const std::string& GetCacheEntryRoleName(CacheEntryRole role) {
|
||||
return kCacheEntryRoleToHyphenString[static_cast<size_t>(role)];
|
||||
}
|
||||
|
||||
const std::string& BlockCacheEntryStatsMapKeys::CacheId() {
|
||||
static const std::string kCacheId = "id";
|
||||
return kCacheId;
|
||||
}
|
||||
|
||||
const std::string& BlockCacheEntryStatsMapKeys::CacheCapacityBytes() {
|
||||
static const std::string kCacheCapacityBytes = "capacity";
|
||||
return kCacheCapacityBytes;
|
||||
}
|
||||
|
||||
const std::string&
|
||||
BlockCacheEntryStatsMapKeys::LastCollectionDurationSeconds() {
|
||||
static const std::string kLastCollectionDurationSeconds =
|
||||
"secs_for_last_collection";
|
||||
return kLastCollectionDurationSeconds;
|
||||
}
|
||||
|
||||
const std::string& BlockCacheEntryStatsMapKeys::LastCollectionAgeSeconds() {
|
||||
static const std::string kLastCollectionAgeSeconds =
|
||||
"secs_since_last_collection";
|
||||
return kLastCollectionAgeSeconds;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::string GetPrefixedCacheEntryRoleName(const std::string& prefix,
|
||||
CacheEntryRole role) {
|
||||
const std::string& role_name = GetCacheEntryRoleName(role);
|
||||
std::string prefixed_role_name;
|
||||
prefixed_role_name.reserve(prefix.size() + role_name.size());
|
||||
prefixed_role_name.append(prefix);
|
||||
prefixed_role_name.append(role_name);
|
||||
return prefixed_role_name;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string BlockCacheEntryStatsMapKeys::EntryCount(CacheEntryRole role) {
|
||||
const static std::string kPrefix = "count.";
|
||||
return GetPrefixedCacheEntryRoleName(kPrefix, role);
|
||||
}
|
||||
|
||||
std::string BlockCacheEntryStatsMapKeys::UsedBytes(CacheEntryRole role) {
|
||||
const static std::string kPrefix = "bytes.";
|
||||
return GetPrefixedCacheEntryRoleName(kPrefix, role);
|
||||
}
|
||||
|
||||
std::string BlockCacheEntryStatsMapKeys::UsedPercent(CacheEntryRole role) {
|
||||
const static std::string kPrefix = "percent.";
|
||||
return GetPrefixedCacheEntryRoleName(kPrefix, role);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct Registry {
|
||||
std::mutex mutex;
|
||||
std::unordered_map<Cache::DeleterFn, CacheEntryRole> role_map;
|
||||
UnorderedMap<Cache::DeleterFn, CacheEntryRole> role_map;
|
||||
void Register(Cache::DeleterFn fn, CacheEntryRole role) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
role_map[fn] = role;
|
||||
}
|
||||
std::unordered_map<Cache::DeleterFn, CacheEntryRole> Copy() {
|
||||
UnorderedMap<Cache::DeleterFn, CacheEntryRole> Copy() {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
return role_map;
|
||||
}
|
||||
@@ -63,7 +121,7 @@ void RegisterCacheDeleterRole(Cache::DeleterFn fn, CacheEntryRole role) {
|
||||
GetRegistry().Register(fn, role);
|
||||
}
|
||||
|
||||
std::unordered_map<Cache::DeleterFn, CacheEntryRole> CopyCacheDeleterRoleMap() {
|
||||
UnorderedMap<Cache::DeleterFn, CacheEntryRole> CopyCacheDeleterRoleMap() {
|
||||
return GetRegistry().Copy();
|
||||
}
|
||||
|
||||
|
||||
Vendored
+4
-35
@@ -9,46 +9,15 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
#include "util/hash_containers.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Classifications of block cache entries, for reporting statistics
|
||||
// Adding new enum to this class requires corresponding updates to
|
||||
// kCacheEntryRoleToCamelString and kCacheEntryRoleToHyphenString
|
||||
enum class CacheEntryRole {
|
||||
// Block-based table data block
|
||||
kDataBlock,
|
||||
// Block-based table filter block (full or partitioned)
|
||||
kFilterBlock,
|
||||
// Block-based table metadata block for partitioned filter
|
||||
kFilterMetaBlock,
|
||||
// Block-based table deprecated filter block (old "block-based" filter)
|
||||
kDeprecatedFilterBlock,
|
||||
// Block-based table index block
|
||||
kIndexBlock,
|
||||
// Other kinds of block-based table block
|
||||
kOtherBlock,
|
||||
// WriteBufferManager reservations to account for memtable usage
|
||||
kWriteBuffer,
|
||||
// BlockBasedTableBuilder reservations to account for
|
||||
// compression dictionary building buffer's memory usage
|
||||
kCompressionDictionaryBuildingBuffer,
|
||||
// Filter reservations to account for
|
||||
// (new) bloom and ribbon filter construction's memory usage
|
||||
kFilterConstruction,
|
||||
// Default bucket, for miscellaneous cache entries. Do not use for
|
||||
// entries that could potentially add up to large usage.
|
||||
kMisc,
|
||||
};
|
||||
constexpr uint32_t kNumCacheEntryRoles =
|
||||
static_cast<uint32_t>(CacheEntryRole::kMisc) + 1;
|
||||
|
||||
extern std::array<const char*, kNumCacheEntryRoles>
|
||||
extern std::array<std::string, kNumCacheEntryRoles>
|
||||
kCacheEntryRoleToCamelString;
|
||||
extern std::array<const char*, kNumCacheEntryRoles>
|
||||
extern std::array<std::string, kNumCacheEntryRoles>
|
||||
kCacheEntryRoleToHyphenString;
|
||||
|
||||
// To associate cache entries with their role, we use a hack on the
|
||||
@@ -75,7 +44,7 @@ void RegisterCacheDeleterRole(Cache::DeleterFn fn, CacheEntryRole role);
|
||||
// * This is suitable for preparing for batch operations, like with
|
||||
// CacheEntryStatsCollector.
|
||||
// * The number of mappings should be sufficiently small (dozens).
|
||||
std::unordered_map<Cache::DeleterFn, CacheEntryRole> CopyCacheDeleterRoleMap();
|
||||
UnorderedMap<Cache::DeleterFn, CacheEntryRole> CopyCacheDeleterRoleMap();
|
||||
|
||||
// ************************************************************** //
|
||||
// An automatic registration infrastructure. This enables code
|
||||
|
||||
Vendored
+59
-64
@@ -17,12 +17,30 @@
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
#include "table/block_based/reader_common.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
CacheReservationManager::CacheReservationManager(std::shared_ptr<Cache> cache,
|
||||
bool delayed_decrease)
|
||||
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationManagerImpl<R>::CacheReservationHandle::CacheReservationHandle(
|
||||
std::size_t incremental_memory_used,
|
||||
std::shared_ptr<CacheReservationManagerImpl> cache_res_mgr)
|
||||
: incremental_memory_used_(incremental_memory_used) {
|
||||
assert(cache_res_mgr);
|
||||
cache_res_mgr_ = cache_res_mgr;
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationManagerImpl<
|
||||
R>::CacheReservationHandle::~CacheReservationHandle() {
|
||||
Status s = cache_res_mgr_->ReleaseCacheReservation(incremental_memory_used_);
|
||||
s.PermitUncheckedError();
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationManagerImpl<R>::CacheReservationManagerImpl(
|
||||
std::shared_ptr<Cache> cache, bool delayed_decrease)
|
||||
: delayed_decrease_(delayed_decrease),
|
||||
cache_allocated_size_(0),
|
||||
memory_used_(0) {
|
||||
@@ -30,14 +48,15 @@ CacheReservationManager::CacheReservationManager(std::shared_ptr<Cache> cache,
|
||||
cache_ = cache;
|
||||
}
|
||||
|
||||
CacheReservationManager::~CacheReservationManager() {
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationManagerImpl<R>::~CacheReservationManagerImpl() {
|
||||
for (auto* handle : dummy_handles_) {
|
||||
cache_->Release(handle, true);
|
||||
}
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
Status CacheReservationManager::UpdateCacheReservation(
|
||||
Status CacheReservationManagerImpl<R>::UpdateCacheReservation(
|
||||
std::size_t new_mem_used) {
|
||||
memory_used_ = new_mem_used;
|
||||
std::size_t cur_cache_allocated_size =
|
||||
@@ -45,7 +64,7 @@ Status CacheReservationManager::UpdateCacheReservation(
|
||||
if (new_mem_used == cur_cache_allocated_size) {
|
||||
return Status::OK();
|
||||
} else if (new_mem_used > cur_cache_allocated_size) {
|
||||
Status s = IncreaseCacheReservation<R>(new_mem_used);
|
||||
Status s = IncreaseCacheReservation(new_mem_used);
|
||||
return s;
|
||||
} else {
|
||||
// In delayed decrease mode, we don't decrease cache reservation
|
||||
@@ -66,41 +85,32 @@ Status CacheReservationManager::UpdateCacheReservation(
|
||||
}
|
||||
}
|
||||
|
||||
// Explicitly instantiate templates for "CacheEntryRole" values we use.
|
||||
// This makes it possible to keep the template definitions in the .cc file.
|
||||
template Status CacheReservationManager::UpdateCacheReservation<
|
||||
CacheEntryRole::kWriteBuffer>(std::size_t new_mem_used);
|
||||
template Status CacheReservationManager::UpdateCacheReservation<
|
||||
CacheEntryRole::kCompressionDictionaryBuildingBuffer>(
|
||||
std::size_t new_mem_used);
|
||||
// For cache reservation manager unit tests
|
||||
template Status CacheReservationManager::UpdateCacheReservation<
|
||||
CacheEntryRole::kMisc>(std::size_t new_mem_used);
|
||||
|
||||
template <CacheEntryRole R>
|
||||
Status CacheReservationManager::MakeCacheReservation(
|
||||
Status CacheReservationManagerImpl<R>::MakeCacheReservation(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationHandle<R>>* handle) {
|
||||
assert(handle != nullptr);
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle>* handle) {
|
||||
assert(handle);
|
||||
Status s =
|
||||
UpdateCacheReservation<R>(GetTotalMemoryUsed() + incremental_memory_used);
|
||||
(*handle).reset(new CacheReservationHandle<R>(incremental_memory_used,
|
||||
shared_from_this()));
|
||||
UpdateCacheReservation(GetTotalMemoryUsed() + incremental_memory_used);
|
||||
(*handle).reset(new CacheReservationManagerImpl::CacheReservationHandle(
|
||||
incremental_memory_used,
|
||||
std::enable_shared_from_this<
|
||||
CacheReservationManagerImpl<R>>::shared_from_this()));
|
||||
return s;
|
||||
}
|
||||
|
||||
template Status
|
||||
CacheReservationManager::MakeCacheReservation<CacheEntryRole::kMisc>(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationHandle<CacheEntryRole::kMisc>>* handle);
|
||||
template Status CacheReservationManager::MakeCacheReservation<
|
||||
CacheEntryRole::kFilterConstruction>(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<
|
||||
CacheReservationHandle<CacheEntryRole::kFilterConstruction>>* handle);
|
||||
template <CacheEntryRole R>
|
||||
Status CacheReservationManagerImpl<R>::ReleaseCacheReservation(
|
||||
std::size_t incremental_memory_used) {
|
||||
assert(GetTotalMemoryUsed() >= incremental_memory_used);
|
||||
std::size_t updated_total_mem_used =
|
||||
GetTotalMemoryUsed() - incremental_memory_used;
|
||||
Status s = UpdateCacheReservation(updated_total_mem_used);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
Status CacheReservationManager::IncreaseCacheReservation(
|
||||
Status CacheReservationManagerImpl<R>::IncreaseCacheReservation(
|
||||
std::size_t new_mem_used) {
|
||||
Status return_status = Status::OK();
|
||||
while (new_mem_used > cache_allocated_size_.load(std::memory_order_relaxed)) {
|
||||
@@ -118,7 +128,8 @@ Status CacheReservationManager::IncreaseCacheReservation(
|
||||
return return_status;
|
||||
}
|
||||
|
||||
Status CacheReservationManager::DecreaseCacheReservation(
|
||||
template <CacheEntryRole R>
|
||||
Status CacheReservationManagerImpl<R>::DecreaseCacheReservation(
|
||||
std::size_t new_mem_used) {
|
||||
Status return_status = Status::OK();
|
||||
|
||||
@@ -137,15 +148,18 @@ Status CacheReservationManager::DecreaseCacheReservation(
|
||||
return return_status;
|
||||
}
|
||||
|
||||
std::size_t CacheReservationManager::GetTotalReservedCacheSize() {
|
||||
template <CacheEntryRole R>
|
||||
std::size_t CacheReservationManagerImpl<R>::GetTotalReservedCacheSize() {
|
||||
return cache_allocated_size_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
std::size_t CacheReservationManager::GetTotalMemoryUsed() {
|
||||
template <CacheEntryRole R>
|
||||
std::size_t CacheReservationManagerImpl<R>::GetTotalMemoryUsed() {
|
||||
return memory_used_;
|
||||
}
|
||||
|
||||
Slice CacheReservationManager::GetNextCacheKey() {
|
||||
template <CacheEntryRole R>
|
||||
Slice CacheReservationManagerImpl<R>::GetNextCacheKey() {
|
||||
// Calling this function will have the side-effect of changing the
|
||||
// underlying cache_key_ that is shared among other keys generated from this
|
||||
// fucntion. Therefore please make sure the previous keys are saved/copied
|
||||
@@ -155,34 +169,15 @@ Slice CacheReservationManager::GetNextCacheKey() {
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
Cache::DeleterFn CacheReservationManager::TEST_GetNoopDeleterForRole() {
|
||||
Cache::DeleterFn CacheReservationManagerImpl<R>::TEST_GetNoopDeleterForRole() {
|
||||
return GetNoopDeleterForRole<R>();
|
||||
}
|
||||
|
||||
template Cache::DeleterFn CacheReservationManager::TEST_GetNoopDeleterForRole<
|
||||
CacheEntryRole::kFilterConstruction>();
|
||||
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationHandle<R>::CacheReservationHandle(
|
||||
std::size_t incremental_memory_used,
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr)
|
||||
: incremental_memory_used_(incremental_memory_used) {
|
||||
assert(cache_res_mgr != nullptr);
|
||||
cache_res_mgr_ = cache_res_mgr;
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationHandle<R>::~CacheReservationHandle() {
|
||||
assert(cache_res_mgr_ != nullptr);
|
||||
assert(cache_res_mgr_->GetTotalMemoryUsed() >= incremental_memory_used_);
|
||||
|
||||
Status s = cache_res_mgr_->UpdateCacheReservation<R>(
|
||||
cache_res_mgr_->GetTotalMemoryUsed() - incremental_memory_used_);
|
||||
s.PermitUncheckedError();
|
||||
}
|
||||
|
||||
// Explicitly instantiate templates for "CacheEntryRole" values we use.
|
||||
// This makes it possible to keep the template definitions in the .cc file.
|
||||
template class CacheReservationHandle<CacheEntryRole::kMisc>;
|
||||
template class CacheReservationHandle<CacheEntryRole::kFilterConstruction>;
|
||||
template class CacheReservationManagerImpl<
|
||||
CacheEntryRole::kBlockBasedTableReader>;
|
||||
template class CacheReservationManagerImpl<
|
||||
CacheEntryRole::kCompressionDictionaryBuildingBuffer>;
|
||||
template class CacheReservationManagerImpl<CacheEntryRole::kFilterConstruction>;
|
||||
template class CacheReservationManagerImpl<CacheEntryRole::kMisc>;
|
||||
template class CacheReservationManagerImpl<CacheEntryRole::kWriteBuffer>;
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
Vendored
+146
-49
@@ -13,54 +13,90 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "cache/cache_entry_roles.h"
|
||||
#include "cache/cache_key.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
// CacheReservationManager is an interface for reserving cache space for the
|
||||
// memory used
|
||||
class CacheReservationManager {
|
||||
public:
|
||||
// CacheReservationHandle is for managing the lifetime of a cache reservation
|
||||
// for an incremental amount of memory used (i.e, incremental_memory_used)
|
||||
class CacheReservationHandle {
|
||||
public:
|
||||
virtual ~CacheReservationHandle() {}
|
||||
};
|
||||
virtual ~CacheReservationManager() {}
|
||||
virtual Status UpdateCacheReservation(std::size_t new_memory_used) = 0;
|
||||
virtual Status MakeCacheReservation(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle>
|
||||
*handle) = 0;
|
||||
virtual std::size_t GetTotalReservedCacheSize() = 0;
|
||||
virtual std::size_t GetTotalMemoryUsed() = 0;
|
||||
};
|
||||
|
||||
template <CacheEntryRole R>
|
||||
class CacheReservationHandle;
|
||||
|
||||
// CacheReservationManager is for reserving cache space for the memory used
|
||||
// through inserting/releasing dummy entries in the cache.
|
||||
// CacheReservationManagerImpl implements interface CacheReservationManager
|
||||
// for reserving cache space for the memory used by inserting/releasing dummy
|
||||
// entries in the cache.
|
||||
//
|
||||
// This class is NOT thread-safe, except that GetTotalReservedCacheSize()
|
||||
// can be called without external synchronization.
|
||||
class CacheReservationManager
|
||||
: public std::enable_shared_from_this<CacheReservationManager> {
|
||||
template <CacheEntryRole R>
|
||||
class CacheReservationManagerImpl
|
||||
: public CacheReservationManager,
|
||||
public std::enable_shared_from_this<CacheReservationManagerImpl<R>> {
|
||||
public:
|
||||
// Construct a CacheReservationManager
|
||||
class CacheReservationHandle
|
||||
: public CacheReservationManager::CacheReservationHandle {
|
||||
public:
|
||||
CacheReservationHandle(
|
||||
std::size_t incremental_memory_used,
|
||||
std::shared_ptr<CacheReservationManagerImpl> cache_res_mgr);
|
||||
~CacheReservationHandle() override;
|
||||
|
||||
private:
|
||||
std::size_t incremental_memory_used_;
|
||||
std::shared_ptr<CacheReservationManagerImpl> cache_res_mgr_;
|
||||
};
|
||||
|
||||
// Construct a CacheReservationManagerImpl
|
||||
// @param cache The cache where dummy entries are inserted and released for
|
||||
// reserving cache space
|
||||
// @param delayed_decrease If set true, then dummy entries won't be released
|
||||
// immediately when memory usage decreases.
|
||||
// immediately when memory usage decreases.
|
||||
// Instead, it will be released when the memory usage
|
||||
// decreases to 3/4 of what we have reserved so far.
|
||||
// This is for saving some future dummy entry
|
||||
// insertion when memory usage increases are likely to
|
||||
// happen in the near future.
|
||||
explicit CacheReservationManager(std::shared_ptr<Cache> cache,
|
||||
bool delayed_decrease = false);
|
||||
//
|
||||
// REQUIRED: cache is not nullptr
|
||||
explicit CacheReservationManagerImpl(std::shared_ptr<Cache> cache,
|
||||
bool delayed_decrease = false);
|
||||
|
||||
// no copy constructor, copy assignment, move constructor, move assignment
|
||||
CacheReservationManager(const CacheReservationManager &) = delete;
|
||||
CacheReservationManager &operator=(const CacheReservationManager &) = delete;
|
||||
CacheReservationManager(CacheReservationManager &&) = delete;
|
||||
CacheReservationManager &operator=(CacheReservationManager &&) = delete;
|
||||
CacheReservationManagerImpl(const CacheReservationManagerImpl &) = delete;
|
||||
CacheReservationManagerImpl &operator=(const CacheReservationManagerImpl &) =
|
||||
delete;
|
||||
CacheReservationManagerImpl(CacheReservationManagerImpl &&) = delete;
|
||||
CacheReservationManagerImpl &operator=(CacheReservationManagerImpl &&) =
|
||||
delete;
|
||||
|
||||
~CacheReservationManager();
|
||||
~CacheReservationManagerImpl() override;
|
||||
|
||||
template <CacheEntryRole R>
|
||||
|
||||
// One of the two ways of reserving/releasing cache,
|
||||
// see CacheReservationManager::MakeCacheReservation() for the other.
|
||||
// Use ONLY one of them to prevent unexpected behavior.
|
||||
// One of the two ways of reserving/releasing cache space,
|
||||
// see MakeCacheReservation() for the other.
|
||||
//
|
||||
// Use ONLY one of these two ways to prevent unexpected behavior.
|
||||
//
|
||||
// Insert and release dummy entries in the cache to
|
||||
// match the size of total dummy entries with the least multiple of
|
||||
@@ -90,11 +126,13 @@ class CacheReservationManager
|
||||
// Otherwise, it returns the first non-ok status;
|
||||
// On releasing dummy entries, it always returns Status::OK().
|
||||
// On keeping dummy entries the same, it always returns Status::OK().
|
||||
Status UpdateCacheReservation(std::size_t new_memory_used);
|
||||
Status UpdateCacheReservation(std::size_t new_memory_used) override;
|
||||
|
||||
// One of the two ways of reserving/releasing cache,
|
||||
// see CacheReservationManager::UpdateCacheReservation() for the other.
|
||||
// Use ONLY one of them to prevent unexpected behavior.
|
||||
// One of the two ways of reserving cache space and releasing is done through
|
||||
// destruction of CacheReservationHandle.
|
||||
// See UpdateCacheReservation() for the other way.
|
||||
//
|
||||
// Use ONLY one of these two ways to prevent unexpected behavior.
|
||||
//
|
||||
// Insert dummy entries in the cache for the incremental memory usage
|
||||
// to match the size of total dummy entries with the least multiple of
|
||||
@@ -118,21 +156,19 @@ class CacheReservationManager
|
||||
// calling MakeCacheReservation() is needed if you want
|
||||
// GetTotalMemoryUsed() indeed returns the latest memory used.
|
||||
//
|
||||
// @param handle An pointer to std::unique_ptr<CacheReservationHandle<R>> that
|
||||
// manages the lifetime of the handle and its cache reservation.
|
||||
// @param handle An pointer to std::unique_ptr<CacheReservationHandle> that
|
||||
// manages the lifetime of the cache reservation represented by the
|
||||
// handle.
|
||||
//
|
||||
// @return It returns Status::OK() if all dummy
|
||||
// entry insertions succeed.
|
||||
// Otherwise, it returns the first non-ok status;
|
||||
//
|
||||
// REQUIRES: handle != nullptr
|
||||
// REQUIRES: The CacheReservationManager object is NOT managed by
|
||||
// std::unique_ptr as CacheReservationHandle needs to
|
||||
// shares ownership to the CacheReservationManager object.
|
||||
template <CacheEntryRole R>
|
||||
Status MakeCacheReservation(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationHandle<R>> *handle);
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle> *handle)
|
||||
override;
|
||||
|
||||
// Return the size of the cache (which is a multiple of kSizeDummyEntry)
|
||||
// successfully reserved by calling UpdateCacheReservation().
|
||||
@@ -142,25 +178,25 @@ class CacheReservationManager
|
||||
// smaller number than the actual reserved cache size due to
|
||||
// the returned number will always be a multiple of kSizeDummyEntry
|
||||
// and cache full might happen in the middle of inserting a dummy entry.
|
||||
std::size_t GetTotalReservedCacheSize();
|
||||
std::size_t GetTotalReservedCacheSize() override;
|
||||
|
||||
// Return the latest total memory used indicated by the most recent call of
|
||||
// UpdateCacheReservation(std::size_t new_memory_used);
|
||||
std::size_t GetTotalMemoryUsed();
|
||||
std::size_t GetTotalMemoryUsed() override;
|
||||
|
||||
static constexpr std::size_t GetDummyEntrySize() { return kSizeDummyEntry; }
|
||||
|
||||
// For testing only - it is to help ensure the NoopDeleterForRole<R>
|
||||
// accessed from CacheReservationManager and the one accessed from the test
|
||||
// are from the same translation units
|
||||
template <CacheEntryRole R>
|
||||
// accessed from CacheReservationManagerImpl and the one accessed from the
|
||||
// test are from the same translation units
|
||||
static Cache::DeleterFn TEST_GetNoopDeleterForRole();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t kSizeDummyEntry = 256 * 1024;
|
||||
|
||||
Slice GetNextCacheKey();
|
||||
template <CacheEntryRole R>
|
||||
|
||||
Status ReleaseCacheReservation(std::size_t incremental_memory_used);
|
||||
Status IncreaseCacheReservation(std::size_t new_mem_used);
|
||||
Status DecreaseCacheReservation(std::size_t new_mem_used);
|
||||
|
||||
@@ -172,20 +208,81 @@ class CacheReservationManager
|
||||
CacheKey cache_key_;
|
||||
};
|
||||
|
||||
// CacheReservationHandle is for managing the lifetime of a cache reservation
|
||||
// This class is NOT thread-safe
|
||||
template <CacheEntryRole R>
|
||||
class CacheReservationHandle {
|
||||
class ConcurrentCacheReservationManager
|
||||
: public CacheReservationManager,
|
||||
public std::enable_shared_from_this<ConcurrentCacheReservationManager> {
|
||||
public:
|
||||
// REQUIRES: cache_res_mgr != nullptr
|
||||
explicit CacheReservationHandle(
|
||||
std::size_t incremental_memory_used,
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr);
|
||||
class CacheReservationHandle
|
||||
: public CacheReservationManager::CacheReservationHandle {
|
||||
public:
|
||||
CacheReservationHandle(
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr,
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle>
|
||||
cache_res_handle) {
|
||||
assert(cache_res_mgr && cache_res_handle);
|
||||
cache_res_mgr_ = cache_res_mgr;
|
||||
cache_res_handle_ = std::move(cache_res_handle);
|
||||
}
|
||||
|
||||
~CacheReservationHandle();
|
||||
~CacheReservationHandle() override {
|
||||
std::lock_guard<std::mutex> lock(cache_res_mgr_->cache_res_mgr_mu_);
|
||||
cache_res_handle_.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr_;
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle>
|
||||
cache_res_handle_;
|
||||
};
|
||||
|
||||
explicit ConcurrentCacheReservationManager(
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr) {
|
||||
cache_res_mgr_ = std::move(cache_res_mgr);
|
||||
}
|
||||
ConcurrentCacheReservationManager(const ConcurrentCacheReservationManager &) =
|
||||
delete;
|
||||
ConcurrentCacheReservationManager &operator=(
|
||||
const ConcurrentCacheReservationManager &) = delete;
|
||||
ConcurrentCacheReservationManager(ConcurrentCacheReservationManager &&) =
|
||||
delete;
|
||||
ConcurrentCacheReservationManager &operator=(
|
||||
ConcurrentCacheReservationManager &&) = delete;
|
||||
|
||||
~ConcurrentCacheReservationManager() override {}
|
||||
|
||||
inline Status UpdateCacheReservation(std::size_t new_memory_used) override {
|
||||
std::lock_guard<std::mutex> lock(cache_res_mgr_mu_);
|
||||
return cache_res_mgr_->UpdateCacheReservation(new_memory_used);
|
||||
}
|
||||
inline Status MakeCacheReservation(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle> *handle)
|
||||
override {
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle>
|
||||
wrapped_handle;
|
||||
Status s;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cache_res_mgr_mu_);
|
||||
s = cache_res_mgr_->MakeCacheReservation(incremental_memory_used,
|
||||
&wrapped_handle);
|
||||
}
|
||||
(*handle).reset(
|
||||
new ConcurrentCacheReservationManager::CacheReservationHandle(
|
||||
std::enable_shared_from_this<
|
||||
ConcurrentCacheReservationManager>::shared_from_this(),
|
||||
std::move(wrapped_handle)));
|
||||
return s;
|
||||
}
|
||||
inline std::size_t GetTotalReservedCacheSize() override {
|
||||
return cache_res_mgr_->GetTotalReservedCacheSize();
|
||||
}
|
||||
inline std::size_t GetTotalMemoryUsed() override {
|
||||
std::lock_guard<std::mutex> lock(cache_res_mgr_mu_);
|
||||
return cache_res_mgr_->GetTotalMemoryUsed();
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t incremental_memory_used_;
|
||||
std::mutex cache_res_mgr_mu_;
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr_;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+49
-87
@@ -15,7 +15,6 @@
|
||||
#include "cache/cache_entry_roles.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
@@ -23,25 +22,24 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class CacheReservationManagerTest : public ::testing::Test {
|
||||
protected:
|
||||
static constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
CacheReservationManagerImpl<CacheEntryRole::kMisc>::GetDummyEntrySize();
|
||||
static constexpr std::size_t kCacheCapacity = 4096 * kSizeDummyEntry;
|
||||
static constexpr int kNumShardBits = 0; // 2^0 shard
|
||||
static constexpr std::size_t kMetaDataChargeOverhead = 10000;
|
||||
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(kCacheCapacity, kNumShardBits);
|
||||
std::unique_ptr<CacheReservationManager> test_cache_rev_mng;
|
||||
std::shared_ptr<CacheReservationManager> test_cache_rev_mng;
|
||||
|
||||
CacheReservationManagerTest() {
|
||||
test_cache_rev_mng.reset(new CacheReservationManager(cache));
|
||||
test_cache_rev_mng =
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(CacheReservationManagerTest, GenerateCacheKey) {
|
||||
std::size_t new_mem_used = 1 * kSizeDummyEntry;
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_GE(cache->GetPinnedUsage(), 1 * kSizeDummyEntry);
|
||||
ASSERT_LT(cache->GetPinnedUsage(),
|
||||
@@ -49,13 +47,14 @@ TEST_F(CacheReservationManagerTest, GenerateCacheKey) {
|
||||
|
||||
// Next unique Cache key
|
||||
CacheKey ckey = CacheKey::CreateUniqueForCacheLifetime(cache.get());
|
||||
// Back it up to the one used by CRM (using CacheKey implementation details)
|
||||
using PairU64 = std::pair<uint64_t, uint64_t>;
|
||||
// Get to the underlying values
|
||||
using PairU64 = std::array<uint64_t, 2>;
|
||||
auto& ckey_pair = *reinterpret_cast<PairU64*>(&ckey);
|
||||
ckey_pair.second--;
|
||||
// Back it up to the one used by CRM (using CacheKey implementation details)
|
||||
ckey_pair[1]--;
|
||||
|
||||
// Specific key (subject to implementation details)
|
||||
EXPECT_EQ(ckey_pair, PairU64(0, 2));
|
||||
EXPECT_EQ(ckey_pair, PairU64({0, 2}));
|
||||
|
||||
Cache::Handle* handle = cache->Lookup(ckey.AsSlice());
|
||||
EXPECT_NE(handle, nullptr)
|
||||
@@ -66,10 +65,7 @@ TEST_F(CacheReservationManagerTest, GenerateCacheKey) {
|
||||
|
||||
TEST_F(CacheReservationManagerTest, KeepCacheReservationTheSame) {
|
||||
std::size_t new_mem_used = 1 * kSizeDummyEntry;
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
1 * kSizeDummyEntry);
|
||||
@@ -79,9 +75,7 @@ TEST_F(CacheReservationManagerTest, KeepCacheReservationTheSame) {
|
||||
ASSERT_LT(initial_pinned_usage,
|
||||
1 * kSizeDummyEntry + kMetaDataChargeOverhead);
|
||||
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to keep cache reservation the same when new_mem_used equals "
|
||||
"to current cache reservation";
|
||||
@@ -100,10 +94,7 @@ TEST_F(CacheReservationManagerTest, KeepCacheReservationTheSame) {
|
||||
TEST_F(CacheReservationManagerTest,
|
||||
IncreaseCacheReservationByMultiplesOfDummyEntrySize) {
|
||||
std::size_t new_mem_used = 2 * kSizeDummyEntry;
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to increase cache reservation correctly";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
@@ -121,10 +112,7 @@ TEST_F(CacheReservationManagerTest,
|
||||
TEST_F(CacheReservationManagerTest,
|
||||
IncreaseCacheReservationNotByMultiplesOfDummyEntrySize) {
|
||||
std::size_t new_mem_used = 2 * kSizeDummyEntry + kSizeDummyEntry / 2;
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to increase cache reservation correctly";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
@@ -143,7 +131,7 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
IncreaseCacheReservationOnFullCache) {
|
||||
;
|
||||
constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
CacheReservationManagerImpl<CacheEntryRole::kMisc>::GetDummyEntrySize();
|
||||
constexpr std::size_t kSmallCacheCapacity = 4 * kSizeDummyEntry;
|
||||
constexpr std::size_t kBigCacheCapacity = 4096 * kSizeDummyEntry;
|
||||
constexpr std::size_t kMetaDataChargeOverhead = 10000;
|
||||
@@ -153,14 +141,12 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
lo.num_shard_bits = 0; // 2^0 shard
|
||||
lo.strict_capacity_limit = true;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lo);
|
||||
std::unique_ptr<CacheReservationManager> test_cache_rev_mng(
|
||||
new CacheReservationManager(cache));
|
||||
std::shared_ptr<CacheReservationManager> test_cache_rev_mng =
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache);
|
||||
|
||||
std::size_t new_mem_used = kSmallCacheCapacity + 1;
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::Incomplete())
|
||||
<< "Failed to return status to indicate failure of dummy entry insertion "
|
||||
"during cache reservation on full cache";
|
||||
@@ -183,9 +169,7 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
"encountering cache resevation failure due to full cache";
|
||||
|
||||
new_mem_used = kSmallCacheCapacity / 2; // 2 dummy entries
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to decrease cache reservation after encountering cache "
|
||||
"reservation failure due to full cache";
|
||||
@@ -207,9 +191,7 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
|
||||
// Create cache full again for subsequent tests
|
||||
new_mem_used = kSmallCacheCapacity + 1;
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::Incomplete())
|
||||
<< "Failed to return status to indicate failure of dummy entry insertion "
|
||||
"during cache reservation on full cache";
|
||||
@@ -235,9 +217,7 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
// succeed
|
||||
cache->SetCapacity(kBigCacheCapacity);
|
||||
new_mem_used = kSmallCacheCapacity + 1;
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to increase cache reservation after increasing cache capacity "
|
||||
"and mitigating cache full error";
|
||||
@@ -259,10 +239,7 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
TEST_F(CacheReservationManagerTest,
|
||||
DecreaseCacheReservationByMultiplesOfDummyEntrySize) {
|
||||
std::size_t new_mem_used = 2 * kSizeDummyEntry;
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
2 * kSizeDummyEntry);
|
||||
@@ -272,9 +249,7 @@ TEST_F(CacheReservationManagerTest,
|
||||
2 * kSizeDummyEntry + kMetaDataChargeOverhead);
|
||||
|
||||
new_mem_used = 1 * kSizeDummyEntry;
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to decrease cache reservation correctly";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
@@ -292,10 +267,7 @@ TEST_F(CacheReservationManagerTest,
|
||||
TEST_F(CacheReservationManagerTest,
|
||||
DecreaseCacheReservationNotByMultiplesOfDummyEntrySize) {
|
||||
std::size_t new_mem_used = 2 * kSizeDummyEntry;
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
2 * kSizeDummyEntry);
|
||||
@@ -305,9 +277,7 @@ TEST_F(CacheReservationManagerTest,
|
||||
2 * kSizeDummyEntry + kMetaDataChargeOverhead);
|
||||
|
||||
new_mem_used = kSizeDummyEntry / 2;
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to decrease cache reservation correctly";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
@@ -325,7 +295,7 @@ TEST_F(CacheReservationManagerTest,
|
||||
TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
DecreaseCacheReservationWithDelayedDecrease) {
|
||||
constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
CacheReservationManagerImpl<CacheEntryRole::kMisc>::GetDummyEntrySize();
|
||||
constexpr std::size_t kCacheCapacity = 4096 * kSizeDummyEntry;
|
||||
constexpr std::size_t kMetaDataChargeOverhead = 10000;
|
||||
|
||||
@@ -333,14 +303,12 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
lo.capacity = kCacheCapacity;
|
||||
lo.num_shard_bits = 0;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lo);
|
||||
std::unique_ptr<CacheReservationManager> test_cache_rev_mng(
|
||||
new CacheReservationManager(cache, true /* delayed_decrease */));
|
||||
std::shared_ptr<CacheReservationManager> test_cache_rev_mng =
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache, true /* delayed_decrease */);
|
||||
|
||||
std::size_t new_mem_used = 8 * kSizeDummyEntry;
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
8 * kSizeDummyEntry);
|
||||
@@ -351,9 +319,7 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
8 * kSizeDummyEntry + kMetaDataChargeOverhead);
|
||||
|
||||
new_mem_used = 6 * kSizeDummyEntry;
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK()) << "Failed to delay decreasing cache reservation";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
8 * kSizeDummyEntry)
|
||||
@@ -365,9 +331,7 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
<< "Failed to delay decreasing underlying dummy entries in cache";
|
||||
|
||||
new_mem_used = 7 * kSizeDummyEntry;
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK()) << "Failed to delay decreasing cache reservation";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
8 * kSizeDummyEntry)
|
||||
@@ -379,9 +343,7 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
<< "Failed to delay decreasing underlying dummy entries in cache";
|
||||
|
||||
new_mem_used = 6 * kSizeDummyEntry - 1;
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to decrease cache reservation correctly when new_mem_used < "
|
||||
"GetTotalReservedCacheSize() * 3 / 4 on delayed decrease mode";
|
||||
@@ -405,7 +367,7 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
TEST(CacheReservationManagerDestructorTest,
|
||||
ReleaseRemainingDummyEntriesOnDestruction) {
|
||||
constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
CacheReservationManagerImpl<CacheEntryRole::kMisc>::GetDummyEntrySize();
|
||||
constexpr std::size_t kCacheCapacity = 4096 * kSizeDummyEntry;
|
||||
constexpr std::size_t kMetaDataChargeOverhead = 10000;
|
||||
|
||||
@@ -414,13 +376,11 @@ TEST(CacheReservationManagerDestructorTest,
|
||||
lo.num_shard_bits = 0;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lo);
|
||||
{
|
||||
std::unique_ptr<CacheReservationManager> test_cache_rev_mng(
|
||||
new CacheReservationManager(cache));
|
||||
std::shared_ptr<CacheReservationManager> test_cache_rev_mng =
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache);
|
||||
std::size_t new_mem_used = 1 * kSizeDummyEntry;
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_GE(cache->GetPinnedUsage(), 1 * kSizeDummyEntry);
|
||||
ASSERT_LT(cache->GetPinnedUsage(),
|
||||
@@ -442,18 +402,19 @@ TEST(CacheReservationHandleTest, HandleTest) {
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lo);
|
||||
|
||||
std::shared_ptr<CacheReservationManager> test_cache_rev_mng(
|
||||
std::make_shared<CacheReservationManager>(cache));
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache));
|
||||
|
||||
std::size_t mem_used = 0;
|
||||
const std::size_t incremental_mem_used_handle_1 = 1 * kSizeDummyEntry;
|
||||
const std::size_t incremental_mem_used_handle_2 = 2 * kSizeDummyEntry;
|
||||
std::unique_ptr<CacheReservationHandle<CacheEntryRole::kMisc>> handle_1,
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle> handle_1,
|
||||
handle_2;
|
||||
|
||||
// To test consecutive CacheReservationManager::MakeCacheReservation works
|
||||
// correctly in terms of returning the handle as well as updating cache
|
||||
// reservation and the latest total memory used
|
||||
Status s = test_cache_rev_mng->MakeCacheReservation<CacheEntryRole::kMisc>(
|
||||
Status s = test_cache_rev_mng->MakeCacheReservation(
|
||||
incremental_mem_used_handle_1, &handle_1);
|
||||
mem_used = mem_used + incremental_mem_used_handle_1;
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
@@ -463,8 +424,8 @@ TEST(CacheReservationHandleTest, HandleTest) {
|
||||
EXPECT_GE(cache->GetPinnedUsage(), mem_used);
|
||||
EXPECT_LT(cache->GetPinnedUsage(), mem_used + kMetaDataChargeOverhead);
|
||||
|
||||
s = test_cache_rev_mng->MakeCacheReservation<CacheEntryRole::kMisc>(
|
||||
incremental_mem_used_handle_2, &handle_2);
|
||||
s = test_cache_rev_mng->MakeCacheReservation(incremental_mem_used_handle_2,
|
||||
&handle_2);
|
||||
mem_used = mem_used + incremental_mem_used_handle_2;
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
EXPECT_TRUE(handle_2 != nullptr);
|
||||
@@ -473,8 +434,9 @@ TEST(CacheReservationHandleTest, HandleTest) {
|
||||
EXPECT_GE(cache->GetPinnedUsage(), mem_used);
|
||||
EXPECT_LT(cache->GetPinnedUsage(), mem_used + kMetaDataChargeOverhead);
|
||||
|
||||
// To test CacheReservationHandle::~CacheReservationHandle() works correctly
|
||||
// in releasing the cache reserved for the handle
|
||||
// To test
|
||||
// CacheReservationManager::CacheReservationHandle::~CacheReservationHandle()
|
||||
// works correctly in releasing the cache reserved for the handle
|
||||
handle_1.reset();
|
||||
EXPECT_TRUE(handle_1 == nullptr);
|
||||
mem_used = mem_used - incremental_mem_used_handle_1;
|
||||
|
||||
Vendored
+5
-5
@@ -286,8 +286,8 @@ class ClockCacheShard final : public CacheShard {
|
||||
return Lookup(key, hash);
|
||||
}
|
||||
bool Release(Cache::Handle* handle, bool /*useful*/,
|
||||
bool force_erase) override {
|
||||
return Release(handle, force_erase);
|
||||
bool erase_if_last_ref) override {
|
||||
return Release(handle, erase_if_last_ref);
|
||||
}
|
||||
bool IsReady(Cache::Handle* /*handle*/) override { return true; }
|
||||
void Wait(Cache::Handle* /*handle*/) override {}
|
||||
@@ -297,7 +297,7 @@ class ClockCacheShard final : public CacheShard {
|
||||
//
|
||||
// Not necessary to hold mutex_ before being called.
|
||||
bool Ref(Cache::Handle* handle) override;
|
||||
bool Release(Cache::Handle* handle, bool force_erase = false) override;
|
||||
bool Release(Cache::Handle* handle, bool erase_if_last_ref = false) override;
|
||||
void Erase(const Slice& key, uint32_t hash) override;
|
||||
bool EraseAndConfirm(const Slice& key, uint32_t hash,
|
||||
CleanupContext* context);
|
||||
@@ -725,11 +725,11 @@ Cache::Handle* ClockCacheShard::Lookup(const Slice& key, uint32_t hash) {
|
||||
return reinterpret_cast<Cache::Handle*>(handle);
|
||||
}
|
||||
|
||||
bool ClockCacheShard::Release(Cache::Handle* h, bool force_erase) {
|
||||
bool ClockCacheShard::Release(Cache::Handle* h, bool erase_if_last_ref) {
|
||||
CleanupContext context;
|
||||
CacheHandle* handle = reinterpret_cast<CacheHandle*>(h);
|
||||
bool erased = Unref(handle, true, &context);
|
||||
if (force_erase && !erased) {
|
||||
if (erase_if_last_ref && !erased) {
|
||||
erased = EraseAndConfirm(handle->key, handle->hash, &context);
|
||||
}
|
||||
Cleanup(context);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "cache/lru_secondary_cache.h"
|
||||
#include "cache/compressed_secondary_cache.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -22,7 +22,7 @@ void DeletionCallback(const Slice& /*key*/, void* obj) {
|
||||
|
||||
} // namespace
|
||||
|
||||
LRUSecondaryCache::LRUSecondaryCache(
|
||||
CompressedSecondaryCache::CompressedSecondaryCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator, bool use_adaptive_mutex,
|
||||
@@ -37,11 +37,13 @@ LRUSecondaryCache::LRUSecondaryCache(
|
||||
use_adaptive_mutex, metadata_charge_policy);
|
||||
}
|
||||
|
||||
LRUSecondaryCache::~LRUSecondaryCache() { cache_.reset(); }
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() { cache_.reset(); }
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> LRUSecondaryCache::Lookup(
|
||||
const Slice& key, const Cache::CreateCallback& create_cb, bool /*wait*/) {
|
||||
std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
const Slice& key, const Cache::CreateCallback& create_cb, bool /*wait*/,
|
||||
bool& is_in_sec_cache) {
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle;
|
||||
is_in_sec_cache = false;
|
||||
Cache::Handle* lru_handle = cache_->Lookup(key);
|
||||
if (lru_handle == nullptr) {
|
||||
return handle;
|
||||
@@ -69,24 +71,25 @@ std::unique_ptr<SecondaryCacheResultHandle> LRUSecondaryCache::Lookup(
|
||||
cache_options_.memory_allocator.get());
|
||||
|
||||
if (!uncompressed) {
|
||||
cache_->Release(lru_handle, true);
|
||||
cache_->Release(lru_handle, /* erase_if_last_ref */ true);
|
||||
return handle;
|
||||
}
|
||||
s = create_cb(uncompressed.get(), uncompressed_size, &value, &charge);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
cache_->Release(lru_handle, true);
|
||||
cache_->Release(lru_handle, /* erase_if_last_ref */ true);
|
||||
return handle;
|
||||
}
|
||||
|
||||
handle.reset(new LRUSecondaryCacheResultHandle(value, charge));
|
||||
cache_->Release(lru_handle);
|
||||
cache_->Release(lru_handle, /* erase_if_last_ref */ true);
|
||||
handle.reset(new CompressedSecondaryCacheResultHandle(value, charge));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
Status LRUSecondaryCache::Insert(const Slice& key, void* value,
|
||||
const Cache::CacheItemHelper* helper) {
|
||||
Status CompressedSecondaryCache::Insert(const Slice& key, void* value,
|
||||
const Cache::CacheItemHelper* helper) {
|
||||
size_t size = (*helper->size_cb)(value);
|
||||
CacheAllocationPtr ptr =
|
||||
AllocateBlock(size, cache_options_.memory_allocator.get());
|
||||
@@ -125,9 +128,9 @@ Status LRUSecondaryCache::Insert(const Slice& key, void* value,
|
||||
return cache_->Insert(key, buf, size, DeletionCallback);
|
||||
}
|
||||
|
||||
void LRUSecondaryCache::Erase(const Slice& key) { cache_->Erase(key); }
|
||||
void CompressedSecondaryCache::Erase(const Slice& key) { cache_->Erase(key); }
|
||||
|
||||
std::string LRUSecondaryCache::GetPrintableOptions() const {
|
||||
std::string CompressedSecondaryCache::GetPrintableOptions() const {
|
||||
std::string ret;
|
||||
ret.reserve(20000);
|
||||
const int kBufferSize = 200;
|
||||
@@ -142,23 +145,23 @@ std::string LRUSecondaryCache::GetPrintableOptions() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache> NewLRUSecondaryCache(
|
||||
std::shared_ptr<SecondaryCache> NewCompressedSecondaryCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
CompressionType compression_type, uint32_t compress_format_version) {
|
||||
return std::make_shared<LRUSecondaryCache>(
|
||||
return std::make_shared<CompressedSecondaryCache>(
|
||||
capacity, num_shard_bits, strict_capacity_limit, high_pri_pool_ratio,
|
||||
memory_allocator, use_adaptive_mutex, metadata_charge_policy,
|
||||
compression_type, compress_format_version);
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache> NewLRUSecondaryCache(
|
||||
const LRUSecondaryCacheOptions& opts) {
|
||||
std::shared_ptr<SecondaryCache> NewCompressedSecondaryCache(
|
||||
const CompressedSecondaryCacheOptions& opts) {
|
||||
// The secondary_cache is disabled for this LRUCache instance.
|
||||
assert(opts.secondary_cache == nullptr);
|
||||
return NewLRUSecondaryCache(
|
||||
return NewCompressedSecondaryCache(
|
||||
opts.capacity, opts.num_shard_bits, opts.strict_capacity_limit,
|
||||
opts.high_pri_pool_ratio, opts.memory_allocator, opts.use_adaptive_mutex,
|
||||
opts.metadata_charge_policy, opts.compression_type,
|
||||
@@ -16,15 +16,16 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class LRUSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
|
||||
class CompressedSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
|
||||
public:
|
||||
LRUSecondaryCacheResultHandle(void* value, size_t size)
|
||||
CompressedSecondaryCacheResultHandle(void* value, size_t size)
|
||||
: value_(value), size_(size) {}
|
||||
virtual ~LRUSecondaryCacheResultHandle() override = default;
|
||||
virtual ~CompressedSecondaryCacheResultHandle() override = default;
|
||||
|
||||
LRUSecondaryCacheResultHandle(const LRUSecondaryCacheResultHandle&) = delete;
|
||||
LRUSecondaryCacheResultHandle& operator=(
|
||||
const LRUSecondaryCacheResultHandle&) = delete;
|
||||
CompressedSecondaryCacheResultHandle(
|
||||
const CompressedSecondaryCacheResultHandle&) = delete;
|
||||
CompressedSecondaryCacheResultHandle& operator=(
|
||||
const CompressedSecondaryCacheResultHandle&) = delete;
|
||||
|
||||
bool IsReady() override { return true; }
|
||||
|
||||
@@ -39,19 +40,19 @@ class LRUSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
// The LRUSecondaryCache is a concrete implementation of
|
||||
// The CompressedSecondaryCache is a concrete implementation of
|
||||
// rocksdb::SecondaryCache.
|
||||
//
|
||||
// Users can also cast a pointer to it and call methods on
|
||||
// it directly, especially custom methods that may be added
|
||||
// in the future. For example -
|
||||
// std::unique_ptr<rocksdb::SecondaryCache> cache =
|
||||
// NewLRUSecondaryCache(opts);
|
||||
// static_cast<LRUSecondaryCache*>(cache.get())->Erase(key);
|
||||
// NewCompressedSecondaryCache(opts);
|
||||
// static_cast<CompressedSecondaryCache*>(cache.get())->Erase(key);
|
||||
|
||||
class LRUSecondaryCache : public SecondaryCache {
|
||||
class CompressedSecondaryCache : public SecondaryCache {
|
||||
public:
|
||||
LRUSecondaryCache(
|
||||
CompressedSecondaryCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator = nullptr,
|
||||
@@ -60,16 +61,16 @@ class LRUSecondaryCache : public SecondaryCache {
|
||||
kDontChargeCacheMetadata,
|
||||
CompressionType compression_type = CompressionType::kLZ4Compression,
|
||||
uint32_t compress_format_version = 2);
|
||||
virtual ~LRUSecondaryCache() override;
|
||||
virtual ~CompressedSecondaryCache() override;
|
||||
|
||||
const char* Name() const override { return "LRUSecondaryCache"; }
|
||||
const char* Name() const override { return "CompressedSecondaryCache"; }
|
||||
|
||||
Status Insert(const Slice& key, void* value,
|
||||
const Cache::CacheItemHelper* helper) override;
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CreateCallback& create_cb,
|
||||
bool /*wait*/) override;
|
||||
const Slice& key, const Cache::CreateCallback& create_cb, bool /*wait*/,
|
||||
bool& is_in_sec_cache) override;
|
||||
|
||||
void Erase(const Slice& key) override;
|
||||
|
||||
@@ -79,7 +80,7 @@ class LRUSecondaryCache : public SecondaryCache {
|
||||
|
||||
private:
|
||||
std::shared_ptr<Cache> cache_;
|
||||
LRUSecondaryCacheOptions cache_options_;
|
||||
CompressedSecondaryCacheOptions cache_options_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+134
-124
@@ -3,7 +3,7 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "cache/lru_secondary_cache.h"
|
||||
#include "cache/compressed_secondary_cache.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
@@ -17,10 +17,10 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class LRUSecondaryCacheTest : public testing::Test {
|
||||
class CompressedSecondaryCacheTest : public testing::Test {
|
||||
public:
|
||||
LRUSecondaryCacheTest() : fail_create_(false) {}
|
||||
~LRUSecondaryCacheTest() {}
|
||||
CompressedSecondaryCacheTest() : fail_create_(false) {}
|
||||
~CompressedSecondaryCacheTest() {}
|
||||
|
||||
protected:
|
||||
class TestItem {
|
||||
@@ -80,7 +80,7 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
void SetFailCreate(bool fail) { fail_create_ = fail; }
|
||||
|
||||
void BasicTest(bool sec_cache_is_compressed, bool use_jemalloc) {
|
||||
LRUSecondaryCacheOptions opts;
|
||||
CompressedSecondaryCacheOptions opts;
|
||||
opts.capacity = 2048;
|
||||
opts.num_shard_bits = 0;
|
||||
opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
@@ -107,11 +107,13 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
ROCKSDB_GTEST_BYPASS("JEMALLOC not supported");
|
||||
}
|
||||
}
|
||||
std::shared_ptr<SecondaryCache> cache = NewLRUSecondaryCache(opts);
|
||||
std::shared_ptr<SecondaryCache> sec_cache =
|
||||
NewCompressedSecondaryCache(opts);
|
||||
|
||||
bool is_in_sec_cache{true};
|
||||
// Lookup an non-existent key.
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle0 =
|
||||
cache->Lookup("k0", test_item_creator, true);
|
||||
sec_cache->Lookup("k0", test_item_creator, true, is_in_sec_cache);
|
||||
ASSERT_EQ(handle0, nullptr);
|
||||
|
||||
Random rnd(301);
|
||||
@@ -119,51 +121,47 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
std::string str1;
|
||||
test::CompressibleString(&rnd, 0.25, 1000, &str1);
|
||||
TestItem item1(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", &item1, &LRUSecondaryCacheTest::helper_));
|
||||
ASSERT_OK(sec_cache->Insert("k1", &item1,
|
||||
&CompressedSecondaryCacheTest::helper_));
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1 =
|
||||
cache->Lookup("k1", test_item_creator, true);
|
||||
sec_cache->Lookup("k1", test_item_creator, true, is_in_sec_cache);
|
||||
ASSERT_NE(handle1, nullptr);
|
||||
// delete reinterpret_cast<TestItem*>(handle1->Value());
|
||||
ASSERT_FALSE(is_in_sec_cache);
|
||||
|
||||
std::unique_ptr<TestItem> val1 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle1->Value()));
|
||||
ASSERT_NE(val1, nullptr);
|
||||
ASSERT_EQ(memcmp(val1->Buf(), item1.Buf(), item1.Size()), 0);
|
||||
|
||||
// Lookup the first item again.
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
|
||||
sec_cache->Lookup("k1", test_item_creator, true, is_in_sec_cache);
|
||||
ASSERT_EQ(handle1_1, nullptr);
|
||||
|
||||
// Insert and Lookup the second item.
|
||||
std::string str2;
|
||||
test::CompressibleString(&rnd, 0.5, 1000, &str2);
|
||||
TestItem item2(str2.data(), str2.length());
|
||||
ASSERT_OK(cache->Insert("k2", &item2, &LRUSecondaryCacheTest::helper_));
|
||||
ASSERT_OK(sec_cache->Insert("k2", &item2,
|
||||
&CompressedSecondaryCacheTest::helper_));
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle2 =
|
||||
cache->Lookup("k2", test_item_creator, true);
|
||||
sec_cache->Lookup("k2", test_item_creator, true, is_in_sec_cache);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
std::unique_ptr<TestItem> val2 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle2->Value()));
|
||||
ASSERT_NE(val2, nullptr);
|
||||
ASSERT_EQ(memcmp(val2->Buf(), item2.Buf(), item2.Size()), 0);
|
||||
|
||||
// Lookup the first item again to make sure it is still in the cache.
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
|
||||
cache->Lookup("k1", test_item_creator, true);
|
||||
ASSERT_NE(handle1_1, nullptr);
|
||||
std::unique_ptr<TestItem> val1_1 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle1_1->Value()));
|
||||
ASSERT_NE(val1_1, nullptr);
|
||||
ASSERT_EQ(memcmp(val1_1->Buf(), item1.Buf(), item1.Size()), 0);
|
||||
|
||||
std::vector<SecondaryCacheResultHandle*> handles = {handle1.get(),
|
||||
handle2.get()};
|
||||
cache->WaitAll(handles);
|
||||
sec_cache->WaitAll(handles);
|
||||
|
||||
cache->Erase("k1");
|
||||
handle1 = cache->Lookup("k1", test_item_creator, true);
|
||||
ASSERT_EQ(handle1, nullptr);
|
||||
|
||||
cache.reset();
|
||||
sec_cache.reset();
|
||||
}
|
||||
|
||||
void FailsTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
CompressedSecondaryCacheOptions secondary_cache_opts;
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
@@ -176,32 +174,28 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
secondary_cache_opts.capacity = 1100;
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
std::shared_ptr<SecondaryCache> cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
std::shared_ptr<SecondaryCache> sec_cache =
|
||||
NewCompressedSecondaryCache(secondary_cache_opts);
|
||||
|
||||
// Insert and Lookup the first item.
|
||||
Random rnd(301);
|
||||
std::string str1(rnd.RandomString(1000));
|
||||
TestItem item1(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", &item1, &LRUSecondaryCacheTest::helper_));
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1 =
|
||||
cache->Lookup("k1", test_item_creator, true);
|
||||
ASSERT_NE(handle1, nullptr);
|
||||
std::unique_ptr<TestItem> val1 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle1->Value()));
|
||||
ASSERT_NE(val1, nullptr);
|
||||
ASSERT_EQ(memcmp(val1->Buf(), item1.Buf(), item1.Size()), 0);
|
||||
ASSERT_OK(sec_cache->Insert("k1", &item1,
|
||||
&CompressedSecondaryCacheTest::helper_));
|
||||
|
||||
// Insert and Lookup the second item.
|
||||
std::string str2(rnd.RandomString(200));
|
||||
TestItem item2(str2.data(), str2.length());
|
||||
// k1 is evicted.
|
||||
ASSERT_OK(cache->Insert("k2", &item2, &LRUSecondaryCacheTest::helper_));
|
||||
ASSERT_OK(sec_cache->Insert("k2", &item2,
|
||||
&CompressedSecondaryCacheTest::helper_));
|
||||
bool is_in_sec_cache{false};
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
|
||||
cache->Lookup("k1", test_item_creator, true);
|
||||
sec_cache->Lookup("k1", test_item_creator, true, is_in_sec_cache);
|
||||
ASSERT_EQ(handle1_1, nullptr);
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle2 =
|
||||
cache->Lookup("k2", test_item_creator, true);
|
||||
sec_cache->Lookup("k2", test_item_creator, true, is_in_sec_cache);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
std::unique_ptr<TestItem> val2 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle2->Value()));
|
||||
@@ -211,20 +205,20 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
// Create Fails.
|
||||
SetFailCreate(true);
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle2_1 =
|
||||
cache->Lookup("k2", test_item_creator, true);
|
||||
sec_cache->Lookup("k2", test_item_creator, true, is_in_sec_cache);
|
||||
ASSERT_EQ(handle2_1, nullptr);
|
||||
|
||||
// Save Fails.
|
||||
std::string str3 = rnd.RandomString(10);
|
||||
TestItem item3(str3.data(), str3.length());
|
||||
ASSERT_NOK(
|
||||
cache->Insert("k3", &item3, &LRUSecondaryCacheTest::helper_fail_));
|
||||
ASSERT_NOK(sec_cache->Insert("k3", &item3,
|
||||
&CompressedSecondaryCacheTest::helper_fail_));
|
||||
|
||||
cache.reset();
|
||||
sec_cache.reset();
|
||||
}
|
||||
|
||||
void BasicIntegrationTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
CompressedSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
@@ -239,7 +233,7 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
NewCompressedSecondaryCache(secondary_cache_opts);
|
||||
LRUCacheOptions lru_cache_opts(1024, 0, false, 0.5, nullptr,
|
||||
kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
@@ -252,26 +246,26 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
std::string str1 = rnd.RandomString(1010);
|
||||
std::string str1_clone{str1};
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k1", item1, &CompressedSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// After Insert, lru cache contains k2 and secondary cache contains k1.
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k2", item2, &CompressedSecondaryCacheTest::helper_,
|
||||
str2.length()));
|
||||
|
||||
std::string str3 = rnd.RandomString(1020);
|
||||
TestItem* item3 = new TestItem(str3.data(), str3.length());
|
||||
// After Insert, lru cache contains k3 and secondary cache contains k1 and
|
||||
// k2
|
||||
ASSERT_OK(cache->Insert("k3", item3, &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k3", item3, &CompressedSecondaryCacheTest::helper_,
|
||||
str3.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle =
|
||||
cache->Lookup("k3", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
handle = cache->Lookup("k3", &CompressedSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true,
|
||||
stats.get());
|
||||
ASSERT_NE(handle, nullptr);
|
||||
TestItem* val3 = static_cast<TestItem*>(cache->Value(handle));
|
||||
ASSERT_NE(val3, nullptr);
|
||||
@@ -279,34 +273,35 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
cache->Release(handle);
|
||||
|
||||
// Lookup an non-existent key.
|
||||
handle =
|
||||
cache->Lookup("k0", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
handle = cache->Lookup("k0", &CompressedSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true,
|
||||
stats.get());
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
|
||||
// This Lookup should promote k1 and demote k3, so k2 is evicted from the
|
||||
// secondary cache. The lru cache contains k1 and secondary cache contains
|
||||
// k3. item1 was Free(), so it cannot be compared against the item1.
|
||||
handle =
|
||||
cache->Lookup("k1", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
// This Lookup should promote k1 and erase k1 from the secondary cache,
|
||||
// then k3 is demoted. So k2 and k3 are in the secondary cache.
|
||||
handle = cache->Lookup("k1", &CompressedSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true,
|
||||
stats.get());
|
||||
|
||||
ASSERT_NE(handle, nullptr);
|
||||
TestItem* val1_1 = static_cast<TestItem*>(cache->Value(handle));
|
||||
ASSERT_NE(val1_1, nullptr);
|
||||
ASSERT_EQ(memcmp(val1_1->Buf(), str1_clone.data(), str1_clone.size()), 0);
|
||||
cache->Release(handle);
|
||||
|
||||
handle =
|
||||
cache->Lookup("k2", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
handle = cache->Lookup("k2", &CompressedSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true,
|
||||
stats.get());
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
void BasicIntegrationFailTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
CompressedSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
@@ -321,7 +316,7 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
NewCompressedSecondaryCache(secondary_cache_opts);
|
||||
|
||||
LRUCacheOptions opts(1024, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
@@ -333,7 +328,8 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
auto item1 =
|
||||
std::unique_ptr<TestItem>(new TestItem(str1.data(), str1.length()));
|
||||
ASSERT_NOK(cache->Insert("k1", item1.get(), nullptr, str1.length()));
|
||||
ASSERT_OK(cache->Insert("k1", item1.get(), &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k1", item1.get(),
|
||||
&CompressedSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
item1.release(); // Appease clang-analyze "potential memory leak"
|
||||
|
||||
@@ -341,7 +337,7 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
handle = cache->Lookup("k2", nullptr, test_item_creator,
|
||||
Cache::Priority::LOW, true);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
handle = cache->Lookup("k2", &CompressedSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, false);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
|
||||
@@ -350,7 +346,7 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
}
|
||||
|
||||
void IntegrationSaveFailTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
CompressedSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
@@ -366,7 +362,7 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
NewCompressedSecondaryCache(secondary_cache_opts);
|
||||
|
||||
LRUCacheOptions opts(1024, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
@@ -376,25 +372,27 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_fail_,
|
||||
ASSERT_OK(cache->Insert("k1", item1,
|
||||
&CompressedSecondaryCacheTest::helper_fail_,
|
||||
str1.length()));
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k1 should be demoted to the secondary cache.
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_fail_,
|
||||
ASSERT_OK(cache->Insert("k2", item2,
|
||||
&CompressedSecondaryCacheTest::helper_fail_,
|
||||
str2.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_fail_,
|
||||
handle = cache->Lookup("k2", &CompressedSecondaryCacheTest::helper_fail_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
// This lookup should fail, since k1 demotion would have failed
|
||||
handle = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_fail_,
|
||||
handle = cache->Lookup("k1", &CompressedSecondaryCacheTest::helper_fail_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
// Since k1 didn't get promoted, k2 should still be in cache
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_fail_,
|
||||
handle = cache->Lookup("k2", &CompressedSecondaryCacheTest::helper_fail_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
@@ -404,7 +402,7 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
}
|
||||
|
||||
void IntegrationCreateFailTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
CompressedSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
@@ -420,7 +418,7 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
NewCompressedSecondaryCache(secondary_cache_opts);
|
||||
|
||||
LRUCacheOptions opts(1024, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
@@ -430,27 +428,27 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k1", item1, &CompressedSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k1 should be demoted to the secondary cache.
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k2", item2, &CompressedSecondaryCacheTest::helper_,
|
||||
str2.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
SetFailCreate(true);
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
handle = cache->Lookup("k2", &CompressedSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
// This lookup should fail, since k1 creation would have failed
|
||||
handle = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_,
|
||||
handle = cache->Lookup("k1", &CompressedSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
// Since k1 didn't get promoted, k2 should still be in cache
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
handle = cache->Lookup("k2", &CompressedSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
@@ -460,7 +458,7 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
}
|
||||
|
||||
void IntegrationFullCapacityTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
CompressedSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
@@ -476,7 +474,7 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
NewCompressedSecondaryCache(secondary_cache_opts);
|
||||
|
||||
LRUCacheOptions opts(1024, 0, /*_strict_capacity_limit=*/true, 0.5, nullptr,
|
||||
kDefaultToAdaptiveMutex, kDontChargeCacheMetadata);
|
||||
@@ -486,31 +484,32 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k1", item1, &CompressedSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k1 should be demoted to the secondary cache.
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k2", item2, &CompressedSecondaryCacheTest::helper_,
|
||||
str2.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
// k1 promotion should fail due to the block cache being at capacity,
|
||||
// but the lookup should still succeed
|
||||
Cache::Handle* handle2;
|
||||
handle2 = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_,
|
||||
handle2 = cache->Lookup("k2", &CompressedSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
cache->Release(handle2);
|
||||
// k1 promotion should fail due to the block cache being at capacity,
|
||||
// but the lookup should still succeed
|
||||
Cache::Handle* handle1;
|
||||
handle1 = cache->Lookup("k1", &CompressedSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle1, nullptr);
|
||||
cache->Release(handle1);
|
||||
|
||||
// Since k1 didn't get inserted, k2 should still be in cache
|
||||
handle2 = cache->Lookup("k2", &CompressedSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
// Since k1 didn't get inserted, k2 should still be in cache
|
||||
cache->Release(handle);
|
||||
cache->Release(handle2);
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
@@ -520,72 +519,83 @@ class LRUSecondaryCacheTest : public testing::Test {
|
||||
bool fail_create_;
|
||||
};
|
||||
|
||||
Cache::CacheItemHelper LRUSecondaryCacheTest::helper_(
|
||||
LRUSecondaryCacheTest::SizeCallback, LRUSecondaryCacheTest::SaveToCallback,
|
||||
LRUSecondaryCacheTest::DeletionCallback);
|
||||
Cache::CacheItemHelper CompressedSecondaryCacheTest::helper_(
|
||||
CompressedSecondaryCacheTest::SizeCallback,
|
||||
CompressedSecondaryCacheTest::SaveToCallback,
|
||||
CompressedSecondaryCacheTest::DeletionCallback);
|
||||
|
||||
Cache::CacheItemHelper LRUSecondaryCacheTest::helper_fail_(
|
||||
LRUSecondaryCacheTest::SizeCallback,
|
||||
LRUSecondaryCacheTest::SaveToCallbackFail,
|
||||
LRUSecondaryCacheTest::DeletionCallback);
|
||||
Cache::CacheItemHelper CompressedSecondaryCacheTest::helper_fail_(
|
||||
CompressedSecondaryCacheTest::SizeCallback,
|
||||
CompressedSecondaryCacheTest::SaveToCallbackFail,
|
||||
CompressedSecondaryCacheTest::DeletionCallback);
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicTestWithNoCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest, BasicTestWithNoCompression) {
|
||||
BasicTest(false, false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicTestWithMemoryAllocatorAndNoCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest,
|
||||
BasicTestWithMemoryAllocatorAndNoCompression) {
|
||||
BasicTest(false, true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicTestWithCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest, BasicTestWithCompression) {
|
||||
BasicTest(true, false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicTestWithMemoryAllocatorAndCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest,
|
||||
BasicTestWithMemoryAllocatorAndCompression) {
|
||||
BasicTest(true, true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, FailsTestWithNoCompression) { FailsTest(false); }
|
||||
TEST_F(CompressedSecondaryCacheTest, FailsTestWithNoCompression) {
|
||||
FailsTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, FailsTestWithCompression) { FailsTest(true); }
|
||||
TEST_F(CompressedSecondaryCacheTest, FailsTestWithCompression) {
|
||||
FailsTest(true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicIntegrationTestWithNoCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest, BasicIntegrationTestWithNoCompression) {
|
||||
BasicIntegrationTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicIntegrationTestWithCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest, BasicIntegrationTestWithCompression) {
|
||||
BasicIntegrationTest(true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicIntegrationFailTestWithNoCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest,
|
||||
BasicIntegrationFailTestWithNoCompression) {
|
||||
BasicIntegrationFailTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicIntegrationFailTestWithCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest, BasicIntegrationFailTestWithCompression) {
|
||||
BasicIntegrationFailTest(true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationSaveFailTestWithNoCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest, IntegrationSaveFailTestWithNoCompression) {
|
||||
IntegrationSaveFailTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationSaveFailTestWithCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest, IntegrationSaveFailTestWithCompression) {
|
||||
IntegrationSaveFailTest(true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationCreateFailTestWithNoCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest,
|
||||
IntegrationCreateFailTestWithNoCompression) {
|
||||
IntegrationCreateFailTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationCreateFailTestWithCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest, IntegrationCreateFailTestWithCompression) {
|
||||
IntegrationCreateFailTest(true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationFullCapacityTestWithNoCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest,
|
||||
IntegrationFullCapacityTestWithNoCompression) {
|
||||
IntegrationFullCapacityTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationFullCapacityTestWithCompression) {
|
||||
TEST_F(CompressedSecondaryCacheTest,
|
||||
IntegrationFullCapacityTestWithCompression) {
|
||||
IntegrationFullCapacityTest(true);
|
||||
}
|
||||
|
||||
Vendored
+41
-40
@@ -76,13 +76,12 @@ LRUHandle** LRUHandleTable::FindPointer(const Slice& key, uint32_t hash) {
|
||||
|
||||
void LRUHandleTable::Resize() {
|
||||
if (length_bits_ >= max_length_bits_) {
|
||||
// Due to reaching limit of hash information, if we made the table
|
||||
// bigger, we would allocate more addresses but only the same
|
||||
// number would be used.
|
||||
// Due to reaching limit of hash information, if we made the table bigger,
|
||||
// we would allocate more addresses but only the same number would be used.
|
||||
return;
|
||||
}
|
||||
if (length_bits_ >= 31) {
|
||||
// Avoid undefined behavior shifting uint32_t by 32
|
||||
// Avoid undefined behavior shifting uint32_t by 32.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,7 +124,7 @@ LRUCacheShard::LRUCacheShard(
|
||||
mutex_(use_adaptive_mutex),
|
||||
secondary_cache_(secondary_cache) {
|
||||
set_metadata_charge_policy(metadata_charge_policy);
|
||||
// Make empty circular linked list
|
||||
// Make empty circular linked list.
|
||||
lru_.next = &lru_;
|
||||
lru_.prev = &lru_;
|
||||
lru_low_pri_ = &lru_;
|
||||
@@ -138,7 +137,7 @@ void LRUCacheShard::EraseUnRefEntries() {
|
||||
MutexLock l(&mutex_);
|
||||
while (lru_.next != &lru_) {
|
||||
LRUHandle* old = lru_.next;
|
||||
// LRU list contains only elements which can be evicted
|
||||
// LRU list contains only elements which can be evicted.
|
||||
assert(old->InCache() && !old->HasRefs());
|
||||
LRU_Remove(old);
|
||||
table_.Remove(old->key(), old->hash);
|
||||
@@ -168,7 +167,7 @@ void LRUCacheShard::ApplyToSomeEntries(
|
||||
|
||||
assert(average_entries_per_lock > 0);
|
||||
// Assuming we are called with same average_entries_per_lock repeatedly,
|
||||
// this simplifies some logic (index_end will not overflow)
|
||||
// this simplifies some logic (index_end will not overflow).
|
||||
assert(average_entries_per_lock < length || *state == 0);
|
||||
|
||||
uint32_t index_begin = *state >> (32 - length_bits);
|
||||
@@ -274,7 +273,7 @@ void LRUCacheShard::EvictFromLRU(size_t charge,
|
||||
autovector<LRUHandle*>* deleted) {
|
||||
while ((usage_ + charge) > capacity_ && lru_.next != &lru_) {
|
||||
LRUHandle* old = lru_.next;
|
||||
// LRU list contains only elements which can be evicted
|
||||
// LRU list contains only elements which can be evicted.
|
||||
assert(old->InCache() && !old->HasRefs());
|
||||
LRU_Remove(old);
|
||||
table_.Remove(old->key(), old->hash);
|
||||
@@ -295,11 +294,11 @@ void LRUCacheShard::SetCapacity(size_t capacity) {
|
||||
EvictFromLRU(0, &last_reference_list);
|
||||
}
|
||||
|
||||
// Try to insert the evicted entries into tiered cache
|
||||
// Free the entries outside of mutex for performance reasons
|
||||
// Try to insert the evicted entries into tiered cache.
|
||||
// Free the entries outside of mutex for performance reasons.
|
||||
for (auto entry : last_reference_list) {
|
||||
if (secondary_cache_ && entry->IsSecondaryCacheCompatible() &&
|
||||
!entry->IsPromoted()) {
|
||||
!entry->IsInSecondaryCache()) {
|
||||
secondary_cache_->Insert(entry->key(), entry->value, entry->info_.helper)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
@@ -322,7 +321,7 @@ Status LRUCacheShard::InsertItem(LRUHandle* e, Cache::Handle** handle,
|
||||
MutexLock l(&mutex_);
|
||||
|
||||
// Free the space following strict LRU policy until enough space
|
||||
// is freed or the lru list is empty
|
||||
// is freed or the lru list is empty.
|
||||
EvictFromLRU(total_charge, &last_reference_list);
|
||||
|
||||
if ((usage_ + total_charge) > capacity_ &&
|
||||
@@ -349,7 +348,7 @@ Status LRUCacheShard::InsertItem(LRUHandle* e, Cache::Handle** handle,
|
||||
assert(old->InCache());
|
||||
old->SetInCache(false);
|
||||
if (!old->HasRefs()) {
|
||||
// old is on LRU because it's in cache and its reference count is 0
|
||||
// old is on LRU because it's in cache and its reference count is 0.
|
||||
LRU_Remove(old);
|
||||
size_t old_total_charge =
|
||||
old->CalcTotalCharge(metadata_charge_policy_);
|
||||
@@ -361,7 +360,7 @@ Status LRUCacheShard::InsertItem(LRUHandle* e, Cache::Handle** handle,
|
||||
if (handle == nullptr) {
|
||||
LRU_Insert(e);
|
||||
} else {
|
||||
// If caller already holds a ref, no need to take one here
|
||||
// If caller already holds a ref, no need to take one here.
|
||||
if (!e->HasRefs()) {
|
||||
e->Ref();
|
||||
}
|
||||
@@ -370,11 +369,11 @@ Status LRUCacheShard::InsertItem(LRUHandle* e, Cache::Handle** handle,
|
||||
}
|
||||
}
|
||||
|
||||
// Try to insert the evicted entries into the secondary cache
|
||||
// Free the entries here outside of mutex for performance reasons
|
||||
// Try to insert the evicted entries into the secondary cache.
|
||||
// Free the entries here outside of mutex for performance reasons.
|
||||
for (auto entry : last_reference_list) {
|
||||
if (secondary_cache_ && entry->IsSecondaryCacheCompatible() &&
|
||||
!entry->IsPromoted()) {
|
||||
!entry->IsInSecondaryCache()) {
|
||||
secondary_cache_->Insert(entry->key(), entry->value, entry->info_.helper)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
@@ -390,7 +389,6 @@ void LRUCacheShard::Promote(LRUHandle* e) {
|
||||
assert(secondary_handle->IsReady());
|
||||
e->SetIncomplete(false);
|
||||
e->SetInCache(true);
|
||||
e->SetPromoted(true);
|
||||
e->value = secondary_handle->Value();
|
||||
e->charge = secondary_handle->Size();
|
||||
delete secondary_handle;
|
||||
@@ -404,7 +402,7 @@ void LRUCacheShard::Promote(LRUHandle* e) {
|
||||
Status s = InsertItem(e, &handle, /*free_handle_on_fail=*/false);
|
||||
if (!s.ok()) {
|
||||
// Item is in memory, but not accounted against the cache capacity.
|
||||
// When the handle is released, the item should get deleted
|
||||
// When the handle is released, the item should get deleted.
|
||||
assert(!e->InCache());
|
||||
}
|
||||
} else {
|
||||
@@ -437,8 +435,8 @@ Cache::Handle* LRUCacheShard::Lookup(
|
||||
}
|
||||
|
||||
// If handle table lookup failed, then allocate a handle outside the
|
||||
// mutex if we're going to lookup in the secondary cache
|
||||
// Only support synchronous for now
|
||||
// mutex if we're going to lookup in the secondary cache.
|
||||
// Only support synchronous for now.
|
||||
// TODO: Support asynchronous lookup in secondary cache
|
||||
if (!e && secondary_cache_ && helper && helper->saveto_cb) {
|
||||
// For objects from the secondary cache, we expect the caller to provide
|
||||
@@ -447,8 +445,9 @@ Cache::Handle* LRUCacheShard::Lookup(
|
||||
// accounting purposes, which we won't demote to the secondary cache
|
||||
// anyway.
|
||||
assert(create_cb && helper->del_cb);
|
||||
bool is_in_sec_cache{false};
|
||||
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle =
|
||||
secondary_cache_->Lookup(key, create_cb, wait);
|
||||
secondary_cache_->Lookup(key, create_cb, wait, is_in_sec_cache);
|
||||
if (secondary_handle != nullptr) {
|
||||
e = reinterpret_cast<LRUHandle*>(
|
||||
new char[sizeof(LRUHandle) - 1 + key.size()]);
|
||||
@@ -468,8 +467,9 @@ Cache::Handle* LRUCacheShard::Lookup(
|
||||
|
||||
if (wait) {
|
||||
Promote(e);
|
||||
e->SetIsInSecondaryCache(is_in_sec_cache);
|
||||
if (!e->value) {
|
||||
// The secondary cache returned a handle, but the lookup failed
|
||||
// The secondary cache returned a handle, but the lookup failed.
|
||||
e->Unref();
|
||||
e->Free();
|
||||
e = nullptr;
|
||||
@@ -479,8 +479,9 @@ Cache::Handle* LRUCacheShard::Lookup(
|
||||
}
|
||||
} else {
|
||||
// If wait is false, we always return a handle and let the caller
|
||||
// release the handle after checking for success or failure
|
||||
// release the handle after checking for success or failure.
|
||||
e->SetIncomplete(true);
|
||||
e->SetIsInSecondaryCache(is_in_sec_cache);
|
||||
// This may be slightly inaccurate, if the lookup eventually fails.
|
||||
// But the probability is very low.
|
||||
PERF_COUNTER_ADD(secondary_cache_hit_count, 1);
|
||||
@@ -494,7 +495,7 @@ Cache::Handle* LRUCacheShard::Lookup(
|
||||
bool LRUCacheShard::Ref(Cache::Handle* h) {
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(h);
|
||||
MutexLock l(&mutex_);
|
||||
// To create another reference - entry must be already externally referenced
|
||||
// To create another reference - entry must be already externally referenced.
|
||||
assert(e->HasRefs());
|
||||
e->Ref();
|
||||
return true;
|
||||
@@ -507,7 +508,7 @@ void LRUCacheShard::SetHighPriorityPoolRatio(double high_pri_pool_ratio) {
|
||||
MaintainPoolSize();
|
||||
}
|
||||
|
||||
bool LRUCacheShard::Release(Cache::Handle* handle, bool force_erase) {
|
||||
bool LRUCacheShard::Release(Cache::Handle* handle, bool erase_if_last_ref) {
|
||||
if (handle == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -517,15 +518,15 @@ bool LRUCacheShard::Release(Cache::Handle* handle, bool force_erase) {
|
||||
MutexLock l(&mutex_);
|
||||
last_reference = e->Unref();
|
||||
if (last_reference && e->InCache()) {
|
||||
// The item is still in cache, and nobody else holds a reference to it
|
||||
if (usage_ > capacity_ || force_erase) {
|
||||
// The LRU list must be empty since the cache is full
|
||||
assert(lru_.next == &lru_ || force_erase);
|
||||
// Take this opportunity and remove the item
|
||||
// The item is still in cache, and nobody else holds a reference to it.
|
||||
if (usage_ > capacity_ || erase_if_last_ref) {
|
||||
// The LRU list must be empty since the cache is full.
|
||||
assert(lru_.next == &lru_ || erase_if_last_ref);
|
||||
// Take this opportunity and remove the item.
|
||||
table_.Remove(e->key(), e->hash);
|
||||
e->SetInCache(false);
|
||||
} else {
|
||||
// Put the item back on the LRU list, and don't free it
|
||||
// Put the item back on the LRU list, and don't free it.
|
||||
LRU_Insert(e);
|
||||
last_reference = false;
|
||||
}
|
||||
@@ -542,7 +543,7 @@ bool LRUCacheShard::Release(Cache::Handle* handle, bool force_erase) {
|
||||
}
|
||||
}
|
||||
|
||||
// Free the entry here outside of mutex for performance reasons
|
||||
// Free the entry here outside of mutex for performance reasons.
|
||||
if (last_reference) {
|
||||
e->Free();
|
||||
}
|
||||
@@ -554,8 +555,8 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
const Cache::CacheItemHelper* helper,
|
||||
Cache::Handle** handle, Cache::Priority priority) {
|
||||
// Allocate the memory here outside of the mutex
|
||||
// If the cache is full, we'll have to release it
|
||||
// Allocate the memory here outside of the mutex.
|
||||
// If the cache is full, we'll have to release it.
|
||||
// It shouldn't happen very often though.
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(
|
||||
new char[sizeof(LRUHandle) - 1 + key.size()]);
|
||||
@@ -603,8 +604,8 @@ void LRUCacheShard::Erase(const Slice& key, uint32_t hash) {
|
||||
}
|
||||
}
|
||||
|
||||
// Free the entry here outside of mutex for performance reasons
|
||||
// last_reference will only be true if e != nullptr
|
||||
// Free the entry here outside of mutex for performance reasons.
|
||||
// last_reference will only be true if e != nullptr.
|
||||
if (last_reference) {
|
||||
e->Free();
|
||||
}
|
||||
@@ -705,7 +706,7 @@ uint32_t LRUCache::GetHash(Handle* handle) const {
|
||||
}
|
||||
|
||||
void LRUCache::DisownData() {
|
||||
// Leak data only if that won't generate an ASAN/valgrind warning
|
||||
// Leak data only if that won't generate an ASAN/valgrind warning.
|
||||
if (!kMustFreeHeapAllocations) {
|
||||
shards_ = nullptr;
|
||||
num_shards_ = 0;
|
||||
@@ -765,10 +766,10 @@ std::shared_ptr<Cache> NewLRUCache(
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
const std::shared_ptr<SecondaryCache>& secondary_cache) {
|
||||
if (num_shard_bits >= 20) {
|
||||
return nullptr; // the cache cannot be sharded into too many fine pieces
|
||||
return nullptr; // The cache cannot be sharded into too many fine pieces.
|
||||
}
|
||||
if (high_pri_pool_ratio < 0.0 || high_pri_pool_ratio > 1.0) {
|
||||
// invalid high_pri_pool_ratio
|
||||
// Invalid high_pri_pool_ratio
|
||||
return nullptr;
|
||||
}
|
||||
if (num_shard_bits < 0) {
|
||||
|
||||
Vendored
+30
-29
@@ -81,12 +81,12 @@ struct LRUHandle {
|
||||
IN_HIGH_PRI_POOL = (1 << 2),
|
||||
// Whether this entry has had any lookups (hits).
|
||||
HAS_HIT = (1 << 3),
|
||||
// Can this be inserted into the secondary cache
|
||||
// Can this be inserted into the secondary cache.
|
||||
IS_SECONDARY_CACHE_COMPATIBLE = (1 << 4),
|
||||
// Is the handle still being read from a lower tier
|
||||
// Is the handle still being read from a lower tier.
|
||||
IS_PENDING = (1 << 5),
|
||||
// Has the item been promoted from a lower tier
|
||||
IS_PROMOTED = (1 << 6),
|
||||
// Whether this handle is still in a lower tier
|
||||
IS_IN_SECONDARY_CACHE = (1 << 6),
|
||||
};
|
||||
|
||||
uint8_t flags;
|
||||
@@ -129,7 +129,7 @@ struct LRUHandle {
|
||||
#endif // __SANITIZE_THREAD__
|
||||
}
|
||||
bool IsPending() const { return flags & IS_PENDING; }
|
||||
bool IsPromoted() const { return flags & IS_PROMOTED; }
|
||||
bool IsInSecondaryCache() const { return flags & IS_IN_SECONDARY_CACHE; }
|
||||
|
||||
void SetInCache(bool in_cache) {
|
||||
if (in_cache) {
|
||||
@@ -176,11 +176,11 @@ struct LRUHandle {
|
||||
}
|
||||
}
|
||||
|
||||
void SetPromoted(bool promoted) {
|
||||
if (promoted) {
|
||||
flags |= IS_PROMOTED;
|
||||
void SetIsInSecondaryCache(bool is_in_secondary_cache) {
|
||||
if (is_in_secondary_cache) {
|
||||
flags |= IS_IN_SECONDARY_CACHE;
|
||||
} else {
|
||||
flags &= ~IS_PROMOTED;
|
||||
flags &= ~IS_IN_SECONDARY_CACHE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ struct LRUHandle {
|
||||
delete[] reinterpret_cast<char*>(this);
|
||||
}
|
||||
|
||||
// Calculate the memory usage by metadata
|
||||
// Calculate the memory usage by metadata.
|
||||
inline size_t CalcTotalCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
size_t meta_charge = 0;
|
||||
@@ -216,7 +216,7 @@ struct LRUHandle {
|
||||
#ifdef ROCKSDB_MALLOC_USABLE_SIZE
|
||||
meta_charge += malloc_usable_size(static_cast<void*>(this));
|
||||
#else
|
||||
// This is the size that is used when a new handle is created
|
||||
// This is the size that is used when a new handle is created.
|
||||
meta_charge += sizeof(LRUHandle) - 1 + key_length;
|
||||
#endif
|
||||
}
|
||||
@@ -272,10 +272,10 @@ class LRUHandleTable {
|
||||
// a linked list of cache entries that hash into the bucket.
|
||||
std::unique_ptr<LRUHandle*[]> list_;
|
||||
|
||||
// Number of elements currently in the table
|
||||
// Number of elements currently in the table.
|
||||
uint32_t elems_;
|
||||
|
||||
// Set from max_upper_hash_bits (see constructor)
|
||||
// Set from max_upper_hash_bits (see constructor).
|
||||
const int max_length_bits_;
|
||||
};
|
||||
|
||||
@@ -291,7 +291,7 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
|
||||
// Separate from constructor so caller can easily make an array of LRUCache
|
||||
// if current usage is more than new capacity, the function will attempt to
|
||||
// free the needed space
|
||||
// free the needed space.
|
||||
virtual void SetCapacity(size_t capacity) override;
|
||||
|
||||
// Set the flag to reject insertion if cache if full.
|
||||
@@ -314,8 +314,7 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
assert(helper);
|
||||
return Insert(key, hash, value, charge, nullptr, helper, handle, priority);
|
||||
}
|
||||
// If helper_cb is null, the values of the following arguments don't
|
||||
// matter
|
||||
// If helper_cb is null, the values of the following arguments don't matter.
|
||||
virtual Cache::Handle* Lookup(const Slice& key, uint32_t hash,
|
||||
const ShardedCache::CacheItemHelper* helper,
|
||||
const ShardedCache::CreateCallback& create_cb,
|
||||
@@ -326,14 +325,14 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
nullptr);
|
||||
}
|
||||
virtual bool Release(Cache::Handle* handle, bool /*useful*/,
|
||||
bool force_erase) override {
|
||||
return Release(handle, force_erase);
|
||||
bool erase_if_last_ref) override {
|
||||
return Release(handle, erase_if_last_ref);
|
||||
}
|
||||
virtual bool IsReady(Cache::Handle* /*handle*/) override;
|
||||
virtual void Wait(Cache::Handle* /*handle*/) override {}
|
||||
virtual bool Ref(Cache::Handle* handle) override;
|
||||
virtual bool Release(Cache::Handle* handle,
|
||||
bool force_erase = false) override;
|
||||
bool erase_if_last_ref = false) override;
|
||||
virtual void Erase(const Slice& key, uint32_t hash) override;
|
||||
|
||||
// Although in some platforms the update of size_t is atomic, to make sure
|
||||
@@ -354,8 +353,8 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
|
||||
void TEST_GetLRUList(LRUHandle** lru, LRUHandle** lru_low_pri);
|
||||
|
||||
// Retrieves number of elements in LRU, for unit test purpose only
|
||||
// not threadsafe
|
||||
// Retrieves number of elements in LRU, for unit test purpose only.
|
||||
// Not threadsafe.
|
||||
size_t TEST_GetLRUSize();
|
||||
|
||||
// Retrieves high pri pool ratio
|
||||
@@ -365,14 +364,16 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
friend class LRUCache;
|
||||
// Insert an item into the hash table and, if handle is null, insert into
|
||||
// the LRU list. Older items are evicted as necessary. If the cache is full
|
||||
// and free_handle_on_fail is true, the item is deleted and handle is set to.
|
||||
// and free_handle_on_fail is true, the item is deleted and handle is set to
|
||||
// nullptr.
|
||||
Status InsertItem(LRUHandle* item, Cache::Handle** handle,
|
||||
bool free_handle_on_fail);
|
||||
Status Insert(const Slice& key, uint32_t hash, void* value, size_t charge,
|
||||
DeleterFn deleter, const Cache::CacheItemHelper* helper,
|
||||
Cache::Handle** handle, Cache::Priority priority);
|
||||
// Promote an item looked up from the secondary cache to the LRU cache. The
|
||||
// item is only inserted into the hash table and not the LRU list, and only
|
||||
// Promote an item looked up from the secondary cache to the LRU cache.
|
||||
// The item may be still in the secondary cache.
|
||||
// It is only inserted into the hash table and not the LRU list, and only
|
||||
// if the cache is not at full capacity, as is the case during Insert. The
|
||||
// caller should hold a reference on the LRUHandle. When the caller releases
|
||||
// the last reference, the item is added to the LRU list.
|
||||
@@ -389,7 +390,7 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
// Free some space following strict LRU policy until enough space
|
||||
// to hold (usage_ + charge) is freed or the lru list is empty
|
||||
// This function is not thread safe - it needs to be executed while
|
||||
// holding the mutex_
|
||||
// holding the mutex_.
|
||||
void EvictFromLRU(size_t charge, autovector<LRUHandle*>* deleted);
|
||||
|
||||
// Initialized before use.
|
||||
@@ -429,10 +430,10 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
// ------------vvvvvvvvvvvvv-----------
|
||||
LRUHandleTable table_;
|
||||
|
||||
// Memory size for entries residing in the cache
|
||||
// Memory size for entries residing in the cache.
|
||||
size_t usage_;
|
||||
|
||||
// Memory size for entries residing only in the LRU list
|
||||
// Memory size for entries residing only in the LRU list.
|
||||
size_t lru_usage_;
|
||||
|
||||
// mutex_ protects the following state.
|
||||
@@ -467,9 +468,9 @@ class LRUCache
|
||||
virtual void DisownData() override;
|
||||
virtual void WaitAll(std::vector<Handle*>& handles) override;
|
||||
|
||||
// Retrieves number of elements in LRU, for unit test purpose only
|
||||
// Retrieves number of elements in LRU, for unit test purpose only.
|
||||
size_t TEST_GetLRUSize();
|
||||
// Retrieves high pri pool ratio
|
||||
// Retrieves high pri pool ratio.
|
||||
double GetHighPriPoolRatio();
|
||||
|
||||
private:
|
||||
|
||||
Vendored
+52
-48
@@ -266,12 +266,13 @@ class TestSecondaryCache : public SecondaryCache {
|
||||
}
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CreateCallback& create_cb,
|
||||
bool /*wait*/) override {
|
||||
const Slice& key, const Cache::CreateCallback& create_cb, bool /*wait*/,
|
||||
bool& is_in_sec_cache) override {
|
||||
std::string key_str = key.ToString();
|
||||
TEST_SYNC_POINT_CALLBACK("TestSecondaryCache::Lookup", &key_str);
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle;
|
||||
is_in_sec_cache = false;
|
||||
ResultType type = ResultType::SUCCESS;
|
||||
auto iter = result_map_.find(key.ToString());
|
||||
if (iter != result_map_.end()) {
|
||||
@@ -296,6 +297,7 @@ class TestSecondaryCache : public SecondaryCache {
|
||||
if (s.ok()) {
|
||||
secondary_handle.reset(new TestSecondaryCacheResultHandle(
|
||||
cache_.get(), handle, value, charge, type));
|
||||
is_in_sec_cache = true;
|
||||
} else {
|
||||
cache_->Release(handle);
|
||||
}
|
||||
@@ -383,10 +385,10 @@ class DBSecondaryCacheTest : public DBTestBase {
|
||||
std::unique_ptr<Env> fault_env_;
|
||||
};
|
||||
|
||||
class LRUSecondaryCacheTest : public LRUCacheTest {
|
||||
class LRUCacheSecondaryCacheTest : public LRUCacheTest {
|
||||
public:
|
||||
LRUSecondaryCacheTest() : fail_create_(false) {}
|
||||
~LRUSecondaryCacheTest() {}
|
||||
LRUCacheSecondaryCacheTest() : fail_create_(false) {}
|
||||
~LRUCacheSecondaryCacheTest() {}
|
||||
|
||||
protected:
|
||||
class TestItem {
|
||||
@@ -449,16 +451,17 @@ class LRUSecondaryCacheTest : public LRUCacheTest {
|
||||
bool fail_create_;
|
||||
};
|
||||
|
||||
Cache::CacheItemHelper LRUSecondaryCacheTest::helper_(
|
||||
LRUSecondaryCacheTest::SizeCallback, LRUSecondaryCacheTest::SaveToCallback,
|
||||
LRUSecondaryCacheTest::DeletionCallback);
|
||||
Cache::CacheItemHelper LRUCacheSecondaryCacheTest::helper_(
|
||||
LRUCacheSecondaryCacheTest::SizeCallback,
|
||||
LRUCacheSecondaryCacheTest::SaveToCallback,
|
||||
LRUCacheSecondaryCacheTest::DeletionCallback);
|
||||
|
||||
Cache::CacheItemHelper LRUSecondaryCacheTest::helper_fail_(
|
||||
LRUSecondaryCacheTest::SizeCallback,
|
||||
LRUSecondaryCacheTest::SaveToCallbackFail,
|
||||
LRUSecondaryCacheTest::DeletionCallback);
|
||||
Cache::CacheItemHelper LRUCacheSecondaryCacheTest::helper_fail_(
|
||||
LRUCacheSecondaryCacheTest::SizeCallback,
|
||||
LRUCacheSecondaryCacheTest::SaveToCallbackFail,
|
||||
LRUCacheSecondaryCacheTest::DeletionCallback);
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicTest) {
|
||||
TEST_F(LRUCacheSecondaryCacheTest, BasicTest) {
|
||||
LRUCacheOptions opts(1024, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
@@ -470,25 +473,25 @@ TEST_F(LRUSecondaryCacheTest, BasicTest) {
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUCacheSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k1 should be demoted to NVM
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUCacheSecondaryCacheTest::helper_,
|
||||
str2.length()));
|
||||
|
||||
get_perf_context()->Reset();
|
||||
Cache::Handle* handle;
|
||||
handle =
|
||||
cache->Lookup("k2", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
cache->Lookup("k2", &LRUCacheSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
// This lookup should promote k1 and demote k2
|
||||
handle =
|
||||
cache->Lookup("k1", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
cache->Lookup("k1", &LRUCacheSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
ASSERT_EQ(secondary_cache->num_inserts(), 2u);
|
||||
@@ -502,7 +505,7 @@ TEST_F(LRUSecondaryCacheTest, BasicTest) {
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicFailTest) {
|
||||
TEST_F(LRUCacheSecondaryCacheTest, BasicFailTest) {
|
||||
LRUCacheOptions opts(1024, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
@@ -515,15 +518,15 @@ TEST_F(LRUSecondaryCacheTest, BasicFailTest) {
|
||||
auto item1 = std::make_unique<TestItem>(str1.data(), str1.length());
|
||||
ASSERT_TRUE(cache->Insert("k1", item1.get(), nullptr, str1.length())
|
||||
.IsInvalidArgument());
|
||||
ASSERT_OK(cache->Insert("k1", item1.get(), &LRUSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
ASSERT_OK(cache->Insert("k1", item1.get(),
|
||||
&LRUCacheSecondaryCacheTest::helper_, str1.length()));
|
||||
item1.release(); // Appease clang-analyze "potential memory leak"
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup("k2", nullptr, test_item_creator, Cache::Priority::LOW,
|
||||
true);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
handle = cache->Lookup("k2", &LRUCacheSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, false);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
|
||||
@@ -531,7 +534,7 @@ TEST_F(LRUSecondaryCacheTest, BasicFailTest) {
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, SaveFailTest) {
|
||||
TEST_F(LRUCacheSecondaryCacheTest, SaveFailTest) {
|
||||
LRUCacheOptions opts(1024, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
@@ -542,25 +545,25 @@ TEST_F(LRUSecondaryCacheTest, SaveFailTest) {
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_fail_,
|
||||
str1.length()));
|
||||
ASSERT_OK(cache->Insert(
|
||||
"k1", item1, &LRUCacheSecondaryCacheTest::helper_fail_, str1.length()));
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k1 should be demoted to NVM
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_fail_,
|
||||
str2.length()));
|
||||
ASSERT_OK(cache->Insert(
|
||||
"k2", item2, &LRUCacheSecondaryCacheTest::helper_fail_, str2.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_fail_,
|
||||
handle = cache->Lookup("k2", &LRUCacheSecondaryCacheTest::helper_fail_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
// This lookup should fail, since k1 demotion would have failed
|
||||
handle = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_fail_,
|
||||
handle = cache->Lookup("k1", &LRUCacheSecondaryCacheTest::helper_fail_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
// Since k1 didn't get promoted, k2 should still be in cache
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_fail_,
|
||||
handle = cache->Lookup("k2", &LRUCacheSecondaryCacheTest::helper_fail_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
@@ -571,7 +574,7 @@ TEST_F(LRUSecondaryCacheTest, SaveFailTest) {
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, CreateFailTest) {
|
||||
TEST_F(LRUCacheSecondaryCacheTest, CreateFailTest) {
|
||||
LRUCacheOptions opts(1024, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
@@ -582,26 +585,26 @@ TEST_F(LRUSecondaryCacheTest, CreateFailTest) {
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUCacheSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k1 should be demoted to NVM
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUCacheSecondaryCacheTest::helper_,
|
||||
str2.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
SetFailCreate(true);
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
handle = cache->Lookup("k2", &LRUCacheSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
// This lookup should fail, since k1 creation would have failed
|
||||
handle = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_,
|
||||
handle = cache->Lookup("k1", &LRUCacheSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
// Since k1 didn't get promoted, k2 should still be in cache
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
handle = cache->Lookup("k2", &LRUCacheSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
@@ -612,7 +615,7 @@ TEST_F(LRUSecondaryCacheTest, CreateFailTest) {
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, FullCapacityTest) {
|
||||
TEST_F(LRUCacheSecondaryCacheTest, FullCapacityTest) {
|
||||
LRUCacheOptions opts(1024, 0, /*_strict_capacity_limit=*/true, 0.5, nullptr,
|
||||
kDefaultToAdaptiveMutex, kDontChargeCacheMetadata);
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
@@ -623,28 +626,28 @@ TEST_F(LRUSecondaryCacheTest, FullCapacityTest) {
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUCacheSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k1 should be demoted to NVM
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_,
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUCacheSecondaryCacheTest::helper_,
|
||||
str2.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
handle = cache->Lookup("k2", &LRUCacheSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
// k1 promotion should fail due to the block cache being at capacity,
|
||||
// but the lookup should still succeed
|
||||
Cache::Handle* handle2;
|
||||
handle2 = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_,
|
||||
handle2 = cache->Lookup("k1", &LRUCacheSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
// Since k1 didn't get inserted, k2 should still be in cache
|
||||
cache->Release(handle);
|
||||
cache->Release(handle2);
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
handle = cache->Lookup("k2", &LRUCacheSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
@@ -1046,7 +1049,7 @@ TEST_F(DBSecondaryCacheTest, SecondaryCacheFailureTest) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicWaitAllTest) {
|
||||
TEST_F(LRUCacheSecondaryCacheTest, BasicWaitAllTest) {
|
||||
LRUCacheOptions opts(1024, 2, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
@@ -1062,7 +1065,8 @@ TEST_F(LRUSecondaryCacheTest, BasicWaitAllTest) {
|
||||
values.emplace_back(str);
|
||||
TestItem* item = new TestItem(str.data(), str.length());
|
||||
ASSERT_OK(cache->Insert("k" + std::to_string(i), item,
|
||||
&LRUSecondaryCacheTest::helper_, str.length()));
|
||||
&LRUCacheSecondaryCacheTest::helper_,
|
||||
str.length()));
|
||||
}
|
||||
// Force all entries to be evicted to the secondary cache
|
||||
cache->SetCapacity(0);
|
||||
@@ -1075,9 +1079,9 @@ TEST_F(LRUSecondaryCacheTest, BasicWaitAllTest) {
|
||||
{"k5", TestSecondaryCache::ResultType::FAIL}});
|
||||
std::vector<Cache::Handle*> results;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
results.emplace_back(
|
||||
cache->Lookup("k" + std::to_string(i), &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, false));
|
||||
results.emplace_back(cache->Lookup(
|
||||
"k" + std::to_string(i), &LRUCacheSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, false));
|
||||
}
|
||||
cache->WaitAll(results);
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
|
||||
Vendored
+5
-4
@@ -104,14 +104,15 @@ bool ShardedCache::Ref(Handle* handle) {
|
||||
return GetShard(Shard(hash))->Ref(handle);
|
||||
}
|
||||
|
||||
bool ShardedCache::Release(Handle* handle, bool force_erase) {
|
||||
bool ShardedCache::Release(Handle* handle, bool erase_if_last_ref) {
|
||||
uint32_t hash = GetHash(handle);
|
||||
return GetShard(Shard(hash))->Release(handle, force_erase);
|
||||
return GetShard(Shard(hash))->Release(handle, erase_if_last_ref);
|
||||
}
|
||||
|
||||
bool ShardedCache::Release(Handle* handle, bool useful, bool force_erase) {
|
||||
bool ShardedCache::Release(Handle* handle, bool useful,
|
||||
bool erase_if_last_ref) {
|
||||
uint32_t hash = GetHash(handle);
|
||||
return GetShard(Shard(hash))->Release(handle, useful, force_erase);
|
||||
return GetShard(Shard(hash))->Release(handle, useful, erase_if_last_ref);
|
||||
}
|
||||
|
||||
void ShardedCache::Erase(const Slice& key) {
|
||||
|
||||
Vendored
+4
-4
@@ -37,11 +37,11 @@ class CacheShard {
|
||||
Cache::Priority priority, bool wait,
|
||||
Statistics* stats) = 0;
|
||||
virtual bool Release(Cache::Handle* handle, bool useful,
|
||||
bool force_erase) = 0;
|
||||
bool erase_if_last_ref) = 0;
|
||||
virtual bool IsReady(Cache::Handle* handle) = 0;
|
||||
virtual void Wait(Cache::Handle* handle) = 0;
|
||||
virtual bool Ref(Cache::Handle* handle) = 0;
|
||||
virtual bool Release(Cache::Handle* handle, bool force_erase) = 0;
|
||||
virtual bool Release(Cache::Handle* handle, bool erase_if_last_ref) = 0;
|
||||
virtual void Erase(const Slice& key, uint32_t hash) = 0;
|
||||
virtual void SetCapacity(size_t capacity) = 0;
|
||||
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) = 0;
|
||||
@@ -94,11 +94,11 @@ class ShardedCache : public Cache {
|
||||
const CreateCallback& create_cb, Priority priority,
|
||||
bool wait, Statistics* stats = nullptr) override;
|
||||
virtual bool Release(Handle* handle, bool useful,
|
||||
bool force_erase = false) override;
|
||||
bool erase_if_last_ref = false) override;
|
||||
virtual bool IsReady(Handle* handle) override;
|
||||
virtual void Wait(Handle* handle) override;
|
||||
virtual bool Ref(Handle* handle) override;
|
||||
virtual bool Release(Handle* handle, bool force_erase = false) override;
|
||||
virtual bool Release(Handle* handle, bool erase_if_last_ref = false) override;
|
||||
virtual void Erase(const Slice& key) override;
|
||||
virtual uint64_t NewId() override;
|
||||
virtual size_t GetCapacity() const override;
|
||||
|
||||
@@ -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_platform007.sh
|
||||
source $ROOT/build_tools/fbcode_config_platform009.sh
|
||||
GCOV=$GCC_BASE/bin/gcov
|
||||
else
|
||||
GCOV=$(which gcov)
|
||||
|
||||
@@ -42,6 +42,12 @@ crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_ts
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_ts
|
||||
|
||||
crash_test_with_multiops_wc_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_multiops_wc_txn
|
||||
|
||||
crash_test_with_multiops_wp_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_multiops_wp_txn
|
||||
|
||||
blackbox_crash_test: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(CRASHTEST_PY) blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
@@ -366,19 +366,26 @@ TEST_F(DBBlobBasicTest, GetBlob_CorruptIndex) {
|
||||
Reopen(options);
|
||||
|
||||
constexpr char key[] = "key";
|
||||
constexpr char blob[] = "blob";
|
||||
|
||||
// Fake a corrupt blob index.
|
||||
const std::string blob_index("foobar");
|
||||
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, 0, key, blob_index));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
|
||||
ASSERT_OK(Put(key, blob));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"Version::Get::TamperWithBlobIndex", [](void* arg) {
|
||||
Slice* const blob_index = static_cast<Slice*>(arg);
|
||||
assert(blob_index);
|
||||
assert(!blob_index->empty());
|
||||
blob_index->remove_prefix(1);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
PinnableSlice result;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), db_->DefaultColumnFamily(), key, &result)
|
||||
.IsCorruption());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, MultiGetBlob_CorruptIndex) {
|
||||
@@ -401,17 +408,27 @@ TEST_F(DBBlobBasicTest, MultiGetBlob_CorruptIndex) {
|
||||
}
|
||||
|
||||
constexpr char key[] = "key";
|
||||
{
|
||||
// Fake a corrupt blob index.
|
||||
const std::string blob_index("foobar");
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, 0, key, blob_index));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
keys[kNumOfKeys] = Slice(static_cast<const char*>(key), sizeof(key) - 1);
|
||||
}
|
||||
constexpr char blob[] = "blob";
|
||||
ASSERT_OK(Put(key, blob));
|
||||
keys[kNumOfKeys] = key;
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"Version::MultiGet::TamperWithBlobIndex", [&key](void* arg) {
|
||||
KeyContext* const key_context = static_cast<KeyContext*>(arg);
|
||||
assert(key_context);
|
||||
assert(key_context->key);
|
||||
|
||||
if (*(key_context->key) == key) {
|
||||
Slice* const blob_index = key_context->value;
|
||||
assert(blob_index);
|
||||
assert(!blob_index->empty());
|
||||
blob_index->remove_prefix(1);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::array<PinnableSlice, kNumOfKeys + 1> values;
|
||||
std::array<Status, kNumOfKeys + 1> statuses;
|
||||
db_->MultiGet(ReadOptions(), dbfull()->DefaultColumnFamily(), kNumOfKeys + 1,
|
||||
@@ -425,6 +442,9 @@ TEST_F(DBBlobBasicTest, MultiGetBlob_CorruptIndex) {
|
||||
ASSERT_TRUE(statuses[i].IsCorruption());
|
||||
}
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, MultiGetBlob_ExceedSoftLimit) {
|
||||
@@ -733,6 +753,14 @@ TEST_F(DBBlobBasicTest, Properties) {
|
||||
&live_blob_file_size));
|
||||
ASSERT_EQ(live_blob_file_size, total_expected_size);
|
||||
|
||||
// Total amount of garbage in live blob files
|
||||
{
|
||||
uint64_t live_blob_file_garbage_size = 0;
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kLiveBlobFileGarbageSize,
|
||||
&live_blob_file_garbage_size));
|
||||
ASSERT_EQ(live_blob_file_garbage_size, 0);
|
||||
}
|
||||
|
||||
// Total size of all blob files across all versions
|
||||
// Note: this should be the same as above since we only have one
|
||||
// version at this point.
|
||||
@@ -768,6 +796,14 @@ TEST_F(DBBlobBasicTest, Properties) {
|
||||
<< "\nBlob file space amplification: " << expected_space_amp << '\n';
|
||||
|
||||
ASSERT_EQ(blob_stats, oss.str());
|
||||
|
||||
// Total amount of garbage in live blob files
|
||||
{
|
||||
uint64_t live_blob_file_garbage_size = 0;
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kLiveBlobFileGarbageSize,
|
||||
&live_blob_file_garbage_size));
|
||||
ASSERT_EQ(live_blob_file_garbage_size, expected_garbage_size);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, PropertiesMultiVersion) {
|
||||
|
||||
@@ -415,16 +415,30 @@ TEST_F(DBBlobCompactionTest, CorruptedBlobIndex) {
|
||||
new ValueMutationFilter(""));
|
||||
options.compaction_filter = compaction_filter_guard.get();
|
||||
DestroyAndReopen(options);
|
||||
// Mock a corrupted blob index
|
||||
|
||||
constexpr char key[] = "key";
|
||||
std::string blob_idx("blob_idx");
|
||||
WriteBatch write_batch;
|
||||
ASSERT_OK(WriteBatchInternal::PutBlobIndex(&write_batch, 0, key, blob_idx));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &write_batch));
|
||||
constexpr char blob[] = "blob";
|
||||
|
||||
ASSERT_OK(Put(key, blob));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionIterator::InvokeFilterIfNeeded::TamperWithBlobIndex",
|
||||
[](void* arg) {
|
||||
Slice* const blob_index = static_cast<Slice*>(arg);
|
||||
assert(blob_index);
|
||||
assert(!blob_index->empty());
|
||||
blob_index->remove_prefix(1);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr)
|
||||
.IsCorruption());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/arena_wrapped_db_iter.h"
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/db_test_util.h"
|
||||
@@ -138,20 +139,39 @@ class DBBlobIndexTest : public DBTestBase {
|
||||
}
|
||||
};
|
||||
|
||||
// Should be able to write kTypeBlobIndex to memtables and SST files.
|
||||
// Note: the following test case pertains to the StackableDB-based BlobDB
|
||||
// implementation. We should be able to write kTypeBlobIndex to memtables and
|
||||
// SST files.
|
||||
TEST_F(DBBlobIndexTest, Write) {
|
||||
for (auto tier : kAllTiers) {
|
||||
DestroyAndReopen(GetTestOptions());
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
std::string index = ToString(i);
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> key_values;
|
||||
|
||||
constexpr size_t num_key_values = 5;
|
||||
|
||||
key_values.reserve(num_key_values);
|
||||
|
||||
for (size_t i = 1; i <= num_key_values; ++i) {
|
||||
std::string key = "key" + ToString(i);
|
||||
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeInlinedTTL(&blob_index, /* expiration */ 9876543210,
|
||||
"blob" + ToString(i));
|
||||
|
||||
key_values.emplace_back(std::move(key), std::move(blob_index));
|
||||
}
|
||||
|
||||
for (const auto& key_value : key_values) {
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(PutBlobIndex(&batch, "key" + index, "blob" + index));
|
||||
ASSERT_OK(PutBlobIndex(&batch, key_value.first, key_value.second));
|
||||
ASSERT_OK(Write(&batch));
|
||||
}
|
||||
|
||||
MoveDataTo(tier);
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
std::string index = ToString(i);
|
||||
ASSERT_EQ("blob" + index, GetBlobIndex("key" + index));
|
||||
|
||||
for (const auto& key_value : key_values) {
|
||||
ASSERT_EQ(GetBlobIndex(key_value.first), key_value.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,13 +184,19 @@ TEST_F(DBBlobIndexTest, Write) {
|
||||
// accidentally opening the base DB of a stacked BlobDB and actual corruption
|
||||
// when using the integrated BlobDB.
|
||||
TEST_F(DBBlobIndexTest, Get) {
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeInlinedTTL(&blob_index, /* expiration */ 9876543210, "blob");
|
||||
|
||||
for (auto tier : kAllTiers) {
|
||||
DestroyAndReopen(GetTestOptions());
|
||||
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(batch.Put("key", "value"));
|
||||
ASSERT_OK(PutBlobIndex(&batch, "blob_key", "blob_index"));
|
||||
ASSERT_OK(PutBlobIndex(&batch, "blob_key", blob_index));
|
||||
ASSERT_OK(Write(&batch));
|
||||
|
||||
MoveDataTo(tier);
|
||||
|
||||
// Verify normal value
|
||||
bool is_blob_index = false;
|
||||
PinnableSlice value;
|
||||
@@ -178,6 +204,7 @@ TEST_F(DBBlobIndexTest, Get) {
|
||||
ASSERT_EQ("value", GetImpl("key"));
|
||||
ASSERT_EQ("value", GetImpl("key", &is_blob_index));
|
||||
ASSERT_FALSE(is_blob_index);
|
||||
|
||||
// Verify blob index
|
||||
if (tier <= kImmutableMemtables) {
|
||||
ASSERT_TRUE(Get("blob_key", &value).IsNotSupported());
|
||||
@@ -186,7 +213,7 @@ TEST_F(DBBlobIndexTest, Get) {
|
||||
ASSERT_TRUE(Get("blob_key", &value).IsCorruption());
|
||||
ASSERT_EQ("CORRUPTION", GetImpl("blob_key"));
|
||||
}
|
||||
ASSERT_EQ("blob_index", GetImpl("blob_key", &is_blob_index));
|
||||
ASSERT_EQ(blob_index, GetImpl("blob_key", &is_blob_index));
|
||||
ASSERT_TRUE(is_blob_index);
|
||||
}
|
||||
}
|
||||
@@ -196,11 +223,14 @@ TEST_F(DBBlobIndexTest, Get) {
|
||||
// if blob index is updated with a normal value. See the test case above for
|
||||
// more details.
|
||||
TEST_F(DBBlobIndexTest, Updated) {
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeInlinedTTL(&blob_index, /* expiration */ 9876543210, "blob");
|
||||
|
||||
for (auto tier : kAllTiers) {
|
||||
DestroyAndReopen(GetTestOptions());
|
||||
WriteBatch batch;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(PutBlobIndex(&batch, "key" + ToString(i), "blob_index"));
|
||||
ASSERT_OK(PutBlobIndex(&batch, "key" + ToString(i), blob_index));
|
||||
}
|
||||
ASSERT_OK(Write(&batch));
|
||||
// Avoid blob values from being purged.
|
||||
@@ -218,7 +248,7 @@ TEST_F(DBBlobIndexTest, Updated) {
|
||||
ASSERT_OK(dbfull()->DeleteRange(WriteOptions(), cfh(), "key6", "key9"));
|
||||
MoveDataTo(tier);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_EQ("blob_index", GetBlobIndex("key" + ToString(i), snapshot));
|
||||
ASSERT_EQ(blob_index, GetBlobIndex("key" + ToString(i), snapshot));
|
||||
}
|
||||
ASSERT_EQ("new_value", Get("key1"));
|
||||
if (tier <= kImmutableMemtables) {
|
||||
@@ -232,7 +262,7 @@ TEST_F(DBBlobIndexTest, Updated) {
|
||||
for (int i = 6; i < 9; i++) {
|
||||
ASSERT_EQ("NOT_FOUND", Get("key" + ToString(i)));
|
||||
}
|
||||
ASSERT_EQ("blob_index", GetBlobIndex("key9"));
|
||||
ASSERT_EQ(blob_index, GetBlobIndex("key9"));
|
||||
dbfull()->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-6
@@ -62,9 +62,9 @@ Status BuildTable(
|
||||
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
|
||||
std::vector<SequenceNumber> snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, bool paranoid_file_checks,
|
||||
InternalStats* internal_stats, IOStatus* io_status,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
SequenceNumber job_snapshot, SnapshotChecker* snapshot_checker,
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
IOStatus* io_status, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCreationReason blob_creation_reason, EventLogger* event_logger,
|
||||
int job_id, const Env::IOPriority io_priority,
|
||||
TableProperties* table_properties, Env::WriteLifeTimeHint write_hint,
|
||||
@@ -189,12 +189,13 @@ Status BuildTable(
|
||||
CompactionIterator c_iter(
|
||||
iter, tboptions.internal_comparator.user_comparator(), &merge,
|
||||
kMaxSequenceNumber, &snapshots, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, env, ShouldReportDetailedTime(env, ioptions.stats),
|
||||
job_snapshot, snapshot_checker, env,
|
||||
ShouldReportDetailedTime(env, ioptions.stats),
|
||||
true /* internal key corruption is not ok */, range_del_agg.get(),
|
||||
blob_file_builder.get(), ioptions.allow_data_in_errors,
|
||||
/*compaction=*/nullptr, compaction_filter.get(),
|
||||
/*shutting_down=*/nullptr,
|
||||
/*preserve_deletes_seqnum=*/0, /*manual_compaction_paused=*/nullptr,
|
||||
/*manual_compaction_paused=*/nullptr,
|
||||
/*manual_compaction_canceled=*/nullptr, db_options.info_log,
|
||||
full_history_ts_low);
|
||||
|
||||
@@ -211,7 +212,11 @@ Status BuildTable(
|
||||
break;
|
||||
}
|
||||
builder->Add(key, value);
|
||||
meta->UpdateBoundaries(key, value, ikey.sequence, ikey.type);
|
||||
|
||||
s = meta->UpdateBoundaries(key, value, ikey.sequence, ikey.type);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO(noetzli): Update stats after flush, too.
|
||||
if (io_priority == Env::IO_HIGH &&
|
||||
|
||||
+3
-3
@@ -57,9 +57,9 @@ extern Status BuildTable(
|
||||
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
|
||||
std::vector<SequenceNumber> snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, bool paranoid_file_checks,
|
||||
InternalStats* internal_stats, IOStatus* io_status,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
SequenceNumber job_snapshot, SnapshotChecker* snapshot_checker,
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
IOStatus* io_status, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCreationReason blob_creation_reason,
|
||||
EventLogger* event_logger = nullptr, int job_id = 0,
|
||||
const Env::IOPriority io_priority = Env::IO_HIGH,
|
||||
|
||||
+1
-1
@@ -2448,7 +2448,7 @@ int main(int argc, char** argv) {
|
||||
rocksdb_fifo_compaction_options_destroy(fco);
|
||||
}
|
||||
|
||||
StartPhase("backupable_db_option");
|
||||
StartPhase("backup_engine_option");
|
||||
{
|
||||
rocksdb_backup_engine_options_t* bdo;
|
||||
bdo = rocksdb_backup_engine_options_create("path");
|
||||
|
||||
@@ -1562,20 +1562,6 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
return new_cfd;
|
||||
}
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
void ColumnFamilySet::FreeDeadColumnFamilies() {
|
||||
autovector<ColumnFamilyData*> to_delete;
|
||||
for (auto cfd = dummy_cfd_->next_; cfd != dummy_cfd_; cfd = cfd->next_) {
|
||||
if (cfd->refs_.load(std::memory_order_relaxed) == 0) {
|
||||
to_delete.push_back(cfd);
|
||||
}
|
||||
}
|
||||
for (auto cfd : to_delete) {
|
||||
// this is very rare, so it's not a problem that we do it under a mutex
|
||||
delete cfd;
|
||||
}
|
||||
}
|
||||
|
||||
// under a DB mutex AND from a write thread
|
||||
void ColumnFamilySet::RemoveColumnFamily(ColumnFamilyData* cfd) {
|
||||
auto cfd_iter = column_family_data_.find(cfd->GetID());
|
||||
|
||||
+62
-22
@@ -9,10 +9,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/table_cache.h"
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "trace_replay/block_cache_tracer.h"
|
||||
#include "util/hash_containers.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -520,9 +521,10 @@ class ColumnFamilyData {
|
||||
ThreadLocalPtr* TEST_GetLocalSV() { return local_sv_.get(); }
|
||||
WriteBufferManager* write_buffer_mgr() { return write_buffer_manager_; }
|
||||
|
||||
static const uint32_t kDummyColumnFamilyDataId;
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
static const uint32_t kDummyColumnFamilyDataId;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
Version* dummy_versions, Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
@@ -628,10 +630,8 @@ class ColumnFamilyData {
|
||||
// held and it needs to be executed from the write thread. SetDropped() also
|
||||
// guarantees that it will be called only from single-threaded LogAndApply(),
|
||||
// but this condition is not that important.
|
||||
// * Iteration -- hold DB mutex, but you can release it in the body of
|
||||
// iteration. If you release DB mutex in body, reference the column
|
||||
// family before the mutex and unreference after you unlock, since the column
|
||||
// family might get dropped when the DB mutex is released
|
||||
// * Iteration -- hold DB mutex. If you want to release the DB mutex in the
|
||||
// body of the iteration, wrap in a RefedColumnFamilySet.
|
||||
// * GetDefault() -- thread safe
|
||||
// * GetColumnFamily() -- either inside of DB mutex or from a write thread
|
||||
// * GetNextColumnFamilyID(), GetMaxColumnFamily(), UpdateMaxColumnFamily(),
|
||||
@@ -643,17 +643,12 @@ class ColumnFamilySet {
|
||||
public:
|
||||
explicit iterator(ColumnFamilyData* cfd)
|
||||
: current_(cfd) {}
|
||||
// NOTE: minimum operators for for-loop iteration
|
||||
iterator& operator++() {
|
||||
// dropped column families might still be included in this iteration
|
||||
// (we're only removing them when client drops the last reference to the
|
||||
// column family).
|
||||
// dummy is never dead, so this will never be infinite
|
||||
do {
|
||||
current_ = current_->next_;
|
||||
} while (current_->refs_.load(std::memory_order_relaxed) == 0);
|
||||
current_ = current_->next_;
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(const iterator& other) {
|
||||
bool operator!=(const iterator& other) const {
|
||||
return this->current_ != other.current_;
|
||||
}
|
||||
ColumnFamilyData* operator*() { return current_; }
|
||||
@@ -692,10 +687,6 @@ class ColumnFamilySet {
|
||||
iterator begin() { return iterator(dummy_cfd_->next_); }
|
||||
iterator end() { return iterator(dummy_cfd_); }
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
// Don't call while iterating over ColumnFamilySet
|
||||
void FreeDeadColumnFamilies();
|
||||
|
||||
Cache* get_table_cache() { return table_cache_; }
|
||||
|
||||
WriteBufferManager* write_buffer_manager() { return write_buffer_manager_; }
|
||||
@@ -715,8 +706,8 @@ class ColumnFamilySet {
|
||||
// * when reading, at least one condition needs to be satisfied:
|
||||
// 1. DB mutex locked
|
||||
// 2. accessed from a single-threaded write thread
|
||||
std::unordered_map<std::string, uint32_t> column_families_;
|
||||
std::unordered_map<uint32_t, ColumnFamilyData*> column_family_data_;
|
||||
UnorderedMap<std::string, uint32_t> column_families_;
|
||||
UnorderedMap<uint32_t, ColumnFamilyData*> column_family_data_;
|
||||
|
||||
uint32_t max_column_family_;
|
||||
const FileOptions file_options_;
|
||||
@@ -738,6 +729,55 @@ class ColumnFamilySet {
|
||||
std::string db_session_id_;
|
||||
};
|
||||
|
||||
// A wrapper for ColumnFamilySet that supports releasing DB mutex during each
|
||||
// iteration over the iterator, because the cfd is Refed and Unrefed during
|
||||
// each iteration to prevent concurrent CF drop from destroying it (until
|
||||
// Unref).
|
||||
class RefedColumnFamilySet {
|
||||
public:
|
||||
explicit RefedColumnFamilySet(ColumnFamilySet* cfs) : wrapped_(cfs) {}
|
||||
|
||||
class iterator {
|
||||
public:
|
||||
explicit iterator(ColumnFamilySet::iterator wrapped) : wrapped_(wrapped) {
|
||||
MaybeRef(*wrapped_);
|
||||
}
|
||||
~iterator() { MaybeUnref(*wrapped_); }
|
||||
inline void MaybeRef(ColumnFamilyData* cfd) {
|
||||
if (cfd->GetID() != ColumnFamilyData::kDummyColumnFamilyDataId) {
|
||||
cfd->Ref();
|
||||
}
|
||||
}
|
||||
inline void MaybeUnref(ColumnFamilyData* cfd) {
|
||||
if (cfd->GetID() != ColumnFamilyData::kDummyColumnFamilyDataId) {
|
||||
cfd->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
// NOTE: minimum operators for for-loop iteration
|
||||
inline iterator& operator++() {
|
||||
ColumnFamilyData* old = *wrapped_;
|
||||
++wrapped_;
|
||||
// Can only unref & potentially free cfd after accessing its next_
|
||||
MaybeUnref(old);
|
||||
MaybeRef(*wrapped_);
|
||||
return *this;
|
||||
}
|
||||
inline bool operator!=(const iterator& other) const {
|
||||
return this->wrapped_ != other.wrapped_;
|
||||
}
|
||||
inline ColumnFamilyData* operator*() { return *wrapped_; }
|
||||
|
||||
private:
|
||||
ColumnFamilySet::iterator wrapped_;
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(wrapped_->begin()); }
|
||||
iterator end() { return iterator(wrapped_->end()); }
|
||||
|
||||
private:
|
||||
ColumnFamilySet* wrapped_;
|
||||
};
|
||||
|
||||
// We use ColumnFamilyMemTablesImpl to provide WriteBatch a way to access
|
||||
// memtables of different column families (specified by ID in the write batch)
|
||||
class ColumnFamilyMemTablesImpl : public ColumnFamilyMemTables {
|
||||
|
||||
@@ -24,40 +24,37 @@ CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
|
||||
const Compaction* compaction, const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::atomic<bool>* manual_compaction_canceled,
|
||||
const std::shared_ptr<Logger> info_log,
|
||||
const std::string* full_history_ts_low)
|
||||
: CompactionIterator(
|
||||
input, cmp, merge_helper, last_sequence, snapshots,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, env,
|
||||
earliest_write_conflict_snapshot, job_snapshot, snapshot_checker, env,
|
||||
report_detailed_time, expect_valid_internal_key, range_del_agg,
|
||||
blob_file_builder, allow_data_in_errors,
|
||||
std::unique_ptr<CompactionProxy>(
|
||||
compaction ? new RealCompaction(compaction) : nullptr),
|
||||
compaction_filter, shutting_down, preserve_deletes_seqnum,
|
||||
manual_compaction_paused, manual_compaction_canceled, info_log,
|
||||
full_history_ts_low) {}
|
||||
compaction_filter, shutting_down, manual_compaction_paused,
|
||||
manual_compaction_canceled, info_log, full_history_ts_low) {}
|
||||
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber /*last_sequence*/, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::atomic<bool>* manual_compaction_canceled,
|
||||
const std::shared_ptr<Logger> info_log,
|
||||
@@ -68,6 +65,7 @@ CompactionIterator::CompactionIterator(
|
||||
merge_helper_(merge_helper),
|
||||
snapshots_(snapshots),
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
job_snapshot_(job_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
env_(env),
|
||||
clock_(env_->GetSystemClock().get()),
|
||||
@@ -80,7 +78,6 @@ CompactionIterator::CompactionIterator(
|
||||
shutting_down_(shutting_down),
|
||||
manual_compaction_paused_(manual_compaction_paused),
|
||||
manual_compaction_canceled_(manual_compaction_canceled),
|
||||
preserve_deletes_seqnum_(preserve_deletes_seqnum),
|
||||
info_log_(info_log),
|
||||
allow_data_in_errors_(allow_data_in_errors),
|
||||
timestamp_size_(cmp_ ? cmp_->timestamp_size() : 0),
|
||||
@@ -237,6 +234,10 @@ bool CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionIterator::InvokeFilterIfNeeded::TamperWithBlobIndex",
|
||||
&value_);
|
||||
|
||||
// For integrated BlobDB impl, CompactionIterator reads blob value.
|
||||
// For Stacked BlobDB impl, the corresponding CompactionFilter's
|
||||
// FilterV2 method should read the blob value.
|
||||
@@ -758,7 +759,6 @@ void CompactionIterator::NextFromInput() {
|
||||
(ikey_.type == kTypeDeletionWithTimestamp &&
|
||||
cmp_with_history_ts_low_ < 0)) &&
|
||||
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
&level_ptrs_)) {
|
||||
// TODO(noetzli): This is the only place where we use compaction_
|
||||
@@ -792,7 +792,7 @@ void CompactionIterator::NextFromInput() {
|
||||
} else if ((ikey_.type == kTypeDeletion ||
|
||||
(ikey_.type == kTypeDeletionWithTimestamp &&
|
||||
cmp_with_history_ts_low_ < 0)) &&
|
||||
bottommost_level_ && ikeyNotNeededForIncrementalSnapshot()) {
|
||||
bottommost_level_) {
|
||||
// Handle the case where we have a delete key at the bottom most level
|
||||
// We can skip outputting the key iff there are no subsequent puts for this
|
||||
// key
|
||||
@@ -954,6 +954,10 @@ void CompactionIterator::GarbageCollectBlobIfNeeded() {
|
||||
|
||||
// GC for integrated BlobDB
|
||||
if (compaction_->enable_blob_garbage_collection()) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionIterator::GarbageCollectBlobIfNeeded::TamperWithBlobIndex",
|
||||
&value_);
|
||||
|
||||
BlobIndex blob_index;
|
||||
|
||||
{
|
||||
@@ -1060,10 +1064,9 @@ void CompactionIterator::PrepareOutput() {
|
||||
// Can we do the same for levels above bottom level as long as
|
||||
// KeyNotExistsBeyondOutputLevel() return true?
|
||||
if (valid_ && compaction_ != nullptr &&
|
||||
!compaction_->allow_ingest_behind() &&
|
||||
ikeyNotNeededForIncrementalSnapshot() && bottommost_level_ &&
|
||||
!compaction_->allow_ingest_behind() && bottommost_level_ &&
|
||||
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
|
||||
ikey_.type != kTypeMerge) {
|
||||
ikey_.type != kTypeMerge && current_key_committed_) {
|
||||
assert(ikey_.type != kTypeDeletion);
|
||||
assert(ikey_.type != kTypeSingleDeletion ||
|
||||
(timestamp_size_ || full_history_ts_low_));
|
||||
@@ -1139,13 +1142,6 @@ inline SequenceNumber CompactionIterator::findEarliestVisibleSnapshot(
|
||||
return kMaxSequenceNumber;
|
||||
}
|
||||
|
||||
// used in 2 places - prevents deletion markers to be dropped if they may be
|
||||
// needed and disables seqnum zero-out in PrepareOutput for recent keys.
|
||||
inline bool CompactionIterator::ikeyNotNeededForIncrementalSnapshot() {
|
||||
return (!compaction_->preserve_deletes()) ||
|
||||
(ikey_.sequence < preserve_deletes_seqnum_);
|
||||
}
|
||||
|
||||
uint64_t CompactionIterator::ComputeBlobGarbageCollectionCutoffFileNumber(
|
||||
const CompactionProxy* compaction) {
|
||||
if (!compaction) {
|
||||
|
||||
@@ -92,8 +92,6 @@ class CompactionIterator {
|
||||
|
||||
virtual bool allow_ingest_behind() const = 0;
|
||||
|
||||
virtual bool preserve_deletes() const = 0;
|
||||
|
||||
virtual bool allow_mmap_reads() const = 0;
|
||||
|
||||
virtual bool enable_blob_garbage_collection() const = 0;
|
||||
@@ -139,8 +137,6 @@ class CompactionIterator {
|
||||
return compaction_->immutable_options()->allow_ingest_behind;
|
||||
}
|
||||
|
||||
bool preserve_deletes() const override { return false; }
|
||||
|
||||
bool allow_mmap_reads() const override {
|
||||
return compaction_->immutable_options()->allow_mmap_reads;
|
||||
}
|
||||
@@ -176,14 +172,13 @@ class CompactionIterator {
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::atomic<bool>* manual_compaction_canceled = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
@@ -194,14 +189,13 @@ class CompactionIterator {
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::atomic<bool>* manual_compaction_canceled = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
@@ -272,14 +266,9 @@ class CompactionIterator {
|
||||
inline SequenceNumber findEarliestVisibleSnapshot(
|
||||
SequenceNumber in, SequenceNumber* prev_snapshot);
|
||||
|
||||
// Checks whether the currently seen ikey_ is needed for
|
||||
// incremental (differential) snapshot and hence can't be dropped
|
||||
// or seqnum be zero-ed out even if all other conditions for it are met.
|
||||
inline bool ikeyNotNeededForIncrementalSnapshot();
|
||||
|
||||
inline bool KeyCommitted(SequenceNumber sequence) {
|
||||
return snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->CheckInSnapshot(sequence, kMaxSequenceNumber) ==
|
||||
snapshot_checker_->CheckInSnapshot(sequence, job_snapshot_) ==
|
||||
SnapshotCheckerResult::kInSnapshot;
|
||||
}
|
||||
|
||||
@@ -320,6 +309,7 @@ class CompactionIterator {
|
||||
std::unordered_set<SequenceNumber> released_snapshots_;
|
||||
std::vector<SequenceNumber>::const_iterator earliest_snapshot_iter_;
|
||||
const SequenceNumber earliest_write_conflict_snapshot_;
|
||||
const SequenceNumber job_snapshot_;
|
||||
const SnapshotChecker* const snapshot_checker_;
|
||||
Env* env_;
|
||||
SystemClock* clock_;
|
||||
@@ -332,7 +322,6 @@ class CompactionIterator {
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const std::atomic<int>* manual_compaction_paused_;
|
||||
const std::atomic<bool>* manual_compaction_canceled_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
bool bottommost_level_;
|
||||
bool valid_ = false;
|
||||
bool visible_at_tip_;
|
||||
|
||||
@@ -166,8 +166,6 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
|
||||
bool allow_ingest_behind() const override { return is_allow_ingest_behind; }
|
||||
|
||||
bool preserve_deletes() const override { return false; }
|
||||
|
||||
bool allow_mmap_reads() const override { return false; }
|
||||
|
||||
bool enable_blob_garbage_collection() const override { return false; }
|
||||
@@ -277,11 +275,11 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
iter_->SeekToFirst();
|
||||
c_iter_.reset(new CompactionIterator(
|
||||
iter_.get(), cmp_, merge_helper_.get(), last_sequence, &snapshots_,
|
||||
earliest_write_conflict_snapshot, snapshot_checker_.get(),
|
||||
Env::Default(), false /* report_detailed_time */, false,
|
||||
range_del_agg_.get(), nullptr /* blob_file_builder */,
|
||||
true /*allow_data_in_errors*/, std::move(compaction), filter,
|
||||
&shutting_down_, /*preserve_deletes_seqnum=*/0,
|
||||
earliest_write_conflict_snapshot, kMaxSequenceNumber,
|
||||
snapshot_checker_.get(), Env::Default(),
|
||||
false /* report_detailed_time */, false, range_del_agg_.get(),
|
||||
nullptr /* blob_file_builder */, true /*allow_data_in_errors*/,
|
||||
std::move(compaction), filter, &shutting_down_,
|
||||
/*manual_compaction_paused=*/nullptr,
|
||||
/*manual_compaction_canceled=*/nullptr, /*info_log=*/nullptr,
|
||||
full_history_ts_low));
|
||||
|
||||
@@ -417,16 +417,17 @@ CompactionJob::CompactionJob(
|
||||
int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
|
||||
const MutableDBOptions& mutable_db_options, const FileOptions& file_options,
|
||||
VersionSet* versions, const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum, LogBuffer* log_buffer,
|
||||
FSDirectory* db_directory, FSDirectory* output_directory,
|
||||
FSDirectory* blob_output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
|
||||
LogBuffer* log_buffer, FSDirectory* db_directory,
|
||||
FSDirectory* output_directory, FSDirectory* blob_output_directory,
|
||||
Statistics* stats, InstrumentedMutex* db_mutex,
|
||||
ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
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::shared_ptr<IOTracer>& io_tracer,
|
||||
const SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||
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::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::atomic<bool>* manual_compaction_canceled,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
@@ -456,7 +457,6 @@ CompactionJob::CompactionJob(
|
||||
shutting_down_(shutting_down),
|
||||
manual_compaction_paused_(manual_compaction_paused),
|
||||
manual_compaction_canceled_(manual_compaction_canceled),
|
||||
preserve_deletes_seqnum_(preserve_deletes_seqnum),
|
||||
db_directory_(db_directory),
|
||||
blob_output_directory_(blob_output_directory),
|
||||
db_mutex_(db_mutex),
|
||||
@@ -464,6 +464,7 @@ CompactionJob::CompactionJob(
|
||||
existing_snapshots_(std::move(existing_snapshots)),
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
job_context_(job_context),
|
||||
table_cache_(std::move(table_cache)),
|
||||
event_logger_(event_logger),
|
||||
paranoid_file_checks_(paranoid_file_checks),
|
||||
@@ -1252,7 +1253,7 @@ void CompactionJob::NotifyOnSubcompactionBegin(
|
||||
if (shutting_down_->load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
if (c->is_manual_compaction() &&
|
||||
if (c->is_manual_compaction() && manual_compaction_paused_ &&
|
||||
manual_compaction_paused_->load(std::memory_order_acquire) > 0) {
|
||||
return;
|
||||
}
|
||||
@@ -1341,6 +1342,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
CompactionRangeDelAggregator range_del_agg(&cfd->internal_comparator(),
|
||||
existing_snapshots_);
|
||||
|
||||
// TODO: since we already use C++17, should use
|
||||
// std::optional<const Slice> instead.
|
||||
const Slice* const start = sub_compact->start;
|
||||
const Slice* const end = sub_compact->end;
|
||||
|
||||
@@ -1362,9 +1365,13 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
|
||||
// Although the v2 aggregator is what the level iterator(s) know about,
|
||||
// the AddTombstones calls will be propagated down to the v1 aggregator.
|
||||
std::unique_ptr<InternalIterator> raw_input(
|
||||
versions_->MakeInputIterator(read_options, sub_compact->compaction,
|
||||
&range_del_agg, file_options_for_read_));
|
||||
std::unique_ptr<InternalIterator> raw_input(versions_->MakeInputIterator(
|
||||
read_options, sub_compact->compaction, &range_del_agg,
|
||||
file_options_for_read_,
|
||||
(start == nullptr) ? std::optional<const Slice>{}
|
||||
: std::optional<const Slice>{*start},
|
||||
(end == nullptr) ? std::optional<const Slice>{}
|
||||
: std::optional<const Slice>{*end}));
|
||||
InternalIterator* input = raw_input.get();
|
||||
|
||||
IterKey start_ikey;
|
||||
@@ -1463,15 +1470,18 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
Status status;
|
||||
const std::string* const full_history_ts_low =
|
||||
full_history_ts_low_.empty() ? nullptr : &full_history_ts_low_;
|
||||
const SequenceNumber job_snapshot_seq =
|
||||
job_context_ ? job_context_->GetJobSnapshotSequence()
|
||||
: kMaxSequenceNumber;
|
||||
sub_compact->c_iter.reset(new CompactionIterator(
|
||||
input, cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_,
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_, job_snapshot_seq,
|
||||
snapshot_checker_, env_, ShouldReportDetailedTime(env_, stats_),
|
||||
/*expect_valid_internal_key=*/true, &range_del_agg,
|
||||
blob_file_builder.get(), db_options_.allow_data_in_errors,
|
||||
sub_compact->compaction, compaction_filter, shutting_down_,
|
||||
preserve_deletes_seqnum_, manual_compaction_paused_,
|
||||
manual_compaction_canceled_, db_options_.info_log, full_history_ts_low));
|
||||
manual_compaction_paused_, manual_compaction_canceled_,
|
||||
db_options_.info_log, full_history_ts_low));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
if (c_iter->Valid() && sub_compact->compaction->output_level() != 0) {
|
||||
@@ -1523,11 +1533,15 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
break;
|
||||
}
|
||||
|
||||
const ParsedInternalKey& ikey = c_iter->ikey();
|
||||
status = sub_compact->current_output()->meta.UpdateBoundaries(
|
||||
key, value, ikey.sequence, ikey.type);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
|
||||
sub_compact->current_output_file_size =
|
||||
sub_compact->builder->EstimatedFileSize();
|
||||
const ParsedInternalKey& ikey = c_iter->ikey();
|
||||
sub_compact->current_output()->meta.UpdateBoundaries(
|
||||
key, value, ikey.sequence, ikey.type);
|
||||
sub_compact->num_output_records++;
|
||||
|
||||
// Close output file if it is big enough. Two possibilities determine it's
|
||||
@@ -2482,19 +2496,20 @@ CompactionServiceCompactionJob::CompactionServiceCompactionJob(
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
|
||||
const std::string& dbname, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<bool>* manual_compaction_canceled,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
const std::string& output_path,
|
||||
const CompactionServiceInput& compaction_service_input,
|
||||
CompactionServiceResult* compaction_service_result)
|
||||
: CompactionJob(
|
||||
job_id, compaction, db_options, mutable_db_options, file_options,
|
||||
versions, shutting_down, 0, log_buffer, nullptr, output_directory,
|
||||
versions, shutting_down, log_buffer, nullptr, output_directory,
|
||||
nullptr, stats, db_mutex, db_error_handler, existing_snapshots,
|
||||
kMaxSequenceNumber, nullptr, table_cache, event_logger,
|
||||
kMaxSequenceNumber, nullptr, nullptr, table_cache, event_logger,
|
||||
compaction->mutable_cf_options()->paranoid_file_checks,
|
||||
compaction->mutable_cf_options()->report_bg_io_stats, dbname,
|
||||
&(compaction_service_result->stats), Env::Priority::USER, io_tracer,
|
||||
nullptr, nullptr, db_id, db_session_id,
|
||||
nullptr, manual_compaction_canceled, db_id, db_session_id,
|
||||
compaction->column_family_data()->GetFullHistoryTsLow()),
|
||||
output_path_(output_path),
|
||||
compaction_input_(compaction_service_input),
|
||||
|
||||
@@ -67,14 +67,13 @@ class CompactionJob {
|
||||
int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const FileOptions& file_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum, LogBuffer* log_buffer,
|
||||
const std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
FSDirectory* db_directory, FSDirectory* output_directory,
|
||||
FSDirectory* blob_output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker,
|
||||
const SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||
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,
|
||||
@@ -196,7 +195,6 @@ class CompactionJob {
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const std::atomic<int>* manual_compaction_paused_;
|
||||
const std::atomic<bool>* manual_compaction_canceled_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
FSDirectory* db_directory_;
|
||||
FSDirectory* blob_output_directory_;
|
||||
InstrumentedMutex* db_mutex_;
|
||||
@@ -214,6 +212,8 @@ class CompactionJob {
|
||||
|
||||
const SnapshotChecker* const snapshot_checker_;
|
||||
|
||||
JobContext* job_context_;
|
||||
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
|
||||
EventLogger* event_logger_;
|
||||
@@ -345,6 +345,7 @@ class CompactionServiceCompactionJob : private CompactionJob {
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
|
||||
const std::string& dbname, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<bool>* manual_compaction_canceled,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
const std::string& output_path,
|
||||
const CompactionServiceInput& compaction_service_input,
|
||||
|
||||
@@ -87,7 +87,6 @@ class CompactionJobTestBase : public testing::Test {
|
||||
/*block_cache_tracer=*/nullptr,
|
||||
/*io_tracer=*/nullptr, /*db_session_id*/ "")),
|
||||
shutting_down_(false),
|
||||
preserve_deletes_seqnum_(0),
|
||||
mock_table_factory_(new mock::MockTableFactory()),
|
||||
error_handler_(nullptr, db_options_, &mutex_),
|
||||
encode_u64_ts_(std::move(encode_u64_ts)) {
|
||||
@@ -354,11 +353,11 @@ class CompactionJobTestBase : public testing::Test {
|
||||
ucmp_->timestamp_size() == full_history_ts_low_.size());
|
||||
CompactionJob compaction_job(
|
||||
0, &compaction, db_options_, mutable_db_options_, env_options_,
|
||||
versions_.get(), &shutting_down_, preserve_deletes_seqnum_, &log_buffer,
|
||||
nullptr, nullptr, nullptr, nullptr, &mutex_, &error_handler_, snapshots,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, table_cache_,
|
||||
&event_logger, false, false, dbname_, &compaction_job_stats_,
|
||||
Env::Priority::USER, nullptr /* IOTracer */,
|
||||
versions_.get(), &shutting_down_, &log_buffer, nullptr, nullptr,
|
||||
nullptr, nullptr, &mutex_, &error_handler_, snapshots,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, nullptr,
|
||||
table_cache_, &event_logger, false, false, dbname_,
|
||||
&compaction_job_stats_, Env::Priority::USER, nullptr /* IOTracer */,
|
||||
/*manual_compaction_paused=*/nullptr,
|
||||
/*manual_compaction_canceled=*/nullptr, /*db_id=*/"",
|
||||
/*db_session_id=*/"", full_history_ts_low_);
|
||||
@@ -409,7 +408,6 @@ class CompactionJobTestBase : public testing::Test {
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
InstrumentedMutex mutex_;
|
||||
std::atomic<bool> shutting_down_;
|
||||
SequenceNumber preserve_deletes_seqnum_;
|
||||
std::shared_ptr<mock::MockTableFactory> mock_table_factory_;
|
||||
CompactionJobStats compaction_job_stats_;
|
||||
ColumnFamilyData* cfd_;
|
||||
|
||||
@@ -12,13 +12,16 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class MyTestCompactionService : public CompactionService {
|
||||
public:
|
||||
MyTestCompactionService(std::string db_path, Options& options,
|
||||
std::shared_ptr<Statistics>& statistics)
|
||||
MyTestCompactionService(
|
||||
std::string db_path, Options& options,
|
||||
std::shared_ptr<Statistics>& statistics,
|
||||
std::vector<std::shared_ptr<EventListener>>& listeners)
|
||||
: db_path_(std::move(db_path)),
|
||||
options_(options),
|
||||
statistics_(statistics),
|
||||
start_info_("na", "na", "na", 0, Env::TOTAL),
|
||||
wait_info_("na", "na", "na", 0, Env::TOTAL) {}
|
||||
wait_info_("na", "na", "na", 0, Env::TOTAL),
|
||||
listeners_(listeners) {}
|
||||
|
||||
static const char* kClassName() { return "MyTestCompactionService"; }
|
||||
|
||||
@@ -71,9 +74,16 @@ class MyTestCompactionService : public CompactionService {
|
||||
options_override.table_factory = options_.table_factory;
|
||||
options_override.sst_partitioner_factory = options_.sst_partitioner_factory;
|
||||
options_override.statistics = statistics_;
|
||||
if (!listeners_.empty()) {
|
||||
options_override.listeners = listeners_;
|
||||
}
|
||||
|
||||
OpenAndCompactOptions options;
|
||||
options.canceled = &canceled_;
|
||||
|
||||
Status s = DB::OpenAndCompact(
|
||||
db_path_, db_path_ + "/" + ROCKSDB_NAMESPACE::ToString(info.job_id),
|
||||
options, db_path_,
|
||||
db_path_ + "/" + ROCKSDB_NAMESPACE::ToString(info.job_id),
|
||||
compaction_input, compaction_service_result, options_override);
|
||||
if (is_override_wait_result_) {
|
||||
*compaction_service_result = override_wait_result_;
|
||||
@@ -112,6 +122,8 @@ class MyTestCompactionService : public CompactionService {
|
||||
is_override_wait_status_ = false;
|
||||
}
|
||||
|
||||
void SetCanceled(bool canceled) { canceled_ = canceled; }
|
||||
|
||||
private:
|
||||
InstrumentedMutex mutex_;
|
||||
std::atomic_int compaction_num_{0};
|
||||
@@ -129,6 +141,8 @@ class MyTestCompactionService : public CompactionService {
|
||||
CompactionServiceJobStatus::kFailure;
|
||||
bool is_override_wait_result_ = false;
|
||||
std::string override_wait_result_;
|
||||
std::vector<std::shared_ptr<EventListener>> listeners_;
|
||||
std::atomic_bool canceled_{false};
|
||||
};
|
||||
|
||||
class CompactionServiceTest : public DBTestBase {
|
||||
@@ -144,7 +158,7 @@ class CompactionServiceTest : public DBTestBase {
|
||||
compactor_statistics_ = CreateDBStatistics();
|
||||
|
||||
compaction_service_ = std::make_shared<MyTestCompactionService>(
|
||||
dbname_, *options, compactor_statistics_);
|
||||
dbname_, *options, compactor_statistics_, remote_listeners);
|
||||
options->compaction_service = compaction_service_;
|
||||
DestroyAndReopen(*options);
|
||||
}
|
||||
@@ -192,6 +206,8 @@ class CompactionServiceTest : public DBTestBase {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<EventListener>> remote_listeners;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Statistics> compactor_statistics_;
|
||||
std::shared_ptr<Statistics> primary_statistics_;
|
||||
@@ -322,6 +338,51 @@ TEST_F(CompactionServiceTest, ManualCompaction) {
|
||||
VerifyTestData();
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CancelCompactionOnRemoteSide) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
std::string start_str = Key(15);
|
||||
std::string end_str = Key(45);
|
||||
Slice start(start_str);
|
||||
Slice end(end_str);
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
|
||||
// Test cancel compaction at the beginning
|
||||
my_cs->SetCanceled(true);
|
||||
auto s = db_->CompactRange(CompactRangeOptions(), &start, &end);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
// compaction number is not increased
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num);
|
||||
VerifyTestData();
|
||||
|
||||
// Test cancel compaction in progress
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
my_cs = GetCompactionService();
|
||||
my_cs->SetCanceled(false);
|
||||
|
||||
std::atomic_bool cancel_issued{false};
|
||||
SyncPoint::GetInstance()->SetCallBack("CompactionJob::Run():Inprogress",
|
||||
[&](void* /*arg*/) {
|
||||
cancel_issued = true;
|
||||
my_cs->SetCanceled(true);
|
||||
});
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
s = db_->CompactRange(CompactRangeOptions(), &start, &end);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
ASSERT_TRUE(cancel_issued);
|
||||
// compaction number is not increased
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num);
|
||||
VerifyTestData();
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, FailedToStart) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -685,6 +746,88 @@ TEST_F(CompactionServiceTest, FallbackLocalManual) {
|
||||
VerifyTestData();
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, RemoteEventListener) {
|
||||
class RemoteEventListenerTest : public EventListener {
|
||||
public:
|
||||
const char* Name() const override { return "RemoteEventListenerTest"; }
|
||||
|
||||
void OnSubcompactionBegin(const SubcompactionJobInfo& info) override {
|
||||
auto result = on_going_compactions.emplace(info.job_id);
|
||||
ASSERT_TRUE(result.second); // make sure there's no duplication
|
||||
compaction_num++;
|
||||
EventListener::OnSubcompactionBegin(info);
|
||||
}
|
||||
void OnSubcompactionCompleted(const SubcompactionJobInfo& info) override {
|
||||
auto num = on_going_compactions.erase(info.job_id);
|
||||
ASSERT_TRUE(num == 1); // make sure the compaction id exists
|
||||
EventListener::OnSubcompactionCompleted(info);
|
||||
}
|
||||
void OnTableFileCreated(const TableFileCreationInfo& info) override {
|
||||
ASSERT_EQ(on_going_compactions.count(info.job_id), 1);
|
||||
file_created++;
|
||||
EventListener::OnTableFileCreated(info);
|
||||
}
|
||||
void OnTableFileCreationStarted(
|
||||
const TableFileCreationBriefInfo& info) override {
|
||||
ASSERT_EQ(on_going_compactions.count(info.job_id), 1);
|
||||
file_creation_started++;
|
||||
EventListener::OnTableFileCreationStarted(info);
|
||||
}
|
||||
|
||||
bool ShouldBeNotifiedOnFileIO() override {
|
||||
file_io_notified++;
|
||||
return EventListener::ShouldBeNotifiedOnFileIO();
|
||||
}
|
||||
|
||||
std::atomic_uint64_t file_io_notified{0};
|
||||
std::atomic_uint64_t file_creation_started{0};
|
||||
std::atomic_uint64_t file_created{0};
|
||||
|
||||
std::set<int> on_going_compactions; // store the job_id
|
||||
std::atomic_uint64_t compaction_num{0};
|
||||
};
|
||||
|
||||
auto listener = new RemoteEventListenerTest();
|
||||
remote_listeners.emplace_back(listener);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
ReopenWithCompactionService(&options);
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 10 + j;
|
||||
ASSERT_OK(Put(Key(key_id), "value" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// check the events are triggered
|
||||
ASSERT_TRUE(listener->file_io_notified > 0);
|
||||
ASSERT_TRUE(listener->file_creation_started > 0);
|
||||
ASSERT_TRUE(listener->file_created > 0);
|
||||
ASSERT_TRUE(listener->compaction_num > 0);
|
||||
ASSERT_TRUE(listener->on_going_compactions.empty());
|
||||
|
||||
// verify result
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + ToString(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + ToString(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/utilities/transaction_db.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "table/block_based/block_based_table_builder.h"
|
||||
#include "table/meta_blocks.h"
|
||||
@@ -275,6 +276,42 @@ class CorruptionTest : public testing::Test {
|
||||
}
|
||||
return Slice(*storage);
|
||||
}
|
||||
|
||||
void GetSortedWalFiles(std::vector<uint64_t>& file_nums) {
|
||||
std::vector<std::string> tmp_files;
|
||||
ASSERT_OK(env_->GetChildren(dbname_, &tmp_files));
|
||||
FileType type = kWalFile;
|
||||
for (const auto& file : tmp_files) {
|
||||
uint64_t number = 0;
|
||||
if (ParseFileName(file, &number, &type) && type == kWalFile) {
|
||||
file_nums.push_back(number);
|
||||
}
|
||||
}
|
||||
std::sort(file_nums.begin(), file_nums.end());
|
||||
}
|
||||
|
||||
void CorruptFileWithTruncation(FileType file, uint64_t number,
|
||||
uint64_t bytes_to_truncate = 0) {
|
||||
std::string path;
|
||||
switch (file) {
|
||||
case FileType::kWalFile:
|
||||
path = LogFileName(dbname_, number);
|
||||
break;
|
||||
// TODO: Add other file types as this method is being used for those file
|
||||
// types.
|
||||
default:
|
||||
return;
|
||||
}
|
||||
uint64_t old_size = 0;
|
||||
ASSERT_OK(env_->GetFileSize(path, &old_size));
|
||||
assert(old_size > bytes_to_truncate);
|
||||
uint64_t new_size = old_size - bytes_to_truncate;
|
||||
// If bytes_to_truncate == 0, it will do full truncation.
|
||||
if (bytes_to_truncate == 0) {
|
||||
new_size = old_size;
|
||||
}
|
||||
ASSERT_OK(test::TruncateFile(env_, path, new_size));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(CorruptionTest, Recovery) {
|
||||
@@ -300,6 +337,68 @@ TEST_F(CorruptionTest, Recovery) {
|
||||
Check(36, 36);
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, PostPITRCorruptionWALsRetained) {
|
||||
// Repro for bug where WALs following the point-in-time recovery were not
|
||||
// retained leading to the next recovery failing.
|
||||
CloseDb();
|
||||
|
||||
options_.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
|
||||
const std::string test_cf_name = "test_cf";
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs;
|
||||
cf_descs.emplace_back(kDefaultColumnFamilyName, ColumnFamilyOptions());
|
||||
cf_descs.emplace_back(test_cf_name, ColumnFamilyOptions());
|
||||
|
||||
uint64_t log_num;
|
||||
{
|
||||
options_.create_missing_column_families = true;
|
||||
std::vector<ColumnFamilyHandle*> cfhs;
|
||||
ASSERT_OK(DB::Open(options_, dbname_, cf_descs, &cfhs, &db_));
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), cfhs[0], "k", "v"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), cfhs[1], "k", "v"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), cfhs[0], "k2", "v2"));
|
||||
std::vector<uint64_t> file_nums;
|
||||
GetSortedWalFiles(file_nums);
|
||||
log_num = file_nums.back();
|
||||
for (auto* cfh : cfhs) {
|
||||
delete cfh;
|
||||
}
|
||||
CloseDb();
|
||||
}
|
||||
|
||||
CorruptFileWithTruncation(FileType::kWalFile, log_num,
|
||||
/*bytes_to_truncate=*/1);
|
||||
|
||||
{
|
||||
// Recover "k" -> "v" for both CFs. "k2" -> "v2" is lost due to truncation.
|
||||
options_.avoid_flush_during_recovery = true;
|
||||
std::vector<ColumnFamilyHandle*> cfhs;
|
||||
ASSERT_OK(DB::Open(options_, dbname_, cf_descs, &cfhs, &db_));
|
||||
// Flush one but not both CFs and write some data so there's a seqno gap
|
||||
// between the PITR corruption and the next DB session's first WAL.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), cfhs[1], "k2", "v2"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions(), cfhs[1]));
|
||||
|
||||
for (auto* cfh : cfhs) {
|
||||
delete cfh;
|
||||
}
|
||||
CloseDb();
|
||||
}
|
||||
|
||||
// With the bug, this DB open would remove the WALs following the PITR
|
||||
// corruption. Then, the next recovery would fail.
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
std::vector<ColumnFamilyHandle*> cfhs;
|
||||
ASSERT_OK(DB::Open(options_, dbname_, cf_descs, &cfhs, &db_));
|
||||
|
||||
for (auto* cfh : cfhs) {
|
||||
delete cfh;
|
||||
}
|
||||
CloseDb();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, RecoverWriteError) {
|
||||
env_->writable_file_error_ = true;
|
||||
Status s = TryReopen();
|
||||
@@ -912,6 +1011,313 @@ TEST_F(CorruptionTest, VerifyWholeTableChecksum) {
|
||||
ASSERT_EQ(1, count);
|
||||
}
|
||||
|
||||
class CrashDuringRecoveryWithCorruptionTest
|
||||
: public CorruptionTest,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
explicit CrashDuringRecoveryWithCorruptionTest()
|
||||
: CorruptionTest(),
|
||||
avoid_flush_during_recovery_(std::get<0>(GetParam())),
|
||||
track_and_verify_wals_in_manifest_(std::get<1>(GetParam())) {}
|
||||
|
||||
protected:
|
||||
const bool avoid_flush_during_recovery_;
|
||||
const bool track_and_verify_wals_in_manifest_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CorruptionTest, CrashDuringRecoveryWithCorruptionTest,
|
||||
::testing::Values(std::make_tuple(true, false),
|
||||
std::make_tuple(false, false),
|
||||
std::make_tuple(true, true),
|
||||
std::make_tuple(false, true)));
|
||||
|
||||
// In case of non-TransactionDB with avoid_flush_during_recovery = true, RocksDB
|
||||
// won't flush the data from WAL to L0 for all column families if possible. As a
|
||||
// result, not all column families can increase their log_numbers, and
|
||||
// min_log_number_to_keep won't change.
|
||||
// It may prematurely persist a new MANIFEST even before we can declare the DB
|
||||
// is in consistent state after recovery (this is when the new WAL is synced)
|
||||
// and advances log_numbers for some column families.
|
||||
//
|
||||
// If there is power failure before we sync the new WAL, we will end up in
|
||||
// a situation in which after persisting the MANIFEST, RocksDB will see some
|
||||
// column families' log_numbers larger than the corrupted wal, and
|
||||
// "Column family inconsistency: SST file contains data beyond the point of
|
||||
// corruption" error will be hit, causing recovery to fail.
|
||||
//
|
||||
// After adding the fix, corrupted WALs whose numbers are larger than the
|
||||
// corrupted wal and smaller than the new WAL are moved to a separate folder.
|
||||
// Only after new WAL is synced, RocksDB persist a new MANIFEST with column
|
||||
// families to ensure RocksDB is in consistent state.
|
||||
// RocksDB writes an empty WriteBatch as a sentinel to the new WAL which is
|
||||
// synced immediately afterwards. The sequence number of the sentinel
|
||||
// WriteBatch will be the next sequence number immediately after the largest
|
||||
// sequence number recovered from previous WALs and MANIFEST because of which DB
|
||||
// will be in consistent state.
|
||||
TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
options.track_and_verify_wals_in_manifest =
|
||||
track_and_verify_wals_in_manifest_;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
options.avoid_flush_during_recovery = false;
|
||||
options.env = env_;
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
options.create_if_missing = true;
|
||||
options.max_write_buffer_number = 3;
|
||||
|
||||
Reopen(&options);
|
||||
Status s;
|
||||
const std::string test_cf_name = "test_cf";
|
||||
ColumnFamilyHandle* cfh = nullptr;
|
||||
s = db_->CreateColumnFamily(options, test_cf_name, &cfh);
|
||||
ASSERT_OK(s);
|
||||
delete cfh;
|
||||
CloseDb();
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs;
|
||||
cf_descs.emplace_back(kDefaultColumnFamilyName, options);
|
||||
cf_descs.emplace_back(test_cf_name, options);
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
|
||||
// 1. Open and populate the DB. Write and flush default_cf several times to
|
||||
// advance wal number so that some column families have advanced log_number
|
||||
// while other don't.
|
||||
{
|
||||
ASSERT_OK(DB::Open(options, dbname_, cf_descs, &handles, &db_));
|
||||
auto* dbimpl = static_cast_with_check<DBImpl>(db_);
|
||||
assert(dbimpl);
|
||||
|
||||
// Write one key to test_cf.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles[1], "old_key", "dontcare"));
|
||||
// Write to default_cf and flush this cf several times to advance wal
|
||||
// number.
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key" + std::to_string(i), "value"));
|
||||
ASSERT_OK(dbimpl->TEST_SwitchMemtable());
|
||||
}
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles[1], "dontcare", "dontcare"));
|
||||
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
handles.clear();
|
||||
CloseDb();
|
||||
}
|
||||
|
||||
// 2. Corrupt second last wal file to emulate power reset which caused the DB
|
||||
// to lose the un-synced WAL.
|
||||
{
|
||||
std::vector<uint64_t> file_nums;
|
||||
GetSortedWalFiles(file_nums);
|
||||
size_t size = file_nums.size();
|
||||
uint64_t log_num = file_nums[size - 2];
|
||||
CorruptFileWithTruncation(FileType::kWalFile, log_num,
|
||||
/*bytes_to_truncate=*/8);
|
||||
}
|
||||
|
||||
// 3. After first crash reopen the DB which contains corrupted WAL. Default
|
||||
// family has higher log number than corrupted wal number.
|
||||
//
|
||||
// Case1: If avoid_flush_during_recovery = true, RocksDB won't flush the data
|
||||
// from WAL to L0 for all column families (test_cf_name in this case). As a
|
||||
// result, not all column families can increase their log_numbers, and
|
||||
// min_log_number_to_keep won't change.
|
||||
//
|
||||
// Case2: If avoid_flush_during_recovery = false, all column families have
|
||||
// flushed their data from WAL to L0 during recovery, and none of them will
|
||||
// ever need to read the WALs again.
|
||||
{
|
||||
options.avoid_flush_during_recovery = avoid_flush_during_recovery_;
|
||||
s = DB::Open(options, dbname_, cf_descs, &handles, &db_);
|
||||
ASSERT_OK(s);
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
handles.clear();
|
||||
CloseDb();
|
||||
}
|
||||
|
||||
// 4. Corrupt max_wal_num to emulate second power reset which caused the
|
||||
// DB to again lose the un-synced WAL.
|
||||
{
|
||||
std::vector<uint64_t> file_nums;
|
||||
GetSortedWalFiles(file_nums);
|
||||
size_t size = file_nums.size();
|
||||
uint64_t log_num = file_nums[size - 1];
|
||||
CorruptFileWithTruncation(FileType::kWalFile, log_num);
|
||||
}
|
||||
|
||||
// 5. After second crash reopen the db with second corruption. Default family
|
||||
// has higher log number than corrupted wal number.
|
||||
//
|
||||
// Case1: If avoid_flush_during_recovery = true, we persist a new
|
||||
// MANIFEST with advanced log_numbers for some column families only after
|
||||
// syncing the WAL. So during second crash, RocksDB will skip the corrupted
|
||||
// WAL files as they have been moved to different folder. Since newly synced
|
||||
// WAL file's sequence number (sentinel WriteBatch) will be the next
|
||||
// sequence number immediately after the largest sequence number recovered
|
||||
// from previous WALs and MANIFEST, db will be in consistent state and opens
|
||||
// successfully.
|
||||
//
|
||||
// Case2: If avoid_flush_during_recovery = false, the corrupted WAL is below
|
||||
// this number. So during a second crash after persisting the new MANIFEST,
|
||||
// RocksDB will skip the corrupted WAL(s) because they are all below this
|
||||
// bound. Therefore, we won't hit the "column family inconsistency" error
|
||||
// message.
|
||||
{
|
||||
options.avoid_flush_during_recovery = avoid_flush_during_recovery_;
|
||||
ASSERT_OK(DB::Open(options, dbname_, cf_descs, &handles, &db_));
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
handles.clear();
|
||||
CloseDb();
|
||||
}
|
||||
}
|
||||
|
||||
// In case of TransactionDB, it enables two-phase-commit. The prepare section of
|
||||
// an uncommitted transaction always need to be kept. Even if we perform flush
|
||||
// during recovery, we may still need to hold an old WAL. The
|
||||
// min_log_number_to_keep won't change, and "Column family inconsistency: SST
|
||||
// file contains data beyond the point of corruption" error will be hit, causing
|
||||
// recovery to fail.
|
||||
//
|
||||
// After adding the fix, corrupted WALs whose numbers are larger than the
|
||||
// corrupted wal and smaller than the new WAL are moved to a separate folder.
|
||||
// Only after new WAL is synced, RocksDB persist a new MANIFEST with column
|
||||
// families to ensure RocksDB is in consistent state.
|
||||
// RocksDB writes an empty WriteBatch as a sentinel to the new WAL which is
|
||||
// synced immediately afterwards. The sequence number of the sentinel
|
||||
// WriteBatch will be the next sequence number immediately after the largest
|
||||
// sequence number recovered from previous WALs and MANIFEST because of which DB
|
||||
// will be in consistent state.
|
||||
TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
options.track_and_verify_wals_in_manifest =
|
||||
track_and_verify_wals_in_manifest_;
|
||||
options.avoid_flush_during_recovery = false;
|
||||
options.env = env_;
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
options.create_if_missing = true;
|
||||
options.max_write_buffer_number = 3;
|
||||
Reopen(&options);
|
||||
|
||||
// Create cf test_cf_name.
|
||||
ColumnFamilyHandle* cfh = nullptr;
|
||||
const std::string test_cf_name = "test_cf";
|
||||
Status s = db_->CreateColumnFamily(options, test_cf_name, &cfh);
|
||||
ASSERT_OK(s);
|
||||
delete cfh;
|
||||
CloseDb();
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs;
|
||||
cf_descs.emplace_back(kDefaultColumnFamilyName, options);
|
||||
cf_descs.emplace_back(test_cf_name, options);
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
|
||||
TransactionDB* txn_db = nullptr;
|
||||
TransactionDBOptions txn_db_opts;
|
||||
|
||||
// 1. Open and populate the DB. Write and flush default_cf several times to
|
||||
// advance wal number so that some column families have advanced log_number
|
||||
// while other don't.
|
||||
{
|
||||
options.avoid_flush_during_recovery = avoid_flush_during_recovery_;
|
||||
ASSERT_OK(TransactionDB::Open(options, txn_db_opts, dbname_, cf_descs,
|
||||
&handles, &txn_db));
|
||||
|
||||
auto* txn = txn_db->BeginTransaction(WriteOptions(), TransactionOptions());
|
||||
// Put cf1
|
||||
ASSERT_OK(txn->Put(handles[1], "foo", "value"));
|
||||
ASSERT_OK(txn->SetName("txn0"));
|
||||
ASSERT_OK(txn->Prepare());
|
||||
delete txn;
|
||||
txn = nullptr;
|
||||
|
||||
auto* dbimpl = static_cast_with_check<DBImpl>(txn_db->GetRootDB());
|
||||
assert(dbimpl);
|
||||
|
||||
// Put and flush cf0
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
ASSERT_OK(txn_db->Put(WriteOptions(), "dontcare", "value"));
|
||||
ASSERT_OK(dbimpl->TEST_SwitchMemtable());
|
||||
}
|
||||
|
||||
// Put cf1
|
||||
txn = txn_db->BeginTransaction(WriteOptions(), TransactionOptions());
|
||||
ASSERT_OK(txn->Put(handles[1], "foo1", "value"));
|
||||
ASSERT_OK(txn->Commit());
|
||||
|
||||
delete txn;
|
||||
txn = nullptr;
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
handles.clear();
|
||||
delete txn_db;
|
||||
}
|
||||
|
||||
// 2. Corrupt second last wal to emulate power reset which caused the DB to
|
||||
// lose the un-synced WAL.
|
||||
{
|
||||
std::vector<uint64_t> file_nums;
|
||||
GetSortedWalFiles(file_nums);
|
||||
size_t size = file_nums.size();
|
||||
uint64_t log_num = file_nums[size - 2];
|
||||
CorruptFileWithTruncation(FileType::kWalFile, log_num,
|
||||
/*bytes_to_truncate=*/8);
|
||||
}
|
||||
|
||||
// 3. After first crash reopen the DB which contains corrupted WAL. Default
|
||||
// family has higher log number than corrupted wal number. There may be old
|
||||
// WAL files that it must not delete because they can contain data of
|
||||
// uncommitted transactions. As a result, min_log_number_to_keep won't change.
|
||||
{
|
||||
options.avoid_flush_during_recovery = avoid_flush_during_recovery_;
|
||||
ASSERT_OK(TransactionDB::Open(options, txn_db_opts, dbname_, cf_descs,
|
||||
&handles, &txn_db));
|
||||
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
handles.clear();
|
||||
delete txn_db;
|
||||
}
|
||||
|
||||
// 4. Corrupt max_wal_num to emulate second power reset which caused the
|
||||
// DB to again lose the un-synced WAL.
|
||||
{
|
||||
std::vector<uint64_t> file_nums;
|
||||
GetSortedWalFiles(file_nums);
|
||||
size_t size = file_nums.size();
|
||||
uint64_t log_num = file_nums[size - 1];
|
||||
CorruptFileWithTruncation(FileType::kWalFile, log_num);
|
||||
}
|
||||
|
||||
// 5. After second crash reopen the db with second corruption. Default family
|
||||
// has higher log number than corrupted wal number.
|
||||
// We persist a new MANIFEST with advanced log_numbers for some column
|
||||
// families only after syncing the WAL. So during second crash, RocksDB will
|
||||
// skip the corrupted WAL files as they have been moved to different folder.
|
||||
// Since newly synced WAL file's sequence number (sentinel WriteBatch) will be
|
||||
// the next sequence number immediately after the largest sequence number
|
||||
// recovered from previous WALs and MANIFEST, db will be in consistent state
|
||||
// and opens successfully.
|
||||
{
|
||||
options.avoid_flush_during_recovery = false;
|
||||
|
||||
ASSERT_OK(TransactionDB::Open(options, txn_db_opts, dbname_, cf_descs,
|
||||
&handles, &txn_db));
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
delete txn_db;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+4
-1
@@ -175,7 +175,10 @@ TEST_F(DBBasicTest, ReadOnlyDB) {
|
||||
ASSERT_TRUE(db_->SyncWAL().IsNotSupported());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, ReadOnlyDBWithWriteDBIdToManifestSet) {
|
||||
// TODO akanksha: Update the test to check that combination
|
||||
// does not actually write to FS (use open read-only with
|
||||
// CompositeEnvWrapper+ReadOnlyFileSystem).
|
||||
TEST_F(DBBasicTest, DISABLED_ReadOnlyDBWithWriteDBIdToManifestSet) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
ASSERT_OK(Put("bar", "v2"));
|
||||
ASSERT_OK(Put("foo", "v3"));
|
||||
|
||||
@@ -1404,21 +1404,11 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
|
||||
ASSERT_TRUE(
|
||||
db_->GetMapProperty(DB::Properties::kBlockCacheEntryStats, &values));
|
||||
|
||||
EXPECT_EQ(
|
||||
ToString(expected[static_cast<size_t>(CacheEntryRole::kIndexBlock)]),
|
||||
values["count.index-block"]);
|
||||
EXPECT_EQ(
|
||||
ToString(expected[static_cast<size_t>(CacheEntryRole::kDataBlock)]),
|
||||
values["count.data-block"]);
|
||||
EXPECT_EQ(
|
||||
ToString(expected[static_cast<size_t>(CacheEntryRole::kFilterBlock)]),
|
||||
values["count.filter-block"]);
|
||||
EXPECT_EQ(
|
||||
ToString(
|
||||
prev_expected[static_cast<size_t>(CacheEntryRole::kWriteBuffer)]),
|
||||
values["count.write-buffer"]);
|
||||
EXPECT_EQ(ToString(expected[static_cast<size_t>(CacheEntryRole::kMisc)]),
|
||||
values["count.misc"]);
|
||||
for (size_t i = 0; i < kNumCacheEntryRoles; ++i) {
|
||||
auto role = static_cast<CacheEntryRole>(i);
|
||||
EXPECT_EQ(ToString(expected[i]),
|
||||
values[BlockCacheEntryStatsMapKeys::EntryCount(role)]);
|
||||
}
|
||||
|
||||
// Add one for kWriteBuffer
|
||||
{
|
||||
@@ -1431,7 +1421,8 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
|
||||
env_->MockSleepForSeconds(1);
|
||||
EXPECT_EQ(ToString(prev_expected[static_cast<size_t>(
|
||||
CacheEntryRole::kWriteBuffer)]),
|
||||
values["count.write-buffer"]);
|
||||
values[BlockCacheEntryStatsMapKeys::EntryCount(
|
||||
CacheEntryRole::kWriteBuffer)]);
|
||||
// Not enough for a "background" miss but enough for a "foreground" miss
|
||||
env_->MockSleepForSeconds(45);
|
||||
|
||||
@@ -1440,7 +1431,8 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
|
||||
EXPECT_EQ(
|
||||
ToString(
|
||||
expected[static_cast<size_t>(CacheEntryRole::kWriteBuffer)]),
|
||||
values["count.write-buffer"]);
|
||||
values[BlockCacheEntryStatsMapKeys::EntryCount(
|
||||
CacheEntryRole::kWriteBuffer)]);
|
||||
}
|
||||
prev_expected = expected;
|
||||
|
||||
|
||||
+97
-80
@@ -17,8 +17,11 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/advanced_options.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
#include "table/block_based/filter_policy_internal.h"
|
||||
@@ -798,83 +801,87 @@ TEST_F(DBBloomFilterTest, BloomFilterRate) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct CompatibilityConfig {
|
||||
std::shared_ptr<const FilterPolicy> policy;
|
||||
bool partitioned;
|
||||
uint32_t format_version;
|
||||
|
||||
void SetInTableOptions(BlockBasedTableOptions* table_options) {
|
||||
table_options->filter_policy = policy;
|
||||
table_options->partition_filters = partitioned;
|
||||
if (partitioned) {
|
||||
table_options->index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
} else {
|
||||
table_options->index_type =
|
||||
BlockBasedTableOptions::IndexType::kBinarySearch;
|
||||
}
|
||||
table_options->format_version = format_version;
|
||||
}
|
||||
};
|
||||
// High bits per key -> almost no FPs
|
||||
std::shared_ptr<const FilterPolicy> kCompatibilityBloomPolicy{
|
||||
NewBloomFilterPolicy(20)};
|
||||
// bloom_before_level=-1 -> always use Ribbon
|
||||
std::shared_ptr<const FilterPolicy> kCompatibilityRibbonPolicy{
|
||||
NewRibbonFilterPolicy(20, -1)};
|
||||
|
||||
std::vector<CompatibilityConfig> kCompatibilityConfigs = {
|
||||
{Create(20, kDeprecatedBlock), false,
|
||||
BlockBasedTableOptions().format_version},
|
||||
{kCompatibilityBloomPolicy, false, BlockBasedTableOptions().format_version},
|
||||
{kCompatibilityBloomPolicy, true, BlockBasedTableOptions().format_version},
|
||||
{kCompatibilityBloomPolicy, false, /* legacy Bloom */ 4U},
|
||||
{kCompatibilityRibbonPolicy, false,
|
||||
BlockBasedTableOptions().format_version},
|
||||
{kCompatibilityRibbonPolicy, true, BlockBasedTableOptions().format_version},
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilterCompatibility) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.level0_file_num_compaction_trigger =
|
||||
static_cast<int>(kCompatibilityConfigs.size()) + 1;
|
||||
options.max_open_files = -1;
|
||||
|
||||
// Create with block based filter
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Close();
|
||||
|
||||
const int maxKey = 10000;
|
||||
for (int i = 0; i < maxKey; i++) {
|
||||
ASSERT_OK(Put(1, Key(i), Key(i)));
|
||||
}
|
||||
ASSERT_OK(Put(1, Key(maxKey + 55555), Key(maxKey + 55555)));
|
||||
Flush(1);
|
||||
|
||||
// Check db with full filter
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
// Check if they can be found
|
||||
for (int i = 0; i < maxKey; i++) {
|
||||
ASSERT_EQ(Key(i), Get(1, Key(i)));
|
||||
}
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
|
||||
// Check db with partitioned full filter
|
||||
table_options.partition_filters = true;
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
// Check if they can be found
|
||||
for (int i = 0; i < maxKey; i++) {
|
||||
ASSERT_EQ(Key(i), Get(1, Key(i)));
|
||||
}
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilterReverseCompatibility) {
|
||||
for (bool partition_filters : {true, false}) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
// Create one file for each kind of filter. Each file covers a distinct key
|
||||
// range.
|
||||
for (size_t i = 0; i < kCompatibilityConfigs.size(); ++i) {
|
||||
BlockBasedTableOptions table_options;
|
||||
if (partition_filters) {
|
||||
table_options.partition_filters = true;
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
}
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
kCompatibilityConfigs[i].SetInTableOptions(&table_options);
|
||||
ASSERT_TRUE(table_options.filter_policy != nullptr);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
Reopen(options);
|
||||
|
||||
// Create with full filter
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
std::string prefix = ToString(i) + "_";
|
||||
ASSERT_OK(Put(prefix + "A", "val"));
|
||||
ASSERT_OK(Put(prefix + "Z", "val"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
const int maxKey = 10000;
|
||||
for (int i = 0; i < maxKey; i++) {
|
||||
ASSERT_OK(Put(1, Key(i), Key(i)));
|
||||
}
|
||||
ASSERT_OK(Put(1, Key(maxKey + 55555), Key(maxKey + 55555)));
|
||||
Flush(1);
|
||||
|
||||
// Check db with block_based filter
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||
// Test filter is used between each pair of {reader,writer} configurations,
|
||||
// because any built-in FilterPolicy should be able to read filters from any
|
||||
// other built-in FilterPolicy
|
||||
for (size_t i = 0; i < kCompatibilityConfigs.size(); ++i) {
|
||||
BlockBasedTableOptions table_options;
|
||||
kCompatibilityConfigs[i].SetInTableOptions(&table_options);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
// Check if they can be found
|
||||
for (int i = 0; i < maxKey; i++) {
|
||||
ASSERT_EQ(Key(i), Get(1, Key(i)));
|
||||
Reopen(options);
|
||||
for (size_t j = 0; j < kCompatibilityConfigs.size(); ++j) {
|
||||
std::string prefix = ToString(j) + "_";
|
||||
ASSERT_EQ("val", Get(prefix + "A")); // Filter positive
|
||||
ASSERT_EQ("val", Get(prefix + "Z")); // Filter positive
|
||||
// Filter negative, with high probability
|
||||
ASSERT_EQ("NOT_FOUND", Get(prefix + "Q"));
|
||||
// FULL_POSITIVE does not include block-based filter case (j == 0)
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_POSITIVE),
|
||||
j == 0 ? 0 : 2);
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
}
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -919,7 +926,7 @@ class FilterConstructResPeakTrackingCache : public CacheWrapper {
|
||||
}
|
||||
|
||||
using Cache::Release;
|
||||
bool Release(Handle* handle, bool force_erase = false) override {
|
||||
bool Release(Handle* handle, bool erase_if_last_ref = false) override {
|
||||
auto deleter = GetDeleter(handle);
|
||||
if (deleter == kNoopDeleterForFilterConstruction) {
|
||||
if (!last_peak_tracked_) {
|
||||
@@ -929,7 +936,7 @@ class FilterConstructResPeakTrackingCache : public CacheWrapper {
|
||||
}
|
||||
cur_cache_res_ -= GetCharge(handle);
|
||||
}
|
||||
bool is_successful = target_->Release(handle, force_erase);
|
||||
bool is_successful = target_->Release(handle, erase_if_last_ref);
|
||||
return is_successful;
|
||||
}
|
||||
|
||||
@@ -952,8 +959,8 @@ class FilterConstructResPeakTrackingCache : public CacheWrapper {
|
||||
|
||||
const Cache::DeleterFn
|
||||
FilterConstructResPeakTrackingCache::kNoopDeleterForFilterConstruction =
|
||||
CacheReservationManager::TEST_GetNoopDeleterForRole<
|
||||
CacheEntryRole::kFilterConstruction>();
|
||||
CacheReservationManagerImpl<
|
||||
CacheEntryRole::kFilterConstruction>::TEST_GetNoopDeleterForRole();
|
||||
|
||||
// To align with the type of hash entry being reserved in implementation.
|
||||
using FilterConstructionReserveMemoryHash = uint64_t;
|
||||
@@ -983,7 +990,9 @@ class DBFilterConstructionReserveMemoryTestWithParam
|
||||
// trigger at least 1 dummy entry reservation each for hash entries and
|
||||
// final filter, we need a large number of keys to ensure we have at least
|
||||
// two partitions.
|
||||
num_key_ = 18 * CacheReservationManager::GetDummyEntrySize() /
|
||||
num_key_ = 18 *
|
||||
CacheReservationManagerImpl<
|
||||
CacheEntryRole::kFilterConstruction>::GetDummyEntrySize() /
|
||||
sizeof(FilterConstructionReserveMemoryHash);
|
||||
} else if (policy_ == kFastLocalBloom) {
|
||||
// For Bloom Filter + FullFilter case, since we design the num_key_ to
|
||||
@@ -993,7 +1002,9 @@ class DBFilterConstructionReserveMemoryTestWithParam
|
||||
// behavior and we don't need a large number of keys to verify we
|
||||
// indeed charge the final filter for cache reservation, even though final
|
||||
// filter is a lot smaller than hash entries.
|
||||
num_key_ = 1 * CacheReservationManager::GetDummyEntrySize() /
|
||||
num_key_ = 1 *
|
||||
CacheReservationManagerImpl<
|
||||
CacheEntryRole::kFilterConstruction>::GetDummyEntrySize() /
|
||||
sizeof(FilterConstructionReserveMemoryHash);
|
||||
} else {
|
||||
// For Ribbon Filter + FullFilter case, we need a large enough number of
|
||||
@@ -1001,7 +1012,9 @@ class DBFilterConstructionReserveMemoryTestWithParam
|
||||
// reservation will trigger at least another dummy entry (or equivalently
|
||||
// to saying, causing another peak in cache reservation) as banding
|
||||
// reservation might not be a multiple of dummy entry.
|
||||
num_key_ = 12 * CacheReservationManager::GetDummyEntrySize() /
|
||||
num_key_ = 12 *
|
||||
CacheReservationManagerImpl<
|
||||
CacheEntryRole::kFilterConstruction>::GetDummyEntrySize() /
|
||||
sizeof(FilterConstructionReserveMemoryHash);
|
||||
}
|
||||
}
|
||||
@@ -1061,7 +1074,8 @@ class DBFilterConstructionReserveMemoryTestWithParam
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
BlockBasedTableOptions, DBFilterConstructionReserveMemoryTestWithParam,
|
||||
DBFilterConstructionReserveMemoryTestWithParam,
|
||||
DBFilterConstructionReserveMemoryTestWithParam,
|
||||
::testing::Values(std::make_tuple(false, kFastLocalBloom, false, false),
|
||||
|
||||
std::make_tuple(true, kFastLocalBloom, false, false),
|
||||
@@ -1077,7 +1091,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
std::make_tuple(true, kDeprecatedBlock, false, false),
|
||||
std::make_tuple(true, kLegacyBloom, false, false)));
|
||||
|
||||
// TODO: Speed up this test.
|
||||
// TODO: Speed up this test, and reduce disk space usage (~700MB)
|
||||
// The current test inserts many keys (on the scale of dummy entry size)
|
||||
// in order to make small memory user (e.g, final filter, partitioned hash
|
||||
// entries/filter/banding) , which is proportional to the number of
|
||||
@@ -1156,8 +1170,8 @@ TEST_P(DBFilterConstructionReserveMemoryTestWithParam, ReserveMemory) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::size_t kDummyEntrySize =
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
const std::size_t kDummyEntrySize = CacheReservationManagerImpl<
|
||||
CacheEntryRole::kFilterConstruction>::GetDummyEntrySize();
|
||||
|
||||
const std::size_t predicted_hash_entries_cache_res =
|
||||
num_key * sizeof(FilterConstructionReserveMemoryHash);
|
||||
@@ -1345,9 +1359,12 @@ TEST_P(DBFilterConstructionReserveMemoryTestWithParam, ReserveMemory) {
|
||||
*
|
||||
*/
|
||||
if (!partition_filters) {
|
||||
ASSERT_GE(std::floor(1.0 * predicted_final_filter_cache_res /
|
||||
CacheReservationManager::GetDummyEntrySize()),
|
||||
1)
|
||||
ASSERT_GE(
|
||||
std::floor(
|
||||
1.0 * predicted_final_filter_cache_res /
|
||||
CacheReservationManagerImpl<
|
||||
CacheEntryRole::kFilterConstruction>::GetDummyEntrySize()),
|
||||
1)
|
||||
<< "Final filter cache reservation too small for this test - please "
|
||||
"increase the number of keys";
|
||||
if (!detect_filter_construct_corruption) {
|
||||
|
||||
@@ -1354,6 +1354,74 @@ TEST_P(DBCompactionTestWithParam, TrivialMoveTargetLevel) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, PartialOverlappingL0) {
|
||||
class SubCompactionEventListener : public EventListener {
|
||||
public:
|
||||
void OnSubcompactionCompleted(const SubcompactionJobInfo&) override {
|
||||
sub_compaction_finished_++;
|
||||
}
|
||||
std::atomic<int> sub_compaction_finished_{0};
|
||||
};
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.write_buffer_size = 10 * 1024 * 1024;
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
SubCompactionEventListener* listener = new SubCompactionEventListener();
|
||||
options.listeners.emplace_back(listener);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// For subcompactino to trigger, output level needs to be non-empty.
|
||||
ASSERT_OK(Put("key", ""));
|
||||
ASSERT_OK(Put("kez", ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("key", ""));
|
||||
ASSERT_OK(Put("kez", ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
// Ranges that are only briefly overlapping so that they won't be trivially
|
||||
// moved but subcompaction ranges would only contain a subset of files.
|
||||
std::vector<std::pair<int32_t, int32_t>> ranges = {
|
||||
{100, 199}, {198, 399}, {397, 600}, {598, 800}, {799, 900}, {895, 999},
|
||||
};
|
||||
int32_t value_size = 10 * 1024; // 10 KB
|
||||
|
||||
Random rnd(301);
|
||||
std::map<int32_t, std::string> values;
|
||||
for (size_t i = 0; i < ranges.size(); i++) {
|
||||
for (int32_t j = ranges[i].first; j <= ranges[i].second; j++) {
|
||||
values[j] = rnd.RandomString(value_size);
|
||||
ASSERT_OK(Put(Key(j), values[j]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
int32_t level0_files = NumTableFilesAtLevel(0, 0);
|
||||
ASSERT_EQ(level0_files, ranges.size()); // Multiple files in L0
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1, 0), 1); // One file in L1
|
||||
|
||||
listener->sub_compaction_finished_ = 0;
|
||||
ASSERT_OK(db_->EnableAutoCompaction({db_->DefaultColumnFamily()}));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
if (max_subcompactions_ > 3) {
|
||||
// RocksDB might not generate the exact number of sub compactions.
|
||||
// Here we validate that at least subcompaction happened.
|
||||
ASSERT_GT(listener->sub_compaction_finished_.load(), 2);
|
||||
}
|
||||
|
||||
// We expect that all the files were compacted to L1
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0, 0), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(1, 0), 1);
|
||||
|
||||
for (size_t i = 0; i < ranges.size(); i++) {
|
||||
for (int32_t j = ranges[i].first; j <= ranges[i].second; j++) {
|
||||
ASSERT_EQ(Get(Key(j)), values[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, ManualCompactionPartial) {
|
||||
int32_t trivial_move = 0;
|
||||
int32_t non_trivial_move = 0;
|
||||
@@ -6416,20 +6484,29 @@ TEST_F(DBCompactionTest, CompactionWithBlobGCError_CorruptIndex) {
|
||||
ASSERT_OK(Put(third_key, third_value));
|
||||
|
||||
constexpr char fourth_key[] = "fourth_key";
|
||||
constexpr char corrupt_blob_index[] = "foobar";
|
||||
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, 0, fourth_key,
|
||||
corrupt_blob_index));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
constexpr char fourth_value[] = "fourth_value";
|
||||
ASSERT_OK(Put(fourth_key, fourth_value));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionIterator::GarbageCollectBlobIfNeeded::TamperWithBlobIndex",
|
||||
[](void* arg) {
|
||||
Slice* const blob_index = static_cast<Slice*>(arg);
|
||||
assert(blob_index);
|
||||
assert(!blob_index->empty());
|
||||
blob_index->remove_prefix(1);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr Slice* begin = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
|
||||
ASSERT_TRUE(
|
||||
db_->CompactRange(CompactRangeOptions(), begin, end).IsCorruption());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactionWithBlobGCError_InlinedTTLIndex) {
|
||||
|
||||
+7
-11
@@ -45,17 +45,15 @@ Status DBImpl::FlushForGetLiveFiles() {
|
||||
}
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions(), FlushReason::kGetLiveFiles);
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:1");
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:2");
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (!status.ok() && !status.IsColumnFamilyDropped()) {
|
||||
break;
|
||||
} else if (status.IsColumnFamilyDropped()) {
|
||||
@@ -63,7 +61,6 @@ Status DBImpl::FlushForGetLiveFiles() {
|
||||
}
|
||||
}
|
||||
}
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -99,7 +96,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
3); // for CURRENT + MANIFEST + OPTIONS
|
||||
|
||||
// create names of the live files. The names are not absolute
|
||||
// paths, instead they are relative to dbname_;
|
||||
// paths, instead they are relative to dbname_.
|
||||
for (const auto& table_file_number : live_table_files) {
|
||||
ret.emplace_back(MakeTableFileName("", table_file_number));
|
||||
}
|
||||
@@ -169,7 +166,7 @@ Status DBImpl::GetCurrentWalFile(std::unique_ptr<LogFile>* current_log_file) {
|
||||
Status DBImpl::GetLiveFilesStorageInfo(
|
||||
const LiveFilesStorageInfoOptions& opts,
|
||||
std::vector<LiveFileStorageInfo>* files) {
|
||||
// To avoid returning partial results, only move to ouput on success
|
||||
// To avoid returning partial results, only move results to files on success.
|
||||
assert(files);
|
||||
files->clear();
|
||||
std::vector<LiveFileStorageInfo> results;
|
||||
@@ -183,7 +180,7 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
if (opts.wal_size_for_flush == port::kMaxUint64) {
|
||||
flush_memtable = false;
|
||||
} else if (opts.wal_size_for_flush > 0) {
|
||||
// If out standing log files are small, we skip the flush.
|
||||
// If the outstanding log files are small, we skip the flush.
|
||||
s = GetSortedWalFiles(live_wal_files);
|
||||
|
||||
if (!s.ok()) {
|
||||
@@ -316,8 +313,7 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
info.relative_filename = kCurrentFileName;
|
||||
info.directory = GetName();
|
||||
info.file_type = kCurrentFile;
|
||||
// CURRENT could be replaced so we have to record the contents we want
|
||||
// for it
|
||||
// CURRENT could be replaced so we have to record the contents as needed.
|
||||
info.replacement_contents = manifest_fname + "\n";
|
||||
info.size = manifest_fname.size() + 1;
|
||||
if (opts.include_checksum_info) {
|
||||
@@ -359,7 +355,7 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
TEST_SYNC_POINT("CheckpointImpl::CreateCustomCheckpoint:AfterGetLive1");
|
||||
TEST_SYNC_POINT("CheckpointImpl::CreateCustomCheckpoint:AfterGetLive2");
|
||||
|
||||
// if we have more than one column family, we need to also get WAL files
|
||||
// If we have more than one column family, we also need to get WAL files.
|
||||
if (s.ok()) {
|
||||
s = GetSortedWalFiles(live_wal_files);
|
||||
}
|
||||
@@ -397,7 +393,7 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
// Only move output on success
|
||||
// Only move results to output on success.
|
||||
*files = std::move(results);
|
||||
}
|
||||
return s;
|
||||
|
||||
+1
-1
@@ -2271,7 +2271,7 @@ TEST_P(DBAtomicFlushTest, ManualFlushUnder2PC) {
|
||||
|
||||
// The recovered min log number with prepared data should be non-zero.
|
||||
// In 2pc mode, MinLogNumberToKeep returns the
|
||||
// VersionSet::min_log_number_to_keep_2pc recovered from MANIFEST, if it's 0,
|
||||
// VersionSet::min_log_number_to_keep recovered from MANIFEST, if it's 0,
|
||||
// it means atomic flush didn't write the min_log_number_to_keep to MANIFEST.
|
||||
cfs.push_back(kDefaultColumnFamilyName);
|
||||
ASSERT_OK(TryReopenWithColumnFamilies(cfs, options));
|
||||
|
||||
+25
-29
@@ -101,6 +101,7 @@
|
||||
#include "util/compression.h"
|
||||
#include "util/crc32c.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/hash_containers.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -375,15 +376,12 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
s = AtomicFlushMemTables(cfds, flush_opts, context.flush_reason);
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
s = FlushMemTable(cfd, flush_opts, context.flush_reason);
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -495,18 +493,14 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
s.PermitUncheckedError(); //**TODO: What to do on error?
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (!cfd->IsDropped() && cfd->initialized() && !cfd->mem()->IsEmpty()) {
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
Status s = FlushMemTable(cfd, FlushOptions(), FlushReason::kShutDown);
|
||||
s.PermitUncheckedError(); //**TODO: What to do on error?
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
}
|
||||
|
||||
shutting_down_.store(true, std::memory_order_release);
|
||||
@@ -969,18 +963,13 @@ void DBImpl::DumpStats() {
|
||||
TEST_SYNC_POINT("DBImpl::DumpStats:StartRunning");
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->initialized()) {
|
||||
// Release DB mutex for gathering cache entry stats. Pass over all
|
||||
// column families for this first so that other stats are dumped
|
||||
// near-atomically.
|
||||
// Get a ref before unlocking
|
||||
cfd->Ref();
|
||||
{
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
cfd->internal_stats()->CollectCacheEntryStats(/*foreground=*/false);
|
||||
}
|
||||
cfd->UnrefAndTryDelete();
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
cfd->internal_stats()->CollectCacheEntryStats(/*foreground=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1093,7 +1082,6 @@ Status DBImpl::SetOptions(
|
||||
MutableCFOptions new_options;
|
||||
Status s;
|
||||
Status persist_options_status;
|
||||
persist_options_status.PermitUncheckedError(); // Allow uninitialized access
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
{
|
||||
auto db_options = GetDBOptions();
|
||||
@@ -1129,9 +1117,11 @@ Status DBImpl::SetOptions(
|
||||
"[%s] SetOptions() succeeded", cfd->GetName().c_str());
|
||||
new_options.Dump(immutable_db_options_.info_log.get());
|
||||
if (!persist_options_status.ok()) {
|
||||
// NOTE: WriteOptionsFile already logs on failure
|
||||
s = persist_options_status;
|
||||
}
|
||||
} else {
|
||||
persist_options_status.PermitUncheckedError(); // less important
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log, "[%s] SetOptions() failed",
|
||||
cfd->GetName().c_str());
|
||||
}
|
||||
@@ -1900,6 +1890,8 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
return s;
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::GetImpl:PostMemTableGet:0");
|
||||
TEST_SYNC_POINT("DBImpl::GetImpl:PostMemTableGet:1");
|
||||
PinnedIteratorsManager pinned_iters_mgr;
|
||||
if (!done) {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
@@ -1917,8 +1909,6 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
{
|
||||
PERF_TIMER_GUARD(get_post_process_time);
|
||||
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
|
||||
RecordTick(stats_, NUMBER_KEYS_READ);
|
||||
size_t size = 0;
|
||||
if (s.ok()) {
|
||||
@@ -1945,6 +1935,8 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
PERF_COUNTER_ADD(get_read_bytes, size);
|
||||
}
|
||||
RecordInHistogram(stats_, BYTES_PER_READ, size);
|
||||
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -2009,7 +2001,7 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
|
||||
SequenceNumber consistent_seqnum;
|
||||
|
||||
std::unordered_map<uint32_t, MultiGetColumnFamilyData> multiget_cf_data(
|
||||
UnorderedMap<uint32_t, MultiGetColumnFamilyData> multiget_cf_data(
|
||||
column_family.size());
|
||||
for (auto cf : column_family) {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(cf);
|
||||
@@ -2021,13 +2013,13 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
}
|
||||
|
||||
std::function<MultiGetColumnFamilyData*(
|
||||
std::unordered_map<uint32_t, MultiGetColumnFamilyData>::iterator&)>
|
||||
UnorderedMap<uint32_t, MultiGetColumnFamilyData>::iterator&)>
|
||||
iter_deref_lambda =
|
||||
[](std::unordered_map<uint32_t, MultiGetColumnFamilyData>::iterator&
|
||||
[](UnorderedMap<uint32_t, MultiGetColumnFamilyData>::iterator&
|
||||
cf_iter) { return &cf_iter->second; };
|
||||
|
||||
bool unref_only =
|
||||
MultiCFSnapshot<std::unordered_map<uint32_t, MultiGetColumnFamilyData>>(
|
||||
MultiCFSnapshot<UnorderedMap<uint32_t, MultiGetColumnFamilyData>>(
|
||||
read_options, nullptr, iter_deref_lambda, &multiget_cf_data,
|
||||
&consistent_seqnum);
|
||||
|
||||
@@ -3490,15 +3482,13 @@ bool DBImpl::GetAggregatedIntProperty(const Slice& property,
|
||||
// Needs mutex to protect the list of column families.
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
uint64_t value;
|
||||
for (auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto* cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (!cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
ret = GetIntPropertyInternal(cfd, *property_info, true, &value);
|
||||
// GetIntPropertyInternal may release db mutex and re-acquire it.
|
||||
mutex_.AssertHeld();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (ret) {
|
||||
sum += value;
|
||||
} else {
|
||||
@@ -3692,6 +3682,11 @@ Status DBImpl::GetUpdatesSince(
|
||||
SequenceNumber seq, std::unique_ptr<TransactionLogIterator>* iter,
|
||||
const TransactionLogIterator::ReadOptions& read_options) {
|
||||
RecordTick(stats_, GET_UPDATES_SINCE_CALLS);
|
||||
if (seq_per_batch_) {
|
||||
return Status::NotSupported(
|
||||
"This API is not yet compatible with write-prepared/write-unprepared "
|
||||
"transactions");
|
||||
}
|
||||
if (seq > versions_->LastSequence()) {
|
||||
return Status::NotFound("Requested sequence not yet written in the db");
|
||||
}
|
||||
@@ -5089,6 +5084,7 @@ Status DBImpl::VerifyChecksumInternal(const ReadOptions& read_options,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: simplify using GetRefedColumnFamilySet?
|
||||
std::vector<ColumnFamilyData*> cfd_list;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
@@ -2144,7 +2144,7 @@ class DBImpl : public DB {
|
||||
// only during recovery. Using this feature enables not writing the state to
|
||||
// memtable on normal writes and hence improving the throughput. Each new
|
||||
// write of the state will replace the previous state entirely even if the
|
||||
// keys in the two consecuitive states do not overlap.
|
||||
// keys in the two consecutive states do not overlap.
|
||||
// It is protected by log_write_mutex_ when two_write_queues_ is enabled.
|
||||
// Otherwise only the heaad of write_thread_ can access it.
|
||||
WriteBatch cached_recoverable_state_;
|
||||
@@ -2369,11 +2369,6 @@ class DBImpl : public DB {
|
||||
// DB::Open() or passed to us
|
||||
bool own_sfm_;
|
||||
|
||||
// Default value is 0 which means ALL deletes are
|
||||
// preserved. Note that this has no effect if preserve_deletes is false.
|
||||
const std::atomic<SequenceNumber> preserve_deletes_seqnum_{0};
|
||||
const bool preserve_deletes_ = false;
|
||||
|
||||
// Flag to check whether Close() has been called on this DB
|
||||
bool closed_;
|
||||
// save the closing status, for re-calling the close()
|
||||
|
||||
@@ -1363,11 +1363,11 @@ Status DBImpl::CompactFilesImpl(
|
||||
CompactionJob compaction_job(
|
||||
job_context->job_id, c.get(), immutable_db_options_, mutable_db_options_,
|
||||
file_options_for_compaction_, versions_.get(), &shutting_down_,
|
||||
preserve_deletes_seqnum_.load(), log_buffer, directories_.GetDbDir(),
|
||||
log_buffer, directories_.GetDbDir(),
|
||||
GetDataDir(c->column_family_data(), c->output_path_id()),
|
||||
GetDataDir(c->column_family_data(), 0), stats_, &mutex_, &error_handler_,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
table_cache_, &event_logger_,
|
||||
job_context, table_cache_, &event_logger_,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, Env::Priority::USER, io_tracer_,
|
||||
@@ -2973,8 +2973,6 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
bg_bottom_compaction_scheduled_--;
|
||||
}
|
||||
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
|
||||
// See if there's more work to be done
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
|
||||
@@ -3359,12 +3357,11 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
CompactionJob compaction_job(
|
||||
job_context->job_id, c.get(), immutable_db_options_,
|
||||
mutable_db_options_, file_options_for_compaction_, versions_.get(),
|
||||
&shutting_down_, preserve_deletes_seqnum_.load(), log_buffer,
|
||||
directories_.GetDbDir(),
|
||||
&shutting_down_, log_buffer, directories_.GetDbDir(),
|
||||
GetDataDir(c->column_family_data(), c->output_path_id()),
|
||||
GetDataDir(c->column_family_data(), 0), stats_, &mutex_,
|
||||
&error_handler_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, table_cache_, &event_logger_,
|
||||
snapshot_checker, job_context, table_cache_, &event_logger_,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, thread_pri, io_tracer_,
|
||||
|
||||
@@ -23,11 +23,7 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
uint64_t DBImpl::MinLogNumberToKeep() {
|
||||
if (allow_2pc()) {
|
||||
return versions_->min_log_number_to_keep_2pc();
|
||||
} else {
|
||||
return versions_->MinLogNumberWithUnflushedData();
|
||||
}
|
||||
return versions_->min_log_number_to_keep();
|
||||
}
|
||||
|
||||
uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
|
||||
@@ -224,7 +220,6 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
}
|
||||
|
||||
// Add log files in wal_dir
|
||||
|
||||
if (!immutable_db_options_.IsWalDirSameAsDBPath(dbname_)) {
|
||||
std::vector<std::string> log_files;
|
||||
Status s = env_->GetChildren(immutable_db_options_.wal_dir, &log_files);
|
||||
@@ -234,6 +229,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
log_file, immutable_db_options_.wal_dir);
|
||||
}
|
||||
}
|
||||
|
||||
// Add info log files in db_log_dir
|
||||
if (!immutable_db_options_.db_log_dir.empty() &&
|
||||
immutable_db_options_.db_log_dir != dbname_) {
|
||||
|
||||
@@ -866,6 +866,11 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
bool flushed = false;
|
||||
uint64_t corrupted_wal_number = kMaxSequenceNumber;
|
||||
uint64_t min_wal_number = MinLogNumberToKeep();
|
||||
if (!allow_2pc()) {
|
||||
// In non-2pc mode, we skip WALs that do not back unflushed data.
|
||||
min_wal_number =
|
||||
std::max(min_wal_number, versions_->MinLogNumberWithUnflushedData());
|
||||
}
|
||||
for (auto wal_number : wal_numbers) {
|
||||
if (wal_number < min_wal_number) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
@@ -1270,9 +1275,16 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
}
|
||||
|
||||
std::unique_ptr<VersionEdit> wal_deletion;
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest) {
|
||||
wal_deletion.reset(new VersionEdit);
|
||||
wal_deletion->DeleteWalsBefore(max_wal_number + 1);
|
||||
if (flushed) {
|
||||
wal_deletion = std::make_unique<VersionEdit>();
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest) {
|
||||
wal_deletion->DeleteWalsBefore(max_wal_number + 1);
|
||||
}
|
||||
if (!allow_2pc()) {
|
||||
// In non-2pc mode, flushing the memtables of the column families
|
||||
// means we can advance min_log_number_to_keep.
|
||||
wal_deletion->SetMinLogNumberToKeep(max_wal_number + 1);
|
||||
}
|
||||
edit_lists.back().push_back(wal_deletion.get());
|
||||
}
|
||||
|
||||
@@ -1351,7 +1363,14 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& wal_numbers) {
|
||||
// FindObsoleteFiles()
|
||||
total_log_size_ = 0;
|
||||
log_empty_ = false;
|
||||
uint64_t min_wal_with_unflushed_data =
|
||||
versions_->MinLogNumberWithUnflushedData();
|
||||
for (auto wal_number : wal_numbers) {
|
||||
if (!allow_2pc() && wal_number < min_wal_with_unflushed_data) {
|
||||
// In non-2pc mode, the WAL files not backing unflushed data are not
|
||||
// alive, thus should not be added to the alive_log_files_.
|
||||
continue;
|
||||
}
|
||||
// We preallocate space for wals, but then after a crash and restart, those
|
||||
// preallocated space are not needed anymore. It is likely only the last
|
||||
// log has such preallocated space, so we only truncate for the last log.
|
||||
@@ -1446,9 +1465,9 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
dbname_, versions_.get(), immutable_db_options_, tboptions,
|
||||
file_options_for_compaction_, cfd->table_cache(), iter.get(),
|
||||
std::move(range_del_iters), &meta, &blob_file_additions,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
paranoid_file_checks, cfd->internal_stats(), &io_s, io_tracer_,
|
||||
BlobFileCreationReason::kRecovery, &event_logger_, job_id,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, kMaxSequenceNumber,
|
||||
snapshot_checker, paranoid_file_checks, cfd->internal_stats(), &io_s,
|
||||
io_tracer_, BlobFileCreationReason::kRecovery, &event_logger_, job_id,
|
||||
Env::IO_HIGH, nullptr /* table_properties */, write_hint,
|
||||
nullptr /*full_history_ts_low*/, &blob_callback_);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
@@ -1952,10 +1971,10 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
"DB::Open() failed --- Unable to persist Options file",
|
||||
persist_options_status.ToString());
|
||||
}
|
||||
} else {
|
||||
}
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(impl->immutable_db_options_.info_log,
|
||||
"Persisting Option File error: %s",
|
||||
persist_options_status.ToString().c_str());
|
||||
"DB::Open() failed: %s", s.ToString().c_str());
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = impl->StartPeriodicWorkScheduler();
|
||||
|
||||
@@ -710,8 +710,11 @@ Status DB::OpenAsSecondary(
|
||||
}
|
||||
|
||||
Status DBImplSecondary::CompactWithoutInstallation(
|
||||
ColumnFamilyHandle* cfh, const CompactionServiceInput& input,
|
||||
CompactionServiceResult* result) {
|
||||
const OpenAndCompactOptions& options, ColumnFamilyHandle* cfh,
|
||||
const CompactionServiceInput& input, CompactionServiceResult* result) {
|
||||
if (options.canceled && options.canceled->load(std::memory_order_acquire)) {
|
||||
return Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
auto cfd = static_cast_with_check<ColumnFamilyHandleImpl>(cfh)->cfd();
|
||||
if (!cfd) {
|
||||
@@ -773,7 +776,7 @@ Status DBImplSecondary::CompactWithoutInstallation(
|
||||
file_options_for_compaction_, versions_.get(), &shutting_down_,
|
||||
&log_buffer, output_dir.get(), stats_, &mutex_, &error_handler_,
|
||||
input.snapshots, table_cache_, &event_logger_, dbname_, io_tracer_,
|
||||
db_id_, db_session_id_, secondary_path_, input, result);
|
||||
options.canceled, db_id_, db_session_id_, secondary_path_, input, result);
|
||||
|
||||
mutex_.Unlock();
|
||||
s = compaction_job.Run();
|
||||
@@ -792,9 +795,13 @@ Status DBImplSecondary::CompactWithoutInstallation(
|
||||
}
|
||||
|
||||
Status DB::OpenAndCompact(
|
||||
const std::string& name, const std::string& output_directory,
|
||||
const std::string& input, std::string* result,
|
||||
const OpenAndCompactOptions& options, const std::string& name,
|
||||
const std::string& output_directory, const std::string& input,
|
||||
std::string* output,
|
||||
const CompactionServiceOptionsOverride& override_options) {
|
||||
if (options.canceled && options.canceled->load(std::memory_order_acquire)) {
|
||||
return Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
CompactionServiceInput compaction_input;
|
||||
Status s = CompactionServiceInput::Read(input, &compaction_input);
|
||||
if (!s.ok()) {
|
||||
@@ -824,6 +831,7 @@ Status DB::OpenAndCompact(
|
||||
override_options.table_factory;
|
||||
compaction_input.column_family.options.sst_partitioner_factory =
|
||||
override_options.sst_partitioner_factory;
|
||||
compaction_input.db_options.listeners = override_options.listeners;
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.push_back(compaction_input.column_family);
|
||||
@@ -847,10 +855,10 @@ Status DB::OpenAndCompact(
|
||||
CompactionServiceResult compaction_result;
|
||||
DBImplSecondary* db_secondary = static_cast_with_check<DBImplSecondary>(db);
|
||||
assert(handles.size() > 0);
|
||||
s = db_secondary->CompactWithoutInstallation(handles[0], compaction_input,
|
||||
&compaction_result);
|
||||
s = db_secondary->CompactWithoutInstallation(
|
||||
options, handles[0], compaction_input, &compaction_result);
|
||||
|
||||
Status serialization_status = compaction_result.Write(result);
|
||||
Status serialization_status = compaction_result.Write(output);
|
||||
|
||||
for (auto& handle : handles) {
|
||||
delete handle;
|
||||
@@ -862,6 +870,14 @@ Status DB::OpenAndCompact(
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DB::OpenAndCompact(
|
||||
const std::string& name, const std::string& output_directory,
|
||||
const std::string& input, std::string* output,
|
||||
const CompactionServiceOptionsOverride& override_options) {
|
||||
return OpenAndCompact(OpenAndCompactOptions(), name, output_directory, input,
|
||||
output, override_options);
|
||||
}
|
||||
|
||||
#else // !ROCKSDB_LITE
|
||||
|
||||
Status DB::OpenAsSecondary(const Options& /*options*/,
|
||||
|
||||
@@ -228,10 +228,11 @@ class DBImplSecondary : public DBImpl {
|
||||
Status CheckConsistency() override;
|
||||
|
||||
#ifndef NDEBUG
|
||||
Status TEST_CompactWithoutInstallation(ColumnFamilyHandle* cfh,
|
||||
Status TEST_CompactWithoutInstallation(const OpenAndCompactOptions& options,
|
||||
ColumnFamilyHandle* cfh,
|
||||
const CompactionServiceInput& input,
|
||||
CompactionServiceResult* result) {
|
||||
return CompactWithoutInstallation(cfh, input, result);
|
||||
return CompactWithoutInstallation(options, cfh, input, result);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
@@ -346,7 +347,8 @@ class DBImplSecondary : public DBImpl {
|
||||
// Run compaction without installation, the output files will be placed in the
|
||||
// secondary DB path. The LSM tree won't be changed, the secondary DB is still
|
||||
// in read-only mode.
|
||||
Status CompactWithoutInstallation(ColumnFamilyHandle* cfh,
|
||||
Status CompactWithoutInstallation(const OpenAndCompactOptions& options,
|
||||
ColumnFamilyHandle* cfh,
|
||||
const CompactionServiceInput& input,
|
||||
CompactionServiceResult* result);
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
PreReleaseCallback* pre_release_callback) {
|
||||
assert(!seq_per_batch_ || batch_cnt != 0);
|
||||
if (my_batch == nullptr) {
|
||||
return Status::Corruption("Batch is nullptr!");
|
||||
return Status::InvalidArgument("Batch is nullptr!");
|
||||
} else if (!disable_memtable &&
|
||||
WriteBatchInternal::TimestampsUpdateNeeded(*my_batch)) {
|
||||
// If writing to memtable, then we require the caller to set/update the
|
||||
@@ -1882,9 +1882,11 @@ void DBImpl::NotifyOnMemTableSealed(ColumnFamilyData* /*cfd*/,
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_.Unlock();
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnMemTableSealed(mem_table_info);
|
||||
}
|
||||
mutex_.Lock();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -2085,11 +2087,9 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
mutable_cf_options);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
mutex_.Unlock();
|
||||
// Notify client that memtable is sealed, now that we have successfully
|
||||
// installed a new memtable
|
||||
NotifyOnMemTableSealed(cfd, memtable_info);
|
||||
mutex_.Lock();
|
||||
#endif // ROCKSDB_LITE
|
||||
// It is possible that we got here without checking the value of i_os, but
|
||||
// that is okay. If we did, it most likely means that s was already an error.
|
||||
|
||||
+2
-42
@@ -78,7 +78,6 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
range_del_agg_(&ioptions.internal_comparator, s),
|
||||
db_impl_(db_impl),
|
||||
cfd_(cfd),
|
||||
start_seqnum_(0ULL),
|
||||
timestamp_ub_(read_options.timestamp),
|
||||
timestamp_lb_(read_options.iter_start_ts),
|
||||
timestamp_size_(timestamp_ub_ ? timestamp_ub_->size() : 0) {
|
||||
@@ -328,25 +327,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
case kTypeSingleDeletion:
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
// they are hidden by this deletion.
|
||||
// if iterartor specified start_seqnum we
|
||||
// 1) return internal key, including the type
|
||||
// 2) return ikey only if ikey.seqnum >= start_seqnum_
|
||||
// note that if deletion seqnum is < start_seqnum_ we
|
||||
// just skip it like in normal iterator.
|
||||
if (start_seqnum_ > 0) {
|
||||
if (ikey_.sequence >= start_seqnum_) {
|
||||
saved_key_.SetInternalKey(ikey_);
|
||||
valid_ = true;
|
||||
return true;
|
||||
} else {
|
||||
saved_key_.SetUserKey(
|
||||
ikey_.user_key,
|
||||
!pin_thru_lifetime_ ||
|
||||
!iter_.iter()->IsKeyPinned() /* copy */);
|
||||
skipping_saved_key = true;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
}
|
||||
} else if (timestamp_lb_) {
|
||||
if (timestamp_lb_) {
|
||||
saved_key_.SetInternalKey(ikey_);
|
||||
valid_ = true;
|
||||
return true;
|
||||
@@ -360,28 +341,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
break;
|
||||
case kTypeValue:
|
||||
case kTypeBlobIndex:
|
||||
if (start_seqnum_ > 0) {
|
||||
if (ikey_.sequence >= start_seqnum_) {
|
||||
saved_key_.SetInternalKey(ikey_);
|
||||
|
||||
if (ikey_.type == kTypeBlobIndex) {
|
||||
if (!SetBlobValueIfNeeded(ikey_.user_key, iter_.value())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
return true;
|
||||
} else {
|
||||
// this key and all previous versions shouldn't be included,
|
||||
// skipping_saved_key
|
||||
saved_key_.SetUserKey(
|
||||
ikey_.user_key,
|
||||
!pin_thru_lifetime_ ||
|
||||
!iter_.iter()->IsKeyPinned() /* copy */);
|
||||
skipping_saved_key = true;
|
||||
}
|
||||
} else if (timestamp_lb_) {
|
||||
if (timestamp_lb_) {
|
||||
saved_key_.SetInternalKey(ikey_);
|
||||
|
||||
if (ikey_.type == kTypeBlobIndex) {
|
||||
|
||||
+1
-4
@@ -151,7 +151,7 @@ class DBIter final : public Iterator {
|
||||
}
|
||||
Slice key() const override {
|
||||
assert(valid_);
|
||||
if (start_seqnum_ > 0 || timestamp_lb_) {
|
||||
if (timestamp_lb_) {
|
||||
return saved_key_.GetInternalKey();
|
||||
} else {
|
||||
const Slice ukey_and_ts = saved_key_.GetUserKey();
|
||||
@@ -371,9 +371,6 @@ class DBIter final : public Iterator {
|
||||
ROCKSDB_FIELD_UNUSED
|
||||
#endif
|
||||
ColumnFamilyData* cfd_;
|
||||
// for diff snapshots we want the lower bound on the seqnum;
|
||||
// if this value > 0 iterator will return internal keys
|
||||
SequenceNumber start_seqnum_;
|
||||
const Slice* const timestamp_ub_;
|
||||
const Slice* const timestamp_lb_;
|
||||
const size_t timestamp_size_;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
@@ -54,7 +55,7 @@ class DbKvChecksumTest
|
||||
case WriteBatchOpType::kMerge:
|
||||
s = wb.Merge(cf_handle, "key", "val");
|
||||
break;
|
||||
case WriteBatchOpType::kBlobIndex:
|
||||
case WriteBatchOpType::kBlobIndex: {
|
||||
// TODO(ajkr): use public API once available.
|
||||
uint32_t cf_id;
|
||||
if (cf_handle == nullptr) {
|
||||
@@ -62,8 +63,14 @@ class DbKvChecksumTest
|
||||
} else {
|
||||
cf_id = cf_handle->GetID();
|
||||
}
|
||||
s = WriteBatchInternal::PutBlobIndex(&wb, cf_id, "key", "val");
|
||||
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeInlinedTTL(&blob_index, /* expiration */ 9876543210,
|
||||
"val");
|
||||
|
||||
s = WriteBatchInternal::PutBlobIndex(&wb, cf_id, "key", blob_index);
|
||||
break;
|
||||
}
|
||||
case WriteBatchOpType::kNum:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class DBMergeOperandTest : public DBTestBase {
|
||||
: DBTestBase("db_merge_operand_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBMergeOperandTest, MergeOperandReadAfterFreeBug) {
|
||||
TEST_F(DBMergeOperandTest, CacheEvictedMergeOperandReadAfterFreeBug) {
|
||||
// There was a bug of reading merge operands after they are mistakely freed
|
||||
// in DB::GetMergeOperands, which is surfaced by cache full.
|
||||
// See PR#9507 for more.
|
||||
@@ -86,6 +86,42 @@ TEST_F(DBMergeOperandTest, MergeOperandReadAfterFreeBug) {
|
||||
ASSERT_EQ(values[3].ToString(), "v4");
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, FlushedMergeOperandReadAfterFreeBug) {
|
||||
// Repro for a bug where a memtable containing a merge operand could be
|
||||
// deleted before the merge operand was saved to the result.
|
||||
auto options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Merge("key", "value"));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::GetImpl:PostMemTableGet:0",
|
||||
"DBMergeOperandTest::FlushedMergeOperandReadAfterFreeBug:PreFlush"},
|
||||
{"DBMergeOperandTest::FlushedMergeOperandReadAfterFreeBug:PostFlush",
|
||||
"DBImpl::GetImpl:PostMemTableGet:1"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
auto flush_thread = port::Thread([&]() {
|
||||
TEST_SYNC_POINT(
|
||||
"DBMergeOperandTest::FlushedMergeOperandReadAfterFreeBug:PreFlush");
|
||||
ASSERT_OK(Flush());
|
||||
TEST_SYNC_POINT(
|
||||
"DBMergeOperandTest::FlushedMergeOperandReadAfterFreeBug:PostFlush");
|
||||
});
|
||||
|
||||
PinnableSlice value;
|
||||
GetMergeOperandsOptions merge_operands_info;
|
||||
merge_operands_info.expected_max_number_of_operands = 1;
|
||||
int number_of_operands;
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"key", &value, &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(1, number_of_operands);
|
||||
|
||||
flush_thread.join();
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
|
||||
@@ -13,12 +13,17 @@
|
||||
#include <string>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/perf_level.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/block_based/block.h"
|
||||
#include "table/format.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "test_util/mock_time_env.h"
|
||||
#include "util/random.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -1992,6 +1997,121 @@ TEST_F(DBPropertiesTest, GetMapPropertyDbStats) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, GetMapPropertyBlockCacheEntryStats) {
|
||||
// Currently only verifies the expected properties are present
|
||||
std::map<std::string, std::string> values;
|
||||
ASSERT_TRUE(
|
||||
db_->GetMapProperty(DB::Properties::kBlockCacheEntryStats, &values));
|
||||
|
||||
ASSERT_TRUE(values.find(BlockCacheEntryStatsMapKeys::CacheId()) !=
|
||||
values.end());
|
||||
ASSERT_TRUE(values.find(BlockCacheEntryStatsMapKeys::CacheCapacityBytes()) !=
|
||||
values.end());
|
||||
ASSERT_TRUE(
|
||||
values.find(
|
||||
BlockCacheEntryStatsMapKeys::LastCollectionDurationSeconds()) !=
|
||||
values.end());
|
||||
ASSERT_TRUE(
|
||||
values.find(BlockCacheEntryStatsMapKeys::LastCollectionAgeSeconds()) !=
|
||||
values.end());
|
||||
for (size_t i = 0; i < kNumCacheEntryRoles; ++i) {
|
||||
CacheEntryRole role = static_cast<CacheEntryRole>(i);
|
||||
ASSERT_TRUE(values.find(BlockCacheEntryStatsMapKeys::EntryCount(role)) !=
|
||||
values.end());
|
||||
ASSERT_TRUE(values.find(BlockCacheEntryStatsMapKeys::UsedBytes(role)) !=
|
||||
values.end());
|
||||
ASSERT_TRUE(values.find(BlockCacheEntryStatsMapKeys::UsedPercent(role)) !=
|
||||
values.end());
|
||||
}
|
||||
|
||||
// There should be no extra values in the map.
|
||||
ASSERT_EQ(3 * kNumCacheEntryRoles + 4, values.size());
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string PopMetaIndexKey(InternalIterator* meta_iter) {
|
||||
Status s = meta_iter->status();
|
||||
if (!s.ok()) {
|
||||
return s.ToString();
|
||||
} else if (meta_iter->Valid()) {
|
||||
std::string rv = meta_iter->key().ToString();
|
||||
meta_iter->Next();
|
||||
return rv;
|
||||
} else {
|
||||
return "NOT_FOUND";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(DBPropertiesTest, TableMetaIndexKeys) {
|
||||
// This is to detect unexpected churn in metaindex block keys. This is more
|
||||
// of a "table test" but table_test.cc doesn't depend on db_test_util.h and
|
||||
// we need ChangeOptions() for broad coverage.
|
||||
constexpr int kKeyCount = 100;
|
||||
do {
|
||||
Options options;
|
||||
options = CurrentOptions(options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Create an SST file
|
||||
for (int key = 0; key < kKeyCount; key++) {
|
||||
ASSERT_OK(Put(Key(key), "val"));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Find its file number
|
||||
std::vector<LiveFileMetaData> files;
|
||||
db_->GetLiveFilesMetaData(&files);
|
||||
// 1 SST file
|
||||
ASSERT_EQ(1, files.size());
|
||||
|
||||
// Open it for inspection
|
||||
std::string sst_file =
|
||||
files[0].directory + "/" + files[0].relative_filename;
|
||||
std::unique_ptr<FSRandomAccessFile> f;
|
||||
ASSERT_OK(env_->GetFileSystem()->NewRandomAccessFile(
|
||||
sst_file, FileOptions(), &f, nullptr));
|
||||
std::unique_ptr<RandomAccessFileReader> r;
|
||||
r.reset(new RandomAccessFileReader(std::move(f), sst_file));
|
||||
uint64_t file_size = 0;
|
||||
ASSERT_OK(env_->GetFileSize(sst_file, &file_size));
|
||||
|
||||
// Read metaindex
|
||||
BlockContents bc;
|
||||
ASSERT_OK(ReadMetaIndexBlockInFile(r.get(), file_size, 0U,
|
||||
ImmutableOptions(options), &bc));
|
||||
Block metaindex_block(std::move(bc));
|
||||
std::unique_ptr<InternalIterator> meta_iter;
|
||||
meta_iter.reset(metaindex_block.NewMetaIterator());
|
||||
meta_iter->SeekToFirst();
|
||||
|
||||
if (strcmp(options.table_factory->Name(),
|
||||
TableFactory::kBlockBasedTableName()) == 0) {
|
||||
auto bbto = options.table_factory->GetOptions<BlockBasedTableOptions>();
|
||||
if (bbto->filter_policy) {
|
||||
if (bbto->partition_filters) {
|
||||
// The key names are intentionally hard-coded here to detect
|
||||
// accidental regression on compatibility.
|
||||
EXPECT_EQ("partitionedfilter.rocksdb.BuiltinBloomFilter",
|
||||
PopMetaIndexKey(meta_iter.get()));
|
||||
} else {
|
||||
EXPECT_EQ("fullfilter.rocksdb.BuiltinBloomFilter",
|
||||
PopMetaIndexKey(meta_iter.get()));
|
||||
}
|
||||
}
|
||||
if (bbto->index_type == BlockBasedTableOptions::kHashSearch) {
|
||||
EXPECT_EQ("rocksdb.hashindex.metadata",
|
||||
PopMetaIndexKey(meta_iter.get()));
|
||||
EXPECT_EQ("rocksdb.hashindex.prefixes",
|
||||
PopMetaIndexKey(meta_iter.get()));
|
||||
}
|
||||
}
|
||||
EXPECT_EQ("rocksdb.properties", PopMetaIndexKey(meta_iter.get()));
|
||||
EXPECT_EQ("NOT_FOUND", PopMetaIndexKey(meta_iter.get()));
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+16
-16
@@ -188,8 +188,8 @@ TEST_F(DBSecondaryTest, SimpleInternalCompaction) {
|
||||
auto cfh = db_secondary_->DefaultColumnFamily();
|
||||
|
||||
CompactionServiceResult result;
|
||||
ASSERT_OK(db_secondary_full()->TEST_CompactWithoutInstallation(cfh, input,
|
||||
&result));
|
||||
ASSERT_OK(db_secondary_full()->TEST_CompactWithoutInstallation(
|
||||
OpenAndCompactOptions(), cfh, input, &result));
|
||||
|
||||
ASSERT_EQ(result.output_files.size(), 1);
|
||||
InternalKey smallest, largest;
|
||||
@@ -248,8 +248,8 @@ TEST_F(DBSecondaryTest, InternalCompactionMultiLevels) {
|
||||
OpenSecondary(options);
|
||||
auto cfh = db_secondary_->DefaultColumnFamily();
|
||||
CompactionServiceResult result;
|
||||
ASSERT_OK(db_secondary_full()->TEST_CompactWithoutInstallation(cfh, input1,
|
||||
&result));
|
||||
ASSERT_OK(db_secondary_full()->TEST_CompactWithoutInstallation(
|
||||
OpenAndCompactOptions(), cfh, input1, &result));
|
||||
ASSERT_OK(result.status);
|
||||
|
||||
// pick 2 files on level 1 for compaction, which has 6 overlap files on L2
|
||||
@@ -261,8 +261,8 @@ TEST_F(DBSecondaryTest, InternalCompactionMultiLevels) {
|
||||
}
|
||||
|
||||
input2.output_level = 2;
|
||||
ASSERT_OK(db_secondary_full()->TEST_CompactWithoutInstallation(cfh, input2,
|
||||
&result));
|
||||
ASSERT_OK(db_secondary_full()->TEST_CompactWithoutInstallation(
|
||||
OpenAndCompactOptions(), cfh, input2, &result));
|
||||
ASSERT_OK(result.status);
|
||||
|
||||
CloseSecondary();
|
||||
@@ -273,15 +273,15 @@ TEST_F(DBSecondaryTest, InternalCompactionMultiLevels) {
|
||||
}
|
||||
OpenSecondary(options);
|
||||
cfh = db_secondary_->DefaultColumnFamily();
|
||||
Status s = db_secondary_full()->TEST_CompactWithoutInstallation(cfh, input2,
|
||||
&result);
|
||||
Status s = db_secondary_full()->TEST_CompactWithoutInstallation(
|
||||
OpenAndCompactOptions(), cfh, input2, &result);
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
ASSERT_OK(result.status);
|
||||
|
||||
// TODO: L0 -> L1 compaction should success, currently version is not built
|
||||
// if files is missing.
|
||||
// ASSERT_OK(db_secondary_full()->TEST_CompactWithoutInstallation(cfh,
|
||||
// input1, &result));
|
||||
// ASSERT_OK(db_secondary_full()->TEST_CompactWithoutInstallation(OpenAndCompactOptions(),
|
||||
// cfh, input1, &result));
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, InternalCompactionCompactedFiles) {
|
||||
@@ -319,8 +319,8 @@ TEST_F(DBSecondaryTest, InternalCompactionCompactedFiles) {
|
||||
auto cfh = db_secondary_->DefaultColumnFamily();
|
||||
|
||||
CompactionServiceResult result;
|
||||
Status s =
|
||||
db_secondary_full()->TEST_CompactWithoutInstallation(cfh, input, &result);
|
||||
Status s = db_secondary_full()->TEST_CompactWithoutInstallation(
|
||||
OpenAndCompactOptions(), cfh, input, &result);
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
ASSERT_OK(result.status);
|
||||
}
|
||||
@@ -356,15 +356,15 @@ TEST_F(DBSecondaryTest, InternalCompactionMissingFiles) {
|
||||
auto cfh = db_secondary_->DefaultColumnFamily();
|
||||
|
||||
CompactionServiceResult result;
|
||||
Status s =
|
||||
db_secondary_full()->TEST_CompactWithoutInstallation(cfh, input, &result);
|
||||
Status s = db_secondary_full()->TEST_CompactWithoutInstallation(
|
||||
OpenAndCompactOptions(), cfh, input, &result);
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
ASSERT_OK(result.status);
|
||||
|
||||
input.input_files.erase(input.input_files.begin());
|
||||
|
||||
ASSERT_OK(db_secondary_full()->TEST_CompactWithoutInstallation(cfh, input,
|
||||
&result));
|
||||
ASSERT_OK(db_secondary_full()->TEST_CompactWithoutInstallation(
|
||||
OpenAndCompactOptions(), cfh, input, &result));
|
||||
ASSERT_OK(result.status);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/sst_file_manager.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "util/random.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -1393,6 +1395,69 @@ TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFiles) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFilesSubjectToMemoryLimit) {
|
||||
for (bool reserve_table_builder_memory : {true, false}) {
|
||||
// Open DB with infinite max open files
|
||||
// - First iteration use 1 thread to open files
|
||||
// - Second iteration use 5 threads to open files
|
||||
for (int iter = 0; iter < 2; iter++) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = 100000;
|
||||
options.disable_auto_compactions = true;
|
||||
options.max_open_files = -1;
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
if (iter == 0) {
|
||||
options.max_file_opening_threads = 1;
|
||||
} else {
|
||||
options.max_file_opening_threads = 5;
|
||||
}
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Create 5 Files in L0 (then move then to L2)
|
||||
for (int i = 0; i < 5; i++) {
|
||||
std::string k = "L2_" + Key(i);
|
||||
ASSERT_OK(Put(k, k + std::string(1000, 'a')));
|
||||
ASSERT_OK(Flush()) << i;
|
||||
}
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.change_level = true;
|
||||
compact_options.target_level = 2;
|
||||
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
|
||||
|
||||
// Create 5 Files in L0
|
||||
for (int i = 0; i < 5; i++) {
|
||||
std::string k = "L0_" + Key(i);
|
||||
ASSERT_OK(Put(k, k + std::string(1000, 'a')));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
Close();
|
||||
|
||||
table_options.reserve_table_reader_memory = reserve_table_builder_memory;
|
||||
table_options.block_cache =
|
||||
NewLRUCache(1024 /* capacity */, 0 /* num_shard_bits */,
|
||||
true /* strict_capacity_limit */);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
// Reopening the DB will try to load all existing files, conditionally
|
||||
// subject to memory limit
|
||||
Status s = TryReopen(options);
|
||||
if (table_options.reserve_table_reader_memory) {
|
||||
EXPECT_TRUE(s.IsMemoryLimit());
|
||||
EXPECT_TRUE(s.ToString().find("memory limit based on cache capacity") !=
|
||||
std::string::npos);
|
||||
|
||||
} else {
|
||||
EXPECT_TRUE(s.ok());
|
||||
ASSERT_EQ("5,0,5", FilesPerLevel(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, GetTotalSstFilesSize) {
|
||||
// We don't propagate oldest-key-time table property on compaction and
|
||||
// just write 0 as default value. This affect the exact table size, since
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
@@ -18,6 +19,7 @@
|
||||
#include "rocksdb/utilities/table_properties_collectors.h"
|
||||
#include "table/format.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "table/table_properties_internal.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/random.h"
|
||||
|
||||
@@ -1078,6 +1078,11 @@ void CheckColumnFamilyMeta(
|
||||
ASSERT_LE(file_meta_from_cf.file_creation_time, end_time);
|
||||
ASSERT_GE(file_meta_from_cf.oldest_ancester_time, start_time);
|
||||
ASSERT_LE(file_meta_from_cf.oldest_ancester_time, end_time);
|
||||
// More from FileStorageInfo
|
||||
ASSERT_EQ(file_meta_from_cf.file_type, kTableFile);
|
||||
ASSERT_EQ(file_meta_from_cf.name,
|
||||
"/" + file_meta_from_cf.relative_filename);
|
||||
ASSERT_EQ(file_meta_from_cf.directory, file_meta_from_cf.db_path);
|
||||
}
|
||||
|
||||
ASSERT_EQ(level_meta_from_cf.size, level_size);
|
||||
@@ -1122,6 +1127,11 @@ void CheckLiveFilesMeta(
|
||||
ASSERT_EQ(meta.oldest_blob_file_number,
|
||||
expected_meta.oldest_blob_file_number);
|
||||
|
||||
// More from FileStorageInfo
|
||||
ASSERT_EQ(meta.file_type, kTableFile);
|
||||
ASSERT_EQ(meta.name, "/" + meta.relative_filename);
|
||||
ASSERT_EQ(meta.directory, meta.db_path);
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -894,8 +894,8 @@ class CacheWrapper : public Cache {
|
||||
bool Ref(Handle* handle) override { return target_->Ref(handle); }
|
||||
|
||||
using Cache::Release;
|
||||
bool Release(Handle* handle, bool force_erase = false) override {
|
||||
return target_->Release(handle, force_erase);
|
||||
bool Release(Handle* handle, bool erase_if_last_ref = false) override {
|
||||
return target_->Release(handle, erase_if_last_ref);
|
||||
}
|
||||
|
||||
void* Value(Handle* handle) override { return target_->Value(handle); }
|
||||
|
||||
+87
-1
@@ -287,7 +287,6 @@ TEST_F(DBWALTest, Recover) {
|
||||
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
|
||||
ASSERT_EQ("v1", Get(1, "foo"));
|
||||
|
||||
ASSERT_EQ("v1", Get(1, "foo"));
|
||||
ASSERT_EQ("v5", Get(1, "baz"));
|
||||
ASSERT_OK(Put(1, "bar", "v2"));
|
||||
@@ -1491,6 +1490,93 @@ TEST_F(DBWALTest, kPointInTimeRecoveryCFConsistency) {
|
||||
ASSERT_NOK(TryReopenWithColumnFamilies({"default", "one", "two"}, options));
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RaceInstallFlushResultsWithWalObsoletion) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
// The following make sure there are two bg flush threads.
|
||||
options.max_background_jobs = 8;
|
||||
|
||||
const std::string cf1_name("cf1");
|
||||
CreateAndReopenWithCF({cf1_name}, options);
|
||||
assert(handles_.size() == 2);
|
||||
|
||||
{
|
||||
dbfull()->TEST_LockMutex();
|
||||
ASSERT_LE(2, dbfull()->GetBGJobLimits().max_flushes);
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->PauseBackgroundWork());
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles_[1], "foo", "value"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "value"));
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable(false, true, handles_[1]));
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "value"));
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable(false, true, handles_[0]));
|
||||
|
||||
bool called = false;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
// This callback will be called when the first bg flush thread reaches the
|
||||
// point before entering the MANIFEST write queue after flushing the SST
|
||||
// file.
|
||||
// The purpose of the sync points here is to ensure both bg flush threads
|
||||
// finish computing `min_wal_number_to_keep` before any of them updates the
|
||||
// `log_number` for the column family that's being flushed.
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"MemTableList::TryInstallMemtableFlushResults:AfterComputeMinWalToKeep",
|
||||
[&](void* /*arg*/) {
|
||||
dbfull()->mutex()->AssertHeld();
|
||||
if (!called) {
|
||||
// We are the first bg flush thread in the MANIFEST write queue.
|
||||
// We set up the dependency between sync points for two threads that
|
||||
// will be executing the same code.
|
||||
// For the interleaving of events, see
|
||||
// https://github.com/facebook/rocksdb/pull/9715.
|
||||
// bg flush thread1 will release the db mutex while in the MANIFEST
|
||||
// write queue. In the meantime, bg flush thread2 locks db mutex and
|
||||
// computes the min_wal_number_to_keep (before thread1 writes to
|
||||
// MANIFEST thus before cf1->log_number is updated). Bg thread2 joins
|
||||
// the MANIFEST write queue afterwards and bg flush thread1 proceeds
|
||||
// with writing to MANIFEST.
|
||||
called = true;
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"VersionSet::LogAndApply:WriteManifestStart",
|
||||
"DBWALTest::RaceInstallFlushResultsWithWalObsoletion:BgFlush2"},
|
||||
{"DBWALTest::RaceInstallFlushResultsWithWalObsoletion:BgFlush2",
|
||||
"VersionSet::LogAndApply:WriteManifest"},
|
||||
});
|
||||
} else {
|
||||
// The other bg flush thread has already been in the MANIFEST write
|
||||
// queue, and we are after.
|
||||
TEST_SYNC_POINT(
|
||||
"DBWALTest::RaceInstallFlushResultsWithWalObsoletion:BgFlush2");
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(dbfull()->ContinueBackgroundWork());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[0]));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[1]));
|
||||
|
||||
ASSERT_TRUE(called);
|
||||
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
DB* db1 = nullptr;
|
||||
Status s = DB::OpenForReadOnly(options, dbname_, &db1);
|
||||
ASSERT_OK(s);
|
||||
assert(db1);
|
||||
delete db1;
|
||||
}
|
||||
|
||||
// Test scope:
|
||||
// - We expect to open data store under all circumstances
|
||||
// - We expect only data upto the point where the first error was encountered
|
||||
|
||||
+3
-2
@@ -95,8 +95,9 @@ void EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
jwriter << "cf_name" << cf_name << "job" << job_id << "event"
|
||||
<< "table_file_creation"
|
||||
<< "file_number" << fd.GetNumber() << "file_size"
|
||||
<< fd.GetFileSize() << "file_checksum" << file_checksum
|
||||
<< "file_checksum_func_name" << file_checksum_func_name;
|
||||
<< fd.GetFileSize() << "file_checksum"
|
||||
<< Slice(file_checksum).ToString(true) << "file_checksum_func_name"
|
||||
<< file_checksum_func_name;
|
||||
|
||||
// table_properties
|
||||
{
|
||||
|
||||
@@ -1796,6 +1796,54 @@ TEST_F(ExternalSSTFileBasicTest, IngestWithTemperature) {
|
||||
ASSERT_EQ(std::atoi(prop.c_str()), 0);
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, FailIfNotBottommostLevel) {
|
||||
Options options = GetDefaultOptions();
|
||||
|
||||
std::string file_path = sst_files_dir_ + ToString(1);
|
||||
SstFileWriter sfw(EnvOptions(), options);
|
||||
|
||||
ASSERT_OK(sfw.Open(file_path));
|
||||
ASSERT_OK(sfw.Put("b", "dontcare"));
|
||||
ASSERT_OK(sfw.Finish());
|
||||
|
||||
// Test universal compaction + ingest with snapshot consistency
|
||||
options.create_if_missing = true;
|
||||
options.compaction_style = CompactionStyle::kCompactionStyleUniversal;
|
||||
DestroyAndReopen(options);
|
||||
{
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ManagedSnapshot snapshot_guard(db_, snapshot);
|
||||
IngestExternalFileOptions ifo;
|
||||
ifo.fail_if_not_bottommost_level = true;
|
||||
ifo.snapshot_consistency = true;
|
||||
const Status s = db_->IngestExternalFile({file_path}, ifo);
|
||||
ASSERT_TRUE(s.IsTryAgain());
|
||||
}
|
||||
|
||||
// Test level compaction
|
||||
options.compaction_style = CompactionStyle::kCompactionStyleLevel;
|
||||
options.num_levels = 2;
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "a", "dontcare"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "c", "dontcare"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "b", "dontcare"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "d", "dontcare"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
|
||||
{
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
|
||||
IngestExternalFileOptions ifo;
|
||||
ifo.fail_if_not_bottommost_level = true;
|
||||
const Status s = db_->IngestExternalFile({file_path}, ifo);
|
||||
ASSERT_TRUE(s.IsTryAgain());
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternalSSTFileBasicTest, ExternalSSTFileBasicTest,
|
||||
testing::Values(std::make_tuple(true, true),
|
||||
std::make_tuple(true, false),
|
||||
|
||||
@@ -739,6 +739,12 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
if (force_global_seqno) {
|
||||
*assigned_seqno = last_seqno + 1;
|
||||
if (compaction_style == kCompactionStyleUniversal || files_overlap_) {
|
||||
if (ingestion_options_.fail_if_not_bottommost_level) {
|
||||
status = Status::TryAgain(
|
||||
"Files cannot be ingested to Lmax. Please make sure key range of "
|
||||
"Lmax does not overlap with files to ingest.");
|
||||
return status;
|
||||
}
|
||||
file_to_ingest->picked_level = 0;
|
||||
return status;
|
||||
}
|
||||
@@ -808,6 +814,15 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
target_level = 0;
|
||||
*assigned_seqno = last_seqno + 1;
|
||||
}
|
||||
|
||||
if (ingestion_options_.fail_if_not_bottommost_level &&
|
||||
target_level < cfd_->NumberLevels() - 1) {
|
||||
status = Status::TryAgain(
|
||||
"Files cannot be ingested to Lmax. Please make sure key range of Lmax "
|
||||
"does not overlap with files to ingest.");
|
||||
return status;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile",
|
||||
&overlap_with_db);
|
||||
|
||||
@@ -171,6 +171,69 @@ TEST_F(FileNameTest, Construction) {
|
||||
ASSERT_EQ(kMetaDatabase, type);
|
||||
}
|
||||
|
||||
TEST_F(FileNameTest, NormalizePath) {
|
||||
// No leading slash
|
||||
const std::string sep = std::string(1, kFilePathSeparator);
|
||||
|
||||
std::string expected = "FOLDER" + sep + "filename.ext";
|
||||
std::string given = "FOLDER" + sep + "filename.ext";
|
||||
|
||||
ASSERT_EQ(expected, NormalizePath(given));
|
||||
|
||||
// Two chars /a
|
||||
|
||||
expected = sep + "a";
|
||||
given = expected;
|
||||
ASSERT_EQ(expected, NormalizePath(given));
|
||||
|
||||
// Two chars a/
|
||||
expected = "a" + sep;
|
||||
given = expected;
|
||||
ASSERT_EQ(expected, NormalizePath(given));
|
||||
|
||||
// Server only
|
||||
expected = sep + sep + "a";
|
||||
given = expected;
|
||||
ASSERT_EQ(expected, NormalizePath(given));
|
||||
|
||||
// Two slashes after character
|
||||
expected = "a" + sep;
|
||||
given = "a" + sep + sep;
|
||||
|
||||
ASSERT_EQ(expected, NormalizePath(given));
|
||||
|
||||
// slash only /
|
||||
expected = sep;
|
||||
given = expected;
|
||||
ASSERT_EQ(expected, NormalizePath(given));
|
||||
|
||||
// UNC only //
|
||||
expected = sep;
|
||||
given = sep + sep;
|
||||
|
||||
ASSERT_EQ(expected, NormalizePath(given));
|
||||
|
||||
// 3 slashesy //
|
||||
expected = sep + sep;
|
||||
given = sep + sep + sep;
|
||||
ASSERT_EQ(expected, NormalizePath(given));
|
||||
|
||||
// 3 slashes //
|
||||
expected = sep + sep + "a" + sep;
|
||||
given = sep + sep + sep + "a" + sep;
|
||||
ASSERT_EQ(expected, NormalizePath(given));
|
||||
|
||||
// 2 separators in the middle
|
||||
expected = "a" + sep + "b";
|
||||
given = "a" + sep + sep + "b";
|
||||
ASSERT_EQ(expected, NormalizePath(given));
|
||||
|
||||
// UNC with duplicate slashes
|
||||
expected = sep + sep + "SERVER" + sep + "a" + sep + "b" + sep + "c";
|
||||
given = sep + sep + "SERVER" + sep + "a" + sep + sep + "b" + sep + "c";
|
||||
ASSERT_EQ(expected, NormalizePath(given));
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+15
-9
@@ -455,16 +455,18 @@ Status FlushJob::MemPurge() {
|
||||
ioptions->logger, true /* internal key corruption is not ok */,
|
||||
existing_snapshots_.empty() ? 0 : existing_snapshots_.back(),
|
||||
snapshot_checker_);
|
||||
assert(job_context_);
|
||||
SequenceNumber job_snapshot_seq = job_context_->GetJobSnapshotSequence();
|
||||
CompactionIterator c_iter(
|
||||
iter.get(), (cfd_->internal_comparator()).user_comparator(), &merge,
|
||||
kMaxSequenceNumber, &existing_snapshots_,
|
||||
earliest_write_conflict_snapshot_, snapshot_checker_, env,
|
||||
ShouldReportDetailedTime(env, ioptions->stats),
|
||||
earliest_write_conflict_snapshot_, job_snapshot_seq, snapshot_checker_,
|
||||
env, ShouldReportDetailedTime(env, ioptions->stats),
|
||||
true /* internal key corruption is not ok */, range_del_agg.get(),
|
||||
nullptr, ioptions->allow_data_in_errors,
|
||||
/*compaction=*/nullptr, compaction_filter.get(),
|
||||
/*shutting_down=*/nullptr,
|
||||
/*preserve_deletes_seqnum=*/0, /*manual_compaction_paused=*/nullptr,
|
||||
/*manual_compaction_paused=*/nullptr,
|
||||
/*manual_compaction_canceled=*/nullptr, ioptions->info_log,
|
||||
&(cfd_->GetFullHistoryTsLow()));
|
||||
|
||||
@@ -829,6 +831,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
// TEST_SYNC_POINT_CALLBACK not used.
|
||||
TEST_SYNC_POINT_CALLBACK("FlushJob::WriteLevel0Table:num_memtables",
|
||||
&mems_size);
|
||||
assert(job_context_);
|
||||
for (MemTable* m : mems_) {
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
@@ -911,16 +914,19 @@ Status FlushJob::WriteLevel0Table() {
|
||||
TableFileCreationReason::kFlush, creation_time, oldest_key_time,
|
||||
current_time, db_id_, db_session_id_, 0 /* target_file_size */,
|
||||
meta_.fd.GetNumber());
|
||||
const SequenceNumber job_snapshot_seq =
|
||||
job_context_->GetJobSnapshotSequence();
|
||||
s = BuildTable(
|
||||
dbname_, versions_, db_options_, tboptions, file_options_,
|
||||
cfd_->table_cache(), iter.get(), std::move(range_del_iters), &meta_,
|
||||
&blob_file_additions, existing_snapshots_,
|
||||
earliest_write_conflict_snapshot_, snapshot_checker_,
|
||||
mutable_cf_options_.paranoid_file_checks, cfd_->internal_stats(),
|
||||
&io_s, io_tracer_, BlobFileCreationReason::kFlush, event_logger_,
|
||||
job_context_->job_id, Env::IO_HIGH, &table_properties_, write_hint,
|
||||
full_history_ts_low, blob_callback_, &num_input_entries,
|
||||
&memtable_payload_bytes, &memtable_garbage_bytes);
|
||||
earliest_write_conflict_snapshot_, job_snapshot_seq,
|
||||
snapshot_checker_, mutable_cf_options_.paranoid_file_checks,
|
||||
cfd_->internal_stats(), &io_s, io_tracer_,
|
||||
BlobFileCreationReason::kFlush, event_logger_, job_context_->job_id,
|
||||
Env::IO_HIGH, &table_properties_, write_hint, full_history_ts_low,
|
||||
blob_callback_, &num_input_entries, &memtable_payload_bytes,
|
||||
&memtable_garbage_bytes);
|
||||
// TODO: Cleanup io_status in BuildTable and table builders
|
||||
assert(!s.ok() || io_s.ok());
|
||||
io_s.PermitUncheckedError();
|
||||
|
||||
+43
-10
@@ -27,6 +27,7 @@
|
||||
#include "rocksdb/system_clock.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/block_based/cachable_entry.h"
|
||||
#include "util/hash_containers.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -304,6 +305,8 @@ static const std::string num_blob_files = "num-blob-files";
|
||||
static const std::string blob_stats = "blob-stats";
|
||||
static const std::string total_blob_file_size = "total-blob-file-size";
|
||||
static const std::string live_blob_file_size = "live-blob-file-size";
|
||||
static const std::string live_blob_file_garbage_size =
|
||||
"live-blob-file-garbage-size";
|
||||
|
||||
const std::string DB::Properties::kNumFilesAtLevelPrefix =
|
||||
rocksdb_prefix + num_files_at_level_prefix;
|
||||
@@ -404,8 +407,10 @@ const std::string DB::Properties::kTotalBlobFileSize =
|
||||
rocksdb_prefix + total_blob_file_size;
|
||||
const std::string DB::Properties::kLiveBlobFileSize =
|
||||
rocksdb_prefix + live_blob_file_size;
|
||||
const std::string DB::Properties::kLiveBlobFileGarbageSize =
|
||||
rocksdb_prefix + live_blob_file_garbage_size;
|
||||
|
||||
const std::unordered_map<std::string, DBPropertyInfo>
|
||||
const UnorderedMap<std::string, DBPropertyInfo>
|
||||
InternalStats::ppt_name_to_info = {
|
||||
{DB::Properties::kNumFilesAtLevelPrefix,
|
||||
{false, &InternalStats::HandleNumFilesAtLevel, nullptr, nullptr,
|
||||
@@ -562,6 +567,9 @@ const std::unordered_map<std::string, DBPropertyInfo>
|
||||
{DB::Properties::kLiveBlobFileSize,
|
||||
{false, nullptr, &InternalStats::HandleLiveBlobFileSize, nullptr,
|
||||
nullptr}},
|
||||
{DB::Properties::kLiveBlobFileGarbageSize,
|
||||
{false, nullptr, &InternalStats::HandleLiveBlobFileGarbageSize,
|
||||
nullptr, nullptr}},
|
||||
};
|
||||
|
||||
InternalStats::InternalStats(int num_levels, SystemClock* clock,
|
||||
@@ -694,17 +702,21 @@ void InternalStats::CacheEntryRoleStats::ToMap(
|
||||
std::map<std::string, std::string>* values, SystemClock* clock) const {
|
||||
values->clear();
|
||||
auto& v = *values;
|
||||
v["id"] = cache_id;
|
||||
v["capacity"] = ROCKSDB_NAMESPACE::ToString(cache_capacity);
|
||||
v["secs_for_last_collection"] =
|
||||
v[BlockCacheEntryStatsMapKeys::CacheId()] = cache_id;
|
||||
v[BlockCacheEntryStatsMapKeys::CacheCapacityBytes()] =
|
||||
ROCKSDB_NAMESPACE::ToString(cache_capacity);
|
||||
v[BlockCacheEntryStatsMapKeys::LastCollectionDurationSeconds()] =
|
||||
ROCKSDB_NAMESPACE::ToString(GetLastDurationMicros() / 1000000.0);
|
||||
v["secs_since_last_collection"] = ROCKSDB_NAMESPACE::ToString(
|
||||
(clock->NowMicros() - last_end_time_micros_) / 1000000U);
|
||||
v[BlockCacheEntryStatsMapKeys::LastCollectionAgeSeconds()] =
|
||||
ROCKSDB_NAMESPACE::ToString((clock->NowMicros() - last_end_time_micros_) /
|
||||
1000000U);
|
||||
for (size_t i = 0; i < kNumCacheEntryRoles; ++i) {
|
||||
std::string role = kCacheEntryRoleToHyphenString[i];
|
||||
v["count." + role] = ROCKSDB_NAMESPACE::ToString(entry_counts[i]);
|
||||
v["bytes." + role] = ROCKSDB_NAMESPACE::ToString(total_charges[i]);
|
||||
v["percent." + role] =
|
||||
auto role = static_cast<CacheEntryRole>(i);
|
||||
v[BlockCacheEntryStatsMapKeys::EntryCount(role)] =
|
||||
ROCKSDB_NAMESPACE::ToString(entry_counts[i]);
|
||||
v[BlockCacheEntryStatsMapKeys::UsedBytes(role)] =
|
||||
ROCKSDB_NAMESPACE::ToString(total_charges[i]);
|
||||
v[BlockCacheEntryStatsMapKeys::UsedPercent(role)] =
|
||||
ROCKSDB_NAMESPACE::ToString(100.0 * total_charges[i] / cache_capacity);
|
||||
}
|
||||
}
|
||||
@@ -757,6 +769,7 @@ bool InternalStats::HandleLiveSstFilesSizeAtTemperature(std::string* value,
|
||||
|
||||
bool InternalStats::HandleNumBlobFiles(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
assert(value);
|
||||
assert(cfd_);
|
||||
|
||||
const auto* current = cfd_->current();
|
||||
@@ -773,6 +786,7 @@ bool InternalStats::HandleNumBlobFiles(uint64_t* value, DBImpl* /*db*/,
|
||||
}
|
||||
|
||||
bool InternalStats::HandleBlobStats(std::string* value, Slice /*suffix*/) {
|
||||
assert(value);
|
||||
assert(cfd_);
|
||||
|
||||
const auto* current = cfd_->current();
|
||||
@@ -797,6 +811,7 @@ bool InternalStats::HandleBlobStats(std::string* value, Slice /*suffix*/) {
|
||||
|
||||
bool InternalStats::HandleTotalBlobFileSize(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
assert(value);
|
||||
assert(cfd_);
|
||||
|
||||
*value = cfd_->GetTotalBlobFileSize();
|
||||
@@ -806,6 +821,7 @@ bool InternalStats::HandleTotalBlobFileSize(uint64_t* value, DBImpl* /*db*/,
|
||||
|
||||
bool InternalStats::HandleLiveBlobFileSize(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
assert(value);
|
||||
assert(cfd_);
|
||||
|
||||
const auto* current = cfd_->current();
|
||||
@@ -819,6 +835,23 @@ bool InternalStats::HandleLiveBlobFileSize(uint64_t* value, DBImpl* /*db*/,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleLiveBlobFileGarbageSize(uint64_t* value,
|
||||
DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
assert(value);
|
||||
assert(cfd_);
|
||||
|
||||
const auto* current = cfd_->current();
|
||||
assert(current);
|
||||
|
||||
const auto* vstorage = current->storage_info();
|
||||
assert(vstorage);
|
||||
|
||||
*value = vstorage->GetBlobStats().total_garbage_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const DBPropertyInfo* GetPropertyInfo(const Slice& property) {
|
||||
std::string ppt_name = GetPropertyNameAndArg(property).first.ToString();
|
||||
auto ppt_info_iter = InternalStats::ppt_name_to_info.find(ppt_name);
|
||||
|
||||
+6
-2
@@ -18,6 +18,7 @@
|
||||
#include "cache/cache_entry_roles.h"
|
||||
#include "db/version_set.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
#include "util/hash_containers.h"
|
||||
|
||||
class ColumnFamilyData;
|
||||
|
||||
@@ -387,7 +388,7 @@ class InternalStats {
|
||||
SystemClock* clock) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<Cache::DeleterFn, CacheEntryRole> role_map_;
|
||||
UnorderedMap<Cache::DeleterFn, CacheEntryRole> role_map_;
|
||||
uint64_t GetLastDurationMicros() const;
|
||||
};
|
||||
|
||||
@@ -482,7 +483,7 @@ class InternalStats {
|
||||
|
||||
// Store a mapping from the user-facing DB::Properties string to our
|
||||
// DBPropertyInfo struct used internally for retrieving properties.
|
||||
static const std::unordered_map<std::string, DBPropertyInfo> ppt_name_to_info;
|
||||
static const UnorderedMap<std::string, DBPropertyInfo> ppt_name_to_info;
|
||||
|
||||
private:
|
||||
void DumpDBMapStats(std::map<std::string, std::string>* db_stats);
|
||||
@@ -690,6 +691,9 @@ class InternalStats {
|
||||
bool HandleBlobStats(std::string* value, Slice suffix);
|
||||
bool HandleTotalBlobFileSize(uint64_t* value, DBImpl* db, Version* version);
|
||||
bool HandleLiveBlobFileSize(uint64_t* value, DBImpl* db, Version* version);
|
||||
bool HandleLiveBlobFileGarbageSize(uint64_t* value, DBImpl* db,
|
||||
Version* version);
|
||||
|
||||
// Total number of background errors encountered. Every time a flush task
|
||||
// or compaction task fails, this counter is incremented. The failure can
|
||||
// be caused by any possible reason, including file system errors, out of
|
||||
|
||||
@@ -124,6 +124,14 @@ struct JobContext {
|
||||
job_snapshot != nullptr || sv_have_sth;
|
||||
}
|
||||
|
||||
SequenceNumber GetJobSnapshotSequence() const {
|
||||
if (job_snapshot) {
|
||||
assert(job_snapshot->snapshot());
|
||||
return job_snapshot->snapshot()->GetSequenceNumber();
|
||||
}
|
||||
return kMaxSequenceNumber;
|
||||
}
|
||||
|
||||
// Structure to store information for candidate files to delete.
|
||||
struct CandidateFileInfo {
|
||||
std::string file_name;
|
||||
|
||||
+2
-2
@@ -13,7 +13,6 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
@@ -31,6 +30,7 @@
|
||||
#include "table/multiget_context.h"
|
||||
#include "util/dynamic_bloom.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/hash_containers.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -566,7 +566,7 @@ class MemTable {
|
||||
const SliceTransform* insert_with_hint_prefix_extractor_;
|
||||
|
||||
// Insert hints for each prefix.
|
||||
std::unordered_map<Slice, void*, SliceHasher> insert_hints_;
|
||||
UnorderedMapH<Slice, void*, SliceHasher> insert_hints_;
|
||||
|
||||
// Timestamp of oldest key
|
||||
std::atomic<uint64_t> oldest_key_time_;
|
||||
|
||||
+23
-24
@@ -489,8 +489,8 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
// TODO(myabandeh): Not sure how batch_count could be 0 here.
|
||||
if (batch_count > 0) {
|
||||
uint64_t min_wal_number_to_keep = 0;
|
||||
assert(edit_list.size() > 0);
|
||||
if (vset->db_options()->allow_2pc) {
|
||||
assert(edit_list.size() > 0);
|
||||
// Note that if mempurge is successful, the edit_list will
|
||||
// not be applicable (contains info of new min_log number to keep,
|
||||
// and level 0 file path of SST file created during normal flush,
|
||||
@@ -499,24 +499,26 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
min_wal_number_to_keep = PrecomputeMinLogNumberToKeep2PC(
|
||||
vset, *cfd, edit_list, memtables_to_flush, prep_tracker);
|
||||
|
||||
// We piggyback the information of earliest log file to keep in the
|
||||
// We piggyback the information of earliest log file to keep in the
|
||||
// manifest entry for the last file flushed.
|
||||
edit_list.back()->SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
} else {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, *cfd, edit_list);
|
||||
}
|
||||
|
||||
std::unique_ptr<VersionEdit> wal_deletion;
|
||||
VersionEdit wal_deletion;
|
||||
wal_deletion.SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
if (vset->db_options()->track_and_verify_wals_in_manifest) {
|
||||
if (!vset->db_options()->allow_2pc) {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, *cfd, edit_list);
|
||||
}
|
||||
if (min_wal_number_to_keep >
|
||||
vset->GetWalSet().GetMinWalNumberToKeep()) {
|
||||
wal_deletion.reset(new VersionEdit);
|
||||
wal_deletion->DeleteWalsBefore(min_wal_number_to_keep);
|
||||
edit_list.push_back(wal_deletion.get());
|
||||
wal_deletion.DeleteWalsBefore(min_wal_number_to_keep);
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"MemTableList::TryInstallMemtableFlushResults:"
|
||||
"AfterComputeMinWalToKeep",
|
||||
nullptr);
|
||||
}
|
||||
edit_list.push_back(&wal_deletion);
|
||||
|
||||
const auto manifest_write_cb = [this, cfd, batch_count, log_buffer,
|
||||
to_delete, mu](const Status& status) {
|
||||
@@ -798,22 +800,19 @@ Status InstallMemtableAtomicFlushResults(
|
||||
if (vset->db_options()->allow_2pc) {
|
||||
min_wal_number_to_keep = PrecomputeMinLogNumberToKeep2PC(
|
||||
vset, cfds, edit_lists, mems_list, prep_tracker);
|
||||
edit_lists.back().back()->SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
} else {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, cfds, edit_lists);
|
||||
}
|
||||
|
||||
std::unique_ptr<VersionEdit> wal_deletion;
|
||||
if (vset->db_options()->track_and_verify_wals_in_manifest) {
|
||||
if (!vset->db_options()->allow_2pc) {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, cfds, edit_lists);
|
||||
}
|
||||
if (min_wal_number_to_keep > vset->GetWalSet().GetMinWalNumberToKeep()) {
|
||||
wal_deletion.reset(new VersionEdit);
|
||||
wal_deletion->DeleteWalsBefore(min_wal_number_to_keep);
|
||||
edit_lists.back().push_back(wal_deletion.get());
|
||||
++num_entries;
|
||||
}
|
||||
VersionEdit wal_deletion;
|
||||
wal_deletion.SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
if (vset->db_options()->track_and_verify_wals_in_manifest &&
|
||||
min_wal_number_to_keep > vset->GetWalSet().GetMinWalNumberToKeep()) {
|
||||
wal_deletion.DeleteWalsBefore(min_wal_number_to_keep);
|
||||
}
|
||||
edit_lists.back().push_back(&wal_deletion);
|
||||
++num_entries;
|
||||
|
||||
// Mark the version edits as an atomic group if the number of version edits
|
||||
// exceeds 1.
|
||||
|
||||
+7
-4
@@ -450,7 +450,7 @@ class Repairer {
|
||||
dbname_, /* versions */ nullptr, immutable_db_options_, tboptions,
|
||||
file_options_, table_cache_.get(), iter.get(),
|
||||
std::move(range_del_iters), &meta, nullptr /* blob_file_additions */,
|
||||
{}, kMaxSequenceNumber, snapshot_checker,
|
||||
{}, kMaxSequenceNumber, kMaxSequenceNumber, snapshot_checker,
|
||||
false /* paranoid_file_checks*/, nullptr /* internal_stats */, &io_s,
|
||||
nullptr /*IOTracer*/, BlobFileCreationReason::kRecovery,
|
||||
nullptr /* event_logger */, 0 /* job_id */, Env::IO_HIGH,
|
||||
@@ -565,10 +565,13 @@ class Repairer {
|
||||
|
||||
counter++;
|
||||
|
||||
t->meta.UpdateBoundaries(key, iter->value(), parsed.sequence,
|
||||
parsed.type);
|
||||
status = t->meta.UpdateBoundaries(key, iter->value(), parsed.sequence,
|
||||
parsed.type);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!iter->status().ok()) {
|
||||
if (status.ok() && !iter->status().ok()) {
|
||||
status = iter->status();
|
||||
}
|
||||
delete iter;
|
||||
|
||||
+28
-53
@@ -6,9 +6,12 @@
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/transaction_log_impl.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "file/sequence_file_reader.h"
|
||||
#include "util/defer.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -24,16 +27,17 @@ TransactionLogIteratorImpl::TransactionLogIteratorImpl(
|
||||
soptions_(soptions),
|
||||
starting_sequence_number_(seq),
|
||||
files_(std::move(files)),
|
||||
versions_(versions),
|
||||
seq_per_batch_(seq_per_batch),
|
||||
io_tracer_(io_tracer),
|
||||
started_(false),
|
||||
is_valid_(false),
|
||||
current_file_index_(0),
|
||||
current_batch_seq_(0),
|
||||
current_last_seq_(0),
|
||||
versions_(versions),
|
||||
seq_per_batch_(seq_per_batch),
|
||||
io_tracer_(io_tracer) {
|
||||
current_last_seq_(0) {
|
||||
assert(files_ != nullptr);
|
||||
assert(versions_ != nullptr);
|
||||
assert(!seq_per_batch_);
|
||||
current_status_.PermitUncheckedError(); // Clear on start
|
||||
reporter_.env = options_->env;
|
||||
reporter_.info_log = options_->info_log.get();
|
||||
@@ -94,8 +98,21 @@ void TransactionLogIteratorImpl::SeekToStartSequence(uint64_t start_file_index,
|
||||
Slice record;
|
||||
started_ = false;
|
||||
is_valid_ = false;
|
||||
// Check invariant of TransactionLogIterator when SeekToStartSequence()
|
||||
// succeeds.
|
||||
const Defer defer([this]() {
|
||||
if (is_valid_) {
|
||||
assert(current_status_.ok());
|
||||
if (starting_sequence_number_ > current_batch_seq_) {
|
||||
assert(current_batch_seq_ < current_last_seq_);
|
||||
assert(current_last_seq_ >= starting_sequence_number_);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (files_->size() <= start_file_index) {
|
||||
return;
|
||||
} else if (!current_status_.ok()) {
|
||||
return;
|
||||
}
|
||||
Status s =
|
||||
OpenLogReader(files_->at(static_cast<size_t>(start_file_index)).get());
|
||||
@@ -151,6 +168,9 @@ void TransactionLogIteratorImpl::SeekToStartSequence(uint64_t start_file_index,
|
||||
}
|
||||
|
||||
void TransactionLogIteratorImpl::Next() {
|
||||
if (!current_status_.ok()) {
|
||||
return;
|
||||
}
|
||||
return NextImpl(false);
|
||||
}
|
||||
|
||||
@@ -159,7 +179,7 @@ void TransactionLogIteratorImpl::NextImpl(bool internal) {
|
||||
is_valid_ = false;
|
||||
if (!internal && !started_) {
|
||||
// Runs every time until we can seek to the start sequence
|
||||
return SeekToStartSequence();
|
||||
SeekToStartSequence();
|
||||
}
|
||||
while(true) {
|
||||
assert(current_log_reader_);
|
||||
@@ -249,55 +269,10 @@ void TransactionLogIteratorImpl::UpdateCurrentWriteBatch(const Slice& record) {
|
||||
return SeekToStartSequence(current_file_index_, !seq_per_batch_);
|
||||
}
|
||||
|
||||
struct BatchCounter : public WriteBatch::Handler {
|
||||
SequenceNumber sequence_;
|
||||
BatchCounter(SequenceNumber sequence) : sequence_(sequence) {}
|
||||
Status MarkNoop(bool empty_batch) override {
|
||||
if (!empty_batch) {
|
||||
sequence_++;
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
Status MarkEndPrepare(const Slice&) override {
|
||||
sequence_++;
|
||||
return Status::OK();
|
||||
}
|
||||
Status MarkCommit(const Slice&) override {
|
||||
sequence_++;
|
||||
return Status::OK();
|
||||
}
|
||||
Status MarkCommitWithTimestamp(const Slice&, const Slice&) override {
|
||||
++sequence_;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status PutCF(uint32_t /*cf*/, const Slice& /*key*/,
|
||||
const Slice& /*val*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
Status DeleteCF(uint32_t /*cf*/, const Slice& /*key*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
Status SingleDeleteCF(uint32_t /*cf*/, const Slice& /*key*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
Status MergeCF(uint32_t /*cf*/, const Slice& /*key*/,
|
||||
const Slice& /*val*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
Status MarkBeginPrepare(bool) override { return Status::OK(); }
|
||||
Status MarkRollback(const Slice&) override { return Status::OK(); }
|
||||
};
|
||||
|
||||
current_batch_seq_ = WriteBatchInternal::Sequence(batch.get());
|
||||
if (seq_per_batch_) {
|
||||
BatchCounter counter(current_batch_seq_);
|
||||
batch->Iterate(&counter);
|
||||
current_last_seq_ = counter.sequence_;
|
||||
} else {
|
||||
current_last_seq_ =
|
||||
current_batch_seq_ + WriteBatchInternal::Count(batch.get()) - 1;
|
||||
}
|
||||
assert(!seq_per_batch_);
|
||||
current_last_seq_ =
|
||||
current_batch_seq_ + WriteBatchInternal::Count(batch.get()) - 1;
|
||||
// currentBatchSeq_ can only change here
|
||||
assert(current_last_seq_ <= versions_->LastSequence());
|
||||
|
||||
|
||||
@@ -81,6 +81,13 @@ class TransactionLogIteratorImpl : public TransactionLogIterator {
|
||||
const EnvOptions& soptions_;
|
||||
SequenceNumber starting_sequence_number_;
|
||||
std::unique_ptr<VectorLogPtr> files_;
|
||||
// Used only to get latest seq. num
|
||||
// TODO(icanadi) can this be just a callback?
|
||||
VersionSet const* const versions_;
|
||||
const bool seq_per_batch_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
|
||||
// State variables
|
||||
bool started_;
|
||||
bool is_valid_; // not valid when it starts of.
|
||||
Status current_status_;
|
||||
@@ -104,14 +111,11 @@ class TransactionLogIteratorImpl : public TransactionLogIterator {
|
||||
SequenceNumber
|
||||
current_batch_seq_; // sequence number at start of current batch
|
||||
SequenceNumber current_last_seq_; // last sequence in the current batch
|
||||
// Used only to get latest seq. num
|
||||
// TODO(icanadi) can this be just a callback?
|
||||
VersionSet const* const versions_;
|
||||
const bool seq_per_batch_;
|
||||
// Reads from transaction log only if the writebatch record has been written
|
||||
bool RestrictedRead(Slice* record);
|
||||
// Seeks to startingSequenceNumber reading from startFileIndex in files_.
|
||||
// If strict is set,then must get a batch starting with startingSequenceNumber
|
||||
// Seeks to starting_sequence_number_ reading from start_file_index in files_.
|
||||
// If strict is set, then must get a batch starting with
|
||||
// starting_sequence_number_.
|
||||
void SeekToStartSequence(uint64_t start_file_index = 0, bool strict = false);
|
||||
// Implementation of Next. SeekToStartSequence calls it internally with
|
||||
// internal=true to let it find next entry even if it has to jump gaps because
|
||||
@@ -120,10 +124,9 @@ class TransactionLogIteratorImpl : public TransactionLogIterator {
|
||||
void NextImpl(bool internal = false);
|
||||
// Check if batch is expected, else return false
|
||||
bool IsBatchExpected(const WriteBatch* batch, SequenceNumber expected_seq);
|
||||
// Update current batch if a continuous batch is found, else return false
|
||||
// Update current batch if a continuous batch is found.
|
||||
void UpdateCurrentWriteBatch(const Slice& record);
|
||||
Status OpenLogReader(const LogFile* file);
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
+26
-29
@@ -28,9 +28,28 @@ uint64_t PackFileNumberAndPathId(uint64_t number, uint64_t path_id) {
|
||||
return number | (path_id * (kFileNumberMask + 1));
|
||||
}
|
||||
|
||||
void FileMetaData::UpdateBoundaries(const Slice& key, const Slice& value,
|
||||
SequenceNumber seqno,
|
||||
ValueType value_type) {
|
||||
Status FileMetaData::UpdateBoundaries(const Slice& key, const Slice& value,
|
||||
SequenceNumber seqno,
|
||||
ValueType value_type) {
|
||||
if (value_type == kTypeBlobIndex) {
|
||||
BlobIndex blob_index;
|
||||
const Status s = blob_index.DecodeFrom(value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (!blob_index.IsInlined() && !blob_index.HasTTL()) {
|
||||
if (blob_index.file_number() == kInvalidBlobFileNumber) {
|
||||
return Status::Corruption("Invalid blob file number");
|
||||
}
|
||||
|
||||
if (oldest_blob_file_number == kInvalidBlobFileNumber ||
|
||||
oldest_blob_file_number > blob_index.file_number()) {
|
||||
oldest_blob_file_number = blob_index.file_number();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (smallest.size() == 0) {
|
||||
smallest.DecodeFrom(key);
|
||||
}
|
||||
@@ -38,32 +57,7 @@ void FileMetaData::UpdateBoundaries(const Slice& key, const Slice& value,
|
||||
fd.smallest_seqno = std::min(fd.smallest_seqno, seqno);
|
||||
fd.largest_seqno = std::max(fd.largest_seqno, seqno);
|
||||
|
||||
if (value_type == kTypeBlobIndex) {
|
||||
BlobIndex blob_index;
|
||||
const Status s = blob_index.DecodeFrom(value);
|
||||
if (!s.ok()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (blob_index.IsInlined()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (blob_index.HasTTL()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Paranoid check: this should not happen because BlobDB numbers the blob
|
||||
// files starting from 1.
|
||||
if (blob_index.file_number() == kInvalidBlobFileNumber) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldest_blob_file_number == kInvalidBlobFileNumber ||
|
||||
oldest_blob_file_number > blob_index.file_number()) {
|
||||
oldest_blob_file_number = blob_index.file_number();
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void VersionEdit::Clear() {
|
||||
@@ -120,6 +114,9 @@ bool VersionEdit::EncodeTo(std::string* dst) const {
|
||||
if (has_max_column_family_) {
|
||||
PutVarint32Varint32(dst, kMaxColumnFamily, max_column_family_);
|
||||
}
|
||||
if (has_min_log_number_to_keep_) {
|
||||
PutVarint32Varint64(dst, kMinLogNumberToKeep, min_log_number_to_keep_);
|
||||
}
|
||||
if (has_last_sequence_) {
|
||||
PutVarint32Varint64(dst, kLastSequence, last_sequence_);
|
||||
}
|
||||
|
||||
+2
-2
@@ -245,8 +245,8 @@ struct FileMetaData {
|
||||
|
||||
// REQUIRED: Keys must be given to the function in sorted order (it expects
|
||||
// the last key to be the largest).
|
||||
void UpdateBoundaries(const Slice& key, const Slice& value,
|
||||
SequenceNumber seqno, ValueType value_type);
|
||||
Status UpdateBoundaries(const Slice& key, const Slice& value,
|
||||
SequenceNumber seqno, ValueType value_type);
|
||||
|
||||
// Unlike UpdateBoundaries, ranges do not need to be presented in any
|
||||
// particular order.
|
||||
|
||||
@@ -394,7 +394,7 @@ void VersionEditHandler::CheckIterationResult(const log::Reader& reader,
|
||||
if (s->ok()) {
|
||||
version_set_->GetColumnFamilySet()->UpdateMaxColumnFamily(
|
||||
version_edit_params_.max_column_family_);
|
||||
version_set_->MarkMinLogNumberToKeep2PC(
|
||||
version_set_->MarkMinLogNumberToKeep(
|
||||
version_edit_params_.min_log_number_to_keep_);
|
||||
version_set_->MarkFileNumberUsed(version_edit_params_.prev_log_number_);
|
||||
version_set_->MarkFileNumberUsed(version_edit_params_.log_number_);
|
||||
@@ -970,12 +970,11 @@ void DumpManifestHandler::CheckIterationResult(const log::Reader& reader,
|
||||
fprintf(stdout,
|
||||
"next_file_number %" PRIu64 " last_sequence %" PRIu64
|
||||
" prev_log_number %" PRIu64 " max_column_family %" PRIu32
|
||||
" min_log_number_to_keep "
|
||||
"%" PRIu64 "\n",
|
||||
" min_log_number_to_keep %" PRIu64 "\n",
|
||||
version_set_->current_next_file_number(),
|
||||
version_set_->LastSequence(), version_set_->prev_log_number(),
|
||||
version_set_->column_family_set_->GetMaxColumnFamily(),
|
||||
version_set_->min_log_number_to_keep_2pc());
|
||||
version_set_->min_log_number_to_keep());
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "db/version_edit.h"
|
||||
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "rocksdb/advanced_options.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
@@ -611,6 +612,128 @@ TEST_F(VersionEditTest, IgnorableTags) {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST(FileMetaDataTest, UpdateBoundariesBlobIndex) {
|
||||
FileMetaData meta;
|
||||
|
||||
{
|
||||
constexpr uint64_t file_number = 10;
|
||||
constexpr uint32_t path_id = 0;
|
||||
constexpr uint64_t file_size = 0;
|
||||
|
||||
meta.fd = FileDescriptor(file_number, path_id, file_size);
|
||||
}
|
||||
|
||||
constexpr char key[] = "foo";
|
||||
|
||||
constexpr uint64_t expected_oldest_blob_file_number = 20;
|
||||
|
||||
// Plain old value (does not affect oldest_blob_file_number)
|
||||
{
|
||||
constexpr char value[] = "value";
|
||||
constexpr SequenceNumber seq = 200;
|
||||
|
||||
ASSERT_OK(meta.UpdateBoundaries(key, value, seq, kTypeValue));
|
||||
ASSERT_EQ(meta.oldest_blob_file_number, kInvalidBlobFileNumber);
|
||||
}
|
||||
|
||||
// Non-inlined, non-TTL blob index (sets oldest_blob_file_number)
|
||||
{
|
||||
constexpr uint64_t blob_file_number = 25;
|
||||
static_assert(blob_file_number > expected_oldest_blob_file_number,
|
||||
"unexpected");
|
||||
|
||||
constexpr uint64_t offset = 1000;
|
||||
constexpr uint64_t size = 100;
|
||||
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeBlob(&blob_index, blob_file_number, offset, size,
|
||||
kNoCompression);
|
||||
|
||||
constexpr SequenceNumber seq = 201;
|
||||
|
||||
ASSERT_OK(meta.UpdateBoundaries(key, blob_index, seq, kTypeBlobIndex));
|
||||
ASSERT_EQ(meta.oldest_blob_file_number, blob_file_number);
|
||||
}
|
||||
|
||||
// Another one, with the oldest blob file number (updates
|
||||
// oldest_blob_file_number)
|
||||
{
|
||||
constexpr uint64_t offset = 2000;
|
||||
constexpr uint64_t size = 300;
|
||||
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeBlob(&blob_index, expected_oldest_blob_file_number, offset,
|
||||
size, kNoCompression);
|
||||
|
||||
constexpr SequenceNumber seq = 202;
|
||||
|
||||
ASSERT_OK(meta.UpdateBoundaries(key, blob_index, seq, kTypeBlobIndex));
|
||||
ASSERT_EQ(meta.oldest_blob_file_number, expected_oldest_blob_file_number);
|
||||
}
|
||||
|
||||
// Inlined TTL blob index (does not affect oldest_blob_file_number)
|
||||
{
|
||||
constexpr uint64_t expiration = 9876543210;
|
||||
constexpr char value[] = "value";
|
||||
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeInlinedTTL(&blob_index, expiration, value);
|
||||
|
||||
constexpr SequenceNumber seq = 203;
|
||||
|
||||
ASSERT_OK(meta.UpdateBoundaries(key, blob_index, seq, kTypeBlobIndex));
|
||||
ASSERT_EQ(meta.oldest_blob_file_number, expected_oldest_blob_file_number);
|
||||
}
|
||||
|
||||
// Non-inlined TTL blob index (does not affect oldest_blob_file_number, even
|
||||
// though file number is smaller)
|
||||
{
|
||||
constexpr uint64_t expiration = 9876543210;
|
||||
constexpr uint64_t blob_file_number = 15;
|
||||
static_assert(blob_file_number < expected_oldest_blob_file_number,
|
||||
"unexpected");
|
||||
|
||||
constexpr uint64_t offset = 2000;
|
||||
constexpr uint64_t size = 500;
|
||||
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeBlobTTL(&blob_index, expiration, blob_file_number, offset,
|
||||
size, kNoCompression);
|
||||
|
||||
constexpr SequenceNumber seq = 204;
|
||||
|
||||
ASSERT_OK(meta.UpdateBoundaries(key, blob_index, seq, kTypeBlobIndex));
|
||||
ASSERT_EQ(meta.oldest_blob_file_number, expected_oldest_blob_file_number);
|
||||
}
|
||||
|
||||
// Corrupt blob index
|
||||
{
|
||||
constexpr char corrupt_blob_index[] = "!corrupt!";
|
||||
constexpr SequenceNumber seq = 205;
|
||||
|
||||
ASSERT_TRUE(
|
||||
meta.UpdateBoundaries(key, corrupt_blob_index, seq, kTypeBlobIndex)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(meta.oldest_blob_file_number, expected_oldest_blob_file_number);
|
||||
}
|
||||
|
||||
// Invalid blob file number
|
||||
{
|
||||
constexpr uint64_t offset = 10000;
|
||||
constexpr uint64_t size = 1000;
|
||||
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeBlob(&blob_index, kInvalidBlobFileNumber, offset, size,
|
||||
kNoCompression);
|
||||
|
||||
constexpr SequenceNumber seq = 206;
|
||||
|
||||
ASSERT_TRUE(meta.UpdateBoundaries(key, blob_index, seq, kTypeBlobIndex)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(meta.oldest_blob_file_number, expected_oldest_blob_file_number);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user