mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66559b4087 | ||
|
|
d8e835549b | ||
|
|
f2f26b1559 | ||
|
|
710592283f | ||
|
|
f38f5570d5 | ||
|
|
3620e69404 | ||
|
|
34681a1a2a | ||
|
|
5898845c87 | ||
|
|
1ef888f9b8 | ||
|
|
14fb86d1ba | ||
|
|
65a3e21af4 | ||
|
|
b3c68d8de9 |
No files matched your search
+67
-141
@@ -111,12 +111,6 @@ commands:
|
||||
cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DBENCHMARK_ENABLE_GTEST_TESTS=0
|
||||
ninja && sudo ninja install
|
||||
|
||||
install-valgrind:
|
||||
steps:
|
||||
- run:
|
||||
name: Install valgrind
|
||||
command: sudo apt-get update -y && sudo apt-get install -y valgrind
|
||||
|
||||
upgrade-cmake:
|
||||
steps:
|
||||
- run:
|
||||
@@ -147,20 +141,13 @@ commands:
|
||||
command: |
|
||||
sudo apt-get update -y && sudo apt-get install -y libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev libzstd-dev
|
||||
|
||||
install-streaming-compress-libs:
|
||||
steps:
|
||||
- run:
|
||||
name: Install streaming compression libs
|
||||
command: |
|
||||
sudo apt-get update -y && sudo apt-get install -y libzstd-dev
|
||||
|
||||
install-libprotobuf-mutator:
|
||||
steps:
|
||||
- run:
|
||||
name: Install libprotobuf-mutator libs
|
||||
command: |
|
||||
git clone -b v1.0 git@github.com:google/libprotobuf-mutator.git ~/libprotobuf-mutator
|
||||
cd ~/libprotobuf-mutator && git checkout ffd86a32874e5c08a143019aad1aaf0907294c9f && mkdir build && cd build
|
||||
git clone --single-branch --branch master --depth 1 git@github.com:google/libprotobuf-mutator.git ~/libprotobuf-mutator
|
||||
cd ~/libprotobuf-mutator && mkdir build && cd build
|
||||
cmake .. -GNinja -DCMAKE_C_COMPILER=clang-13 -DCMAKE_CXX_COMPILER=clang++-13 -DCMAKE_BUILD_TYPE=Release -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON
|
||||
ninja && sudo ninja install
|
||||
- run:
|
||||
@@ -168,45 +155,6 @@ commands:
|
||||
command: |
|
||||
echo "export PKG_CONFIG_PATH=/usr/local/OFF/:~/libprotobuf-mutator/build/external.protobuf/lib/pkgconfig/" >> $BASH_ENV
|
||||
echo "export PROTOC_BIN=~/libprotobuf-mutator/build/external.protobuf/bin/protoc" >> $BASH_ENV
|
||||
setup-folly:
|
||||
steps:
|
||||
- run:
|
||||
name: Install folly dependencies
|
||||
command: |
|
||||
sudo apt-get install libgoogle-glog-dev
|
||||
- run:
|
||||
name: Checkout folly sources
|
||||
command: |
|
||||
make checkout_folly
|
||||
|
||||
build-for-benchmarks:
|
||||
steps:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "Linux build for benchmarks"
|
||||
command: #sized for the resource-class rocksdb-benchmark-sys1
|
||||
make V=1 J=8 -j8 release
|
||||
|
||||
perform-benchmarks:
|
||||
steps:
|
||||
- run:
|
||||
name: "Run basic benchmark, 5 min"
|
||||
command: ./tools/benchmark.sh fillseq_enable_wal
|
||||
environment:
|
||||
LD_LIBRARY_PATH: /usr/local/lib
|
||||
DB_DIR: /tmp/rocksdb-benchmark-datadir
|
||||
WAL_DIR: /tmp/rocksdb-benchmark-waldir
|
||||
OUTPUT_DIR: /tmp/benchmark-results
|
||||
NUM_KEYS: 10000000
|
||||
|
||||
post-benchmarks:
|
||||
steps:
|
||||
- store_artifacts: # store the benchmark output
|
||||
path: /tmp/benchmark-results
|
||||
destination: test_logs
|
||||
- run:
|
||||
name: Send benchmark report to visualisation
|
||||
command: ./build_tools/benchmark_log_tool.py --tsvfile /tmp/benchmark-results/report.tsv --esdocument https://search-rocksdb-bench-k2izhptfeap2hjfxteolsgsynm.us-west-2.es.amazonaws.com/bench_test3/_doc
|
||||
|
||||
executors:
|
||||
windows-2xlarge:
|
||||
@@ -226,7 +174,7 @@ jobs:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-gflags-on-macos
|
||||
- pre-steps-macos
|
||||
- run: ulimit -S -n `ulimit -H -n` && OPT=-DCIRCLECI make V=1 J=32 -j32 all
|
||||
- run: ulimit -S -n 1048576 && OPT=-DCIRCLECI make V=1 J=32 -j32 all
|
||||
- post-steps
|
||||
|
||||
build-macos-cmake:
|
||||
@@ -245,7 +193,7 @@ jobs:
|
||||
- pre-steps-macos
|
||||
- run:
|
||||
name: "cmake generate project file"
|
||||
command: ulimit -S -n `ulimit -H -n` && mkdir build && cd build && cmake -DWITH_GFLAGS=1 ..
|
||||
command: ulimit -S -n 1048576 && mkdir build && cd build && cmake -DWITH_GFLAGS=1 ..
|
||||
- run:
|
||||
name: "Build tests"
|
||||
command: cd build && make V=1 -j32
|
||||
@@ -254,14 +202,14 @@ jobs:
|
||||
steps:
|
||||
- run:
|
||||
name: "Run even tests"
|
||||
command: ulimit -S -n `ulimit -H -n` && cd build && ctest -j32 -I 0,,2
|
||||
command: ulimit -S -n 1048576 && cd build && ctest -j32 -I 0,,2
|
||||
- when:
|
||||
condition:
|
||||
not: << parameters.run_even_tests >>
|
||||
steps:
|
||||
- run:
|
||||
name: "Run odd tests"
|
||||
command: ulimit -S -n `ulimit -H -n` && cd build && ctest -j32 -I 1,,2
|
||||
command: ulimit -S -n 1048576 && cd build && ctest -j32 -I 1,,2
|
||||
- post-steps
|
||||
|
||||
build-linux:
|
||||
@@ -271,20 +219,17 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-streaming-compress-libs
|
||||
- run: make V=1 J=32 -j32 check
|
||||
- post-steps
|
||||
|
||||
build-linux-encrypted_env-no_compression:
|
||||
build-linux-encrypted-env:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: ENCRYPTED_ENV=1 ROCKSDB_DISABLE_SNAPPY=1 ROCKSDB_DISABLE_ZLIB=1 ROCKSDB_DISABLE_BZIP=1 ROCKSDB_DISABLE_LZ4=1 ROCKSDB_DISABLE_ZSTD=1 make V=1 J=32 -j32 check
|
||||
- run: |
|
||||
./sst_dump --help | egrep -q 'Supported compression types: kNoCompression$' # Verify no compiled in compression
|
||||
- run: ENCRYPTED_ENV=1 make V=1 J=32 -j32 check
|
||||
- post-steps
|
||||
|
||||
build-linux-shared_lib-alt_namespace-status_checked:
|
||||
@@ -365,7 +310,7 @@ jobs:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-clang-10
|
||||
- run: COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check # aligned new doesn't work for reason we haven't figured out
|
||||
- run: ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check # aligned new doesn't work for reason we haven't figured out
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-mini-tsan:
|
||||
@@ -409,17 +354,6 @@ jobs:
|
||||
- run: COMPILE_WITH_UBSAN=1 OPT="-fsanitize-blacklist=.circleci/ubsan_suppression_list.txt" CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 ubsan_check # aligned new doesn't work for reason we haven't figured out
|
||||
- post-steps
|
||||
|
||||
build-linux-valgrind:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-valgrind
|
||||
- run: PORTABLE=1 make V=1 -j32 valgrind_test
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-clang-analyze:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
@@ -432,18 +366,6 @@ 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-runner:
|
||||
machine: true
|
||||
resource_class: facebook/rocksdb-benchmark-sys1
|
||||
steps:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "Checked Linux build (Runner)"
|
||||
command: make V=1 J=8 -j8 check
|
||||
environment:
|
||||
LD_LIBRARY_PATH: /usr/local/lib
|
||||
- post-steps
|
||||
|
||||
build-linux-cmake-with-folly:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
@@ -452,7 +374,7 @@ jobs:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- upgrade-cmake
|
||||
- setup-folly
|
||||
- run: make checkout_folly
|
||||
- run: (mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 .. && make V=1 -j20 && ctest -j20)
|
||||
- post-steps
|
||||
|
||||
@@ -470,13 +392,11 @@ jobs:
|
||||
build-linux-unity-and-headers:
|
||||
docker: # executor type
|
||||
- image: gcc:latest
|
||||
environment:
|
||||
EXTRA_CXXFLAGS: -mno-avx512f # Warnings-as-error in avx512fintrin.h, would be used on newer hardware
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: apt-get update -y && apt-get install -y libgflags-dev
|
||||
- run: make V=1 -j8 unity_test
|
||||
- run: TEST_TMPDIR=/dev/shm && make V=1 -j8 unity_test
|
||||
- run: make V=1 -j8 -k check-headers # could be moved to a different build
|
||||
- post-steps
|
||||
|
||||
@@ -487,7 +407,7 @@ jobs:
|
||||
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
|
||||
- setup-folly
|
||||
- run: make checkout_folly
|
||||
- run: USE_FOLLY=1 CC=gcc-7 CXX=g++-7 V=1 make -j32 check
|
||||
- post-steps
|
||||
|
||||
@@ -533,19 +453,6 @@ jobs:
|
||||
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j16 all microbench
|
||||
- post-steps
|
||||
|
||||
# Ensure ASAN+UBSAN with folly, and full testsuite with clang 13
|
||||
build-linux-clang-13-asan-ubsan-with-folly:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-clang-13
|
||||
- install-gflags
|
||||
- setup-folly
|
||||
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check
|
||||
- post-steps
|
||||
|
||||
# This job is only to make sure the microbench tests are able to run, the benchmark result is not meaningful as the CI host is changing.
|
||||
build-linux-run-microbench:
|
||||
machine:
|
||||
@@ -565,7 +472,7 @@ jobs:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-compression-libs
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS=--duration=960 blackbox_crash_test_with_atomic_flush
|
||||
- run: make V=1 -j8 CRASH_TEST_EXT_ARGS=--duration=960 blackbox_crash_test_with_atomic_flush
|
||||
- post-steps
|
||||
|
||||
build-windows:
|
||||
@@ -885,80 +792,101 @@ jobs:
|
||||
command: cd fuzz && make sst_file_writer_fuzzer db_fuzzer db_map_fuzzer
|
||||
- post-steps
|
||||
|
||||
benchmark-linux: #use a private Circle CI runner (resource_class) to run the job
|
||||
machine: true
|
||||
resource_class: facebook/rocksdb-benchmark-sys1
|
||||
steps:
|
||||
- build-for-benchmarks
|
||||
- perform-benchmarks
|
||||
- post-benchmarks
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
jobs-linux-run-tests:
|
||||
build-linux:
|
||||
jobs:
|
||||
- build-linux
|
||||
build-linux-cmake:
|
||||
jobs:
|
||||
- build-linux-cmake-with-folly
|
||||
- build-linux-gcc-7-with-folly
|
||||
- build-linux-cmake-with-benchmark
|
||||
- build-linux-encrypted_env-no_compression
|
||||
build-linux-encrypted-env:
|
||||
jobs:
|
||||
- build-linux-encrypted-env
|
||||
build-linux-shared_lib-alt_namespace-status_checked:
|
||||
jobs:
|
||||
- build-linux-shared_lib-alt_namespace-status_checked
|
||||
build-linux-lite:
|
||||
jobs:
|
||||
- build-linux-lite
|
||||
jobs-linux-run-tests-san:
|
||||
build-linux-release:
|
||||
jobs:
|
||||
- build-linux-release
|
||||
build-linux-release-rtti:
|
||||
jobs:
|
||||
- build-linux-release-rtti
|
||||
build-linux-lite-release:
|
||||
jobs:
|
||||
- build-linux-lite-release
|
||||
build-linux-clang10-asan:
|
||||
jobs:
|
||||
- build-linux-clang10-asan
|
||||
- build-linux-clang10-ubsan
|
||||
build-linux-clang10-mini-tsan:
|
||||
jobs:
|
||||
- build-linux-clang10-mini-tsan:
|
||||
start_test: ""
|
||||
end_test: "env_test"
|
||||
- build-linux-clang10-mini-tsan:
|
||||
start_test: "env_test"
|
||||
end_test: ""
|
||||
- build-linux-shared_lib-alt_namespace-status_checked
|
||||
jobs-linux-no-test-run:
|
||||
build-linux-clang10-ubsan:
|
||||
jobs:
|
||||
- build-linux-release
|
||||
- build-linux-release-rtti
|
||||
- build-linux-lite-release
|
||||
- build-examples
|
||||
- build-fuzzers
|
||||
- build-linux-clang-no_test_run
|
||||
- build-linux-clang-13-no_test_run
|
||||
- build-linux-gcc-8-no_test_run
|
||||
- build-linux-gcc-10-cxx20-no_test_run
|
||||
- build-linux-gcc-11-no_test_run
|
||||
- build-linux-arm-cmake-no_test_run
|
||||
jobs-linux-other-checks:
|
||||
- build-linux-clang10-ubsan
|
||||
build-linux-clang10-clang-analyze:
|
||||
jobs:
|
||||
- build-linux-clang10-clang-analyze
|
||||
build-linux-unity-and-headers:
|
||||
jobs:
|
||||
- build-linux-unity-and-headers
|
||||
build-linux-mini-crashtest:
|
||||
jobs:
|
||||
- build-linux-mini-crashtest
|
||||
jobs-windows:
|
||||
build-windows-vs2019:
|
||||
jobs:
|
||||
- build-windows:
|
||||
name: "build-windows-vs2019"
|
||||
build-windows-vs2019-cxx20:
|
||||
jobs:
|
||||
- build-windows:
|
||||
name: "build-windows-vs2019-cxx20"
|
||||
extra_cmake_opt: -DCMAKE_CXX_STANDARD=20
|
||||
build-windows-vs2017:
|
||||
jobs:
|
||||
- build-windows:
|
||||
name: "build-windows-vs2017"
|
||||
vs_year: "2017"
|
||||
cmake_generator: "Visual Studio 15 Win64"
|
||||
- build-cmake-mingw
|
||||
jobs-java:
|
||||
build-java:
|
||||
jobs:
|
||||
- build-linux-java
|
||||
- build-linux-java-static
|
||||
- build-macos-java
|
||||
- build-macos-java-static
|
||||
- build-macos-java-static-universal
|
||||
jobs-macos:
|
||||
build-examples:
|
||||
jobs:
|
||||
- build-examples
|
||||
build-linux-compilers-no_test_run:
|
||||
jobs:
|
||||
- build-linux-clang-no_test_run
|
||||
- build-linux-clang-13-no_test_run
|
||||
- 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
|
||||
- build-linux-arm-cmake-no_test_run
|
||||
build-macos:
|
||||
jobs:
|
||||
- build-macos
|
||||
- build-macos-cmake:
|
||||
run_even_tests: true
|
||||
- build-macos-cmake:
|
||||
run_even_tests: false
|
||||
jobs-linux-arm:
|
||||
build-cmake-mingw:
|
||||
jobs:
|
||||
- build-cmake-mingw
|
||||
build-linux-arm:
|
||||
jobs:
|
||||
- build-linux-arm
|
||||
build-fuzzers:
|
||||
@@ -967,7 +895,7 @@ workflows:
|
||||
nightly:
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: "0 9 * * *"
|
||||
cron: "0 0 * * *"
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
@@ -977,5 +905,3 @@ workflows:
|
||||
- build-linux-arm-test-full
|
||||
- build-linux-run-microbench
|
||||
- build-linux-non-shm
|
||||
- build-linux-clang-13-asan-ubsan-with-folly
|
||||
- build-linux-valgrind
|
||||
@@ -36,6 +36,7 @@ manifest_dump
|
||||
sst_dump
|
||||
blob_dump
|
||||
block_cache_trace_analyzer
|
||||
db_with_timestamp_basic_test
|
||||
tools/block_cache_analyzer/*.pyc
|
||||
column_aware_encoding_exp
|
||||
util/build_version.cc
|
||||
@@ -51,6 +52,7 @@ rocksdb_dump
|
||||
rocksdb_undump
|
||||
db_test2
|
||||
trace_analyzer
|
||||
trace_analyzer_test
|
||||
block_cache_trace_analyzer
|
||||
io_tracer_parser
|
||||
.DS_Store
|
||||
|
||||
+8
-43
@@ -40,8 +40,6 @@ include(GoogleTest)
|
||||
get_rocksdb_version(rocksdb_VERSION)
|
||||
project(rocksdb
|
||||
VERSION ${rocksdb_VERSION}
|
||||
DESCRIPTION "An embeddable persistent key-value store for fast storage"
|
||||
HOMEPAGE_URL https://rocksdb.org/
|
||||
LANGUAGES CXX C ASM)
|
||||
|
||||
if(POLICY CMP0042)
|
||||
@@ -89,11 +87,7 @@ include(CMakeDependentOption)
|
||||
if(MSVC)
|
||||
option(WITH_GFLAGS "build with GFlags" OFF)
|
||||
option(WITH_XPRESS "build with windows built in compression" OFF)
|
||||
option(ROCKSDB_SKIP_THIRDPARTY "skip thirdparty.inc" OFF)
|
||||
|
||||
if(NOT ROCKSDB_SKIP_THIRDPARTY)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
endif()
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
else()
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD" AND NOT CMAKE_SYSTEM_NAME MATCHES "kFreeBSD")
|
||||
# FreeBSD has jemalloc as default malloc
|
||||
@@ -194,7 +188,7 @@ else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes")
|
||||
endif()
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format -fno-asynchronous-unwind-tables")
|
||||
add_definitions(-D_POSIX_C_SOURCE=1)
|
||||
endif()
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
@@ -341,6 +335,9 @@ endif()
|
||||
# Reset the required flags
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
|
||||
# thread_local is part of C++11 and later (TODO: clean up this define)
|
||||
add_definitions(-DROCKSDB_SUPPORT_THREAD_LOCAL)
|
||||
|
||||
option(WITH_IOSTATS_CONTEXT "Enable IO stats context" ON)
|
||||
if (NOT WITH_IOSTATS_CONTEXT)
|
||||
add_definitions(-DNIOSTATS_CONTEXT)
|
||||
@@ -587,7 +584,6 @@ include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
if(USE_FOLLY)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third-party/folly)
|
||||
add_definitions(-DUSE_FOLLY -DFOLLY_NO_CONFIG)
|
||||
list(APPEND THIRDPARTY_LIBS glog)
|
||||
endif()
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
@@ -600,7 +596,6 @@ set(SOURCES
|
||||
cache/cache_reservation_manager.cc
|
||||
cache/clock_cache.cc
|
||||
cache/compressed_secondary_cache.cc
|
||||
cache/fast_lru_cache.cc
|
||||
cache/lru_cache.cc
|
||||
cache/sharded_cache.cc
|
||||
db/arena_wrapped_db_iter.cc
|
||||
@@ -615,7 +610,6 @@ set(SOURCES
|
||||
db/blob/blob_log_format.cc
|
||||
db/blob/blob_log_sequential_reader.cc
|
||||
db/blob/blob_log_writer.cc
|
||||
db/blob/blob_source.cc
|
||||
db/blob/prefetch_buffer_collection.cc
|
||||
db/builder.cc
|
||||
db/c.cc
|
||||
@@ -677,7 +671,6 @@ set(SOURCES
|
||||
db/version_set.cc
|
||||
db/wal_edit.cc
|
||||
db/wal_manager.cc
|
||||
db/wide/wide_column_serialization.cc
|
||||
db/write_batch.cc
|
||||
db/write_batch_base.cc
|
||||
db/write_controller.cc
|
||||
@@ -740,6 +733,7 @@ set(SOURCES
|
||||
table/adaptive/adaptive_table_factory.cc
|
||||
table/block_based/binary_search_index_reader.cc
|
||||
table/block_based/block.cc
|
||||
table/block_based/block_based_filter_block.cc
|
||||
table/block_based/block_based_table_builder.cc
|
||||
table/block_based/block_based_table_factory.cc
|
||||
table/block_based/block_based_table_iterator.cc
|
||||
@@ -802,8 +796,6 @@ set(SOURCES
|
||||
trace_replay/trace_record_result.cc
|
||||
trace_replay/trace_record.cc
|
||||
trace_replay/trace_replay.cc
|
||||
util/async_file_reader.cc
|
||||
util/cleanable.cc
|
||||
util/coding.cc
|
||||
util/compaction_job_stats_impl.cc
|
||||
util/comparator.cc
|
||||
@@ -977,13 +969,9 @@ endif()
|
||||
if(USE_FOLLY)
|
||||
list(APPEND SOURCES
|
||||
third-party/folly/folly/container/detail/F14Table.cpp
|
||||
third-party/folly/folly/detail/Futex.cpp
|
||||
third-party/folly/folly/lang/SafeAssert.cpp
|
||||
third-party/folly/folly/lang/ToAscii.cpp
|
||||
third-party/folly/folly/ScopeGuard.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/ScopeGuard.cpp)
|
||||
endif()
|
||||
|
||||
set(ROCKSDB_STATIC_LIB rocksdb${ARTIFACT_SUFFIX})
|
||||
@@ -1131,20 +1119,8 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
configure_file(
|
||||
${PROJECT_NAME}.pc.in
|
||||
${PROJECT_NAME}.pc
|
||||
@ONLY
|
||||
)
|
||||
|
||||
install(DIRECTORY include/rocksdb COMPONENT devel DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
|
||||
foreach (plugin ${PLUGINS})
|
||||
foreach (header ${${plugin}_HEADERS})
|
||||
install(FILES plugin/${plugin}/${header} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rocksdb/plugin/${plugin})
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
install(DIRECTORY "${PROJECT_SOURCE_DIR}/cmake/modules" COMPONENT devel DESTINATION ${package_config_destination})
|
||||
|
||||
install(
|
||||
@@ -1181,13 +1157,6 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
COMPONENT devel
|
||||
DESTINATION ${package_config_destination}
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
|
||||
COMPONENT devel
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
)
|
||||
endif()
|
||||
|
||||
option(WITH_ALL_TESTS "Build all test, rather than a small subset" ON)
|
||||
@@ -1218,7 +1187,6 @@ if(WITH_TESTS)
|
||||
db/blob/blob_file_garbage_test.cc
|
||||
db/blob/blob_file_reader_test.cc
|
||||
db/blob/blob_garbage_meter_test.cc
|
||||
db/blob/blob_source_test.cc
|
||||
db/blob/db_blob_basic_test.cc
|
||||
db/blob/db_blob_compaction_test.cc
|
||||
db/blob/db_blob_corruption_test.cc
|
||||
@@ -1234,7 +1202,6 @@ if(WITH_TESTS)
|
||||
db/comparator_db_test.cc
|
||||
db/corruption_test.cc
|
||||
db/cuckoo_table_db_test.cc
|
||||
db/db_readonly_with_timestamp_test.cc
|
||||
db/db_with_timestamp_basic_test.cc
|
||||
db/db_block_cache_test.cc
|
||||
db/db_bloom_filter_test.cc
|
||||
@@ -1301,7 +1268,6 @@ if(WITH_TESTS)
|
||||
db/version_set_test.cc
|
||||
db/wal_manager_test.cc
|
||||
db/wal_edit_test.cc
|
||||
db/wide/wide_column_serialization_test.cc
|
||||
db/write_batch_test.cc
|
||||
db/write_callback_test.cc
|
||||
db/write_controller_test.cc
|
||||
@@ -1327,6 +1293,7 @@ if(WITH_TESTS)
|
||||
options/customizable_test.cc
|
||||
options/options_settable_test.cc
|
||||
options/options_test.cc
|
||||
table/block_based/block_based_filter_block_test.cc
|
||||
table/block_based/block_based_table_reader_test.cc
|
||||
table/block_based/block_test.cc
|
||||
table/block_based/data_block_hash_index_test.cc
|
||||
@@ -1395,7 +1362,6 @@ if(WITH_TESTS)
|
||||
utilities/transactions/write_prepared_transaction_test.cc
|
||||
utilities/transactions/write_unprepared_transaction_test.cc
|
||||
utilities/transactions/lock/range/range_locking_test.cc
|
||||
utilities/transactions/timestamped_snapshot_test.cc
|
||||
utilities/ttl/ttl_test.cc
|
||||
utilities/util_merge_operators_test.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_test.cc
|
||||
@@ -1404,7 +1370,6 @@ if(WITH_TESTS)
|
||||
|
||||
set(TESTUTIL_SOURCE
|
||||
db/db_test_util.cc
|
||||
db/db_with_timestamp_test_util.cc
|
||||
monitoring/thread_status_updater_debug.cc
|
||||
table/mock_table.cc
|
||||
utilities/agg_merge/test_agg_merge.cc
|
||||
|
||||
+3
-115
@@ -1,124 +1,12 @@
|
||||
# Rocksdb Change Log
|
||||
## 7.4.5 (08/02/2022)
|
||||
## 7.2.2 (04/28/2022)
|
||||
### Bug Fixes
|
||||
* Fix a bug starting in 7.4.0 in which some fsync operations might be skipped in a DB after any DropColumnFamily on that DB, until it is re-opened. This can lead to data loss on power loss. (For custom FileSystem implementations, this could lead to `FSDirectory::Fsync` or `FSDirectory::Close` after the first `FSDirectory::Close`; Also, valgrind could report call to `close()` with `fd=-1`.)
|
||||
|
||||
## 7.4.4 (07/19/2022)
|
||||
### Public API changes
|
||||
* Removed Customizable support for RateLimiter and removed its CreateFromString() and Type() functions.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug where `GenericRateLimiter` could revert the bandwidth set dynamically using `SetBytesPerSecond()` when a user configures a structure enclosing it, e.g., using `GetOptionsFromString()` to configure an `Options` that references an existing `RateLimiter` object.
|
||||
|
||||
## 7.4.3 (07/13/2022)
|
||||
### Behavior Changes
|
||||
* For track_and_verify_wals_in_manifest, revert to the original behavior before #10087: syncing of live WAL file is not tracked, and we track only the synced sizes of **closed** WALs. (PR #10330).
|
||||
|
||||
## 7.4.2 (06/30/2022)
|
||||
### Bug Fixes
|
||||
* Fix a bug in Logger where if dbname and db_log_dir are on different filesystems, dbname creation would fail wrt to db_log_dir path returning an error and fails to open the DB.
|
||||
|
||||
## 7.4.1 (06/28/2022)
|
||||
### Bug Fixes
|
||||
* Pass `rate_limiter_priority` through filter block reader functions to `FileSystem`.
|
||||
|
||||
## 7.4.0 (06/19/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a bug in calculating key-value integrity protection for users of in-place memtable updates. In particular, the affected users would be those who configure `protection_bytes_per_key > 0` on `WriteBatch` or `WriteOptions`, and configure `inplace_callback != nullptr`.
|
||||
* Fixed a bug where a snapshot taken during SST file ingestion would be unstable.
|
||||
* 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, RocksDB will persist the new MANIFEST after successfully syncing the new WAL. If a future recovery starts from the new MANIFEST, then it means the new WAL is successfully synced. Due to the sentinel empty write batch at the beginning, kPointInTimeRecovery of WAL is guaranteed to go after this point. If future recovery starts from the old MANIFEST, it means the writing the new MANIFEST failed. We won't have the "SST ahead of WAL" error.
|
||||
* Fixed a bug where RocksDB DB::Open() may creates and writes to two new MANIFEST files even before recovery succeeds. Now writes to MANIFEST are persisted only after recovery is successful.
|
||||
* Fix a race condition in WAL size tracking which is caused by an unsafe iterator access after container is changed.
|
||||
* Fix unprotected concurrent accesses to `WritableFileWriter::filesize_` by `DB::SyncWAL()` and `DB::Put()` in two write queue mode.
|
||||
* Fix a bug in WAL tracking. Before this PR (#10087), calling `SyncWAL()` on the only WAL file of the db will not log the event in MANIFEST, thus allowing a subsequent `DB::Open` even if the WAL file is missing or corrupted.
|
||||
* Fix a bug that could return wrong results with `index_type=kHashSearch` and using `SetOptions` to change the `prefix_extractor`.
|
||||
* Fixed a bug in WAL tracking with wal_compression. WAL compression writes a kSetCompressionType record which is not associated with any sequence number. As result, WalManager::GetSortedWalsOfType() will skip these WALs and not return them to caller, e.g. Checkpoint, Backup, causing the operations to fail.
|
||||
* Avoid a crash if the IDENTITY file is accidentally truncated to empty. A new DB ID will be written and generated on Open.
|
||||
* Fixed a possible corruption for users of `manual_wal_flush` and/or `FlushWAL(true /* sync */)`, together with `track_and_verify_wals_in_manifest == true`. For those users, losing unsynced data (e.g., due to power loss) could make future DB opens fail with a `Status::Corruption` complaining about missing WAL data.
|
||||
* Fixed a bug in `WriteBatchInternal::Append()` where WAL termination point in write batch was not considered and the function appends an incorrect number of checksums.
|
||||
* Fixed a crash bug introduced in 7.3.0 affecting users of MultiGet with `kDataBlockBinaryAndHash`.
|
||||
* Add some fixes in async_io which was doing extra prefetching in shorter scans.
|
||||
|
||||
### Public API changes
|
||||
* Add new API GetUnixTime in Snapshot class which returns the unix time at which Snapshot is taken.
|
||||
* Add transaction `get_pinned` and `multi_get` to C API.
|
||||
* Add two-phase commit support to C API.
|
||||
* Add `rocksdb_transaction_get_writebatch_wi` and `rocksdb_transaction_rebuild_from_writebatch` to C API.
|
||||
* Add `rocksdb_options_get_blob_file_starting_level` and `rocksdb_options_set_blob_file_starting_level` to C API.
|
||||
* Add `blobFileStartingLevel` and `setBlobFileStartingLevel` to Java API.
|
||||
* Add SingleDelete for DB in C API
|
||||
* Add User Defined Timestamp in C API.
|
||||
* `rocksdb_comparator_with_ts_create` to create timestamp aware comparator
|
||||
* Put, Get, Delete, SingleDelete, MultiGet APIs has corresponding timestamp aware APIs with suffix `with_ts`
|
||||
* And Add C API's for Transaction, SstFileWriter, Compaction as mentioned [here](https://github.com/facebook/rocksdb/wiki/User-defined-Timestamp-(Experimental))
|
||||
* The contract for implementations of Comparator::IsSameLengthImmediateSuccessor has been updated to work around a design bug in `auto_prefix_mode`.
|
||||
* The API documentation for `auto_prefix_mode` now notes some corner cases in which it returns different results than `total_order_seek`, due to design bugs that are not easily fixed. Users using built-in comparators and keys at least the size of a fixed prefix length are not affected.
|
||||
* Obsoleted the NUM_DATA_BLOCKS_READ_PER_LEVEL stat and introduced the NUM_LEVEL_READ_PER_MULTIGET and MULTIGET_COROUTINE_COUNT stats
|
||||
* Introduced `WriteOptions::protection_bytes_per_key`, which can be used to enable key-value integrity protection for live updates.
|
||||
|
||||
### New Features
|
||||
* Add FileSystem::ReadAsync API in io_tracing
|
||||
* Add blob garbage collection parameters `blob_garbage_collection_policy` and `blob_garbage_collection_age_cutoff` to both force-enable and force-disable GC, as well as selectively override age cutoff when using CompactRange.
|
||||
* Add an extra sanity check in `GetSortedWalFiles()` (also used by `GetLiveFilesStorageInfo()`, `BackupEngine`, and `Checkpoint`) to reduce risk of successfully created backup or checkpoint failing to open because of missing WAL file.
|
||||
* Add a new column family option `blob_file_starting_level` to enable writing blob files during flushes and compactions starting from the specified LSM tree level.
|
||||
* Add support for timestamped snapshots (#9879)
|
||||
* Provide support for AbortIO in posix to cancel submitted asynchronous requests using io_uring.
|
||||
* Add support for rate-limiting batched `MultiGet()` APIs
|
||||
|
||||
### Behavior changes
|
||||
* DB::Open(), DB::OpenAsSecondary() will fail if a Logger cannot be created (#9984)
|
||||
* Removed support for reading Bloom filters using obsolete block-based filter format. (Support for writing such filters was dropped in 7.0.) For good read performance on old DBs using these filters, a full compaction is required.
|
||||
* Per KV checksum in write batch is verified before a write batch is written to WAL to detect any corruption to the write batch (#10114).
|
||||
|
||||
### Performance Improvements
|
||||
* When compiled with folly (Meta-internal integration; experimental in open source build), improve the locking performance (CPU efficiency) of LRUCache by using folly DistributedMutex in place of standard mutex.
|
||||
|
||||
## 7.3.0 (05/20/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a bug where manual flush would block forever even though flush options had wait=false.
|
||||
* 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.
|
||||
* 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.
|
||||
* Fixed a CompactionFilter bug. Compaction filter used to use `Delete` to remove keys, even if the keys should be removed with `SingleDelete`. Mixing `Delete` and `SingleDelete` may cause undefined behavior.
|
||||
* Fixed a bug in `WritableFileWriter::WriteDirect` and `WritableFileWriter::WriteDirectWithChecksum`. The rate_limiter_priority specified in ReadOptions was not passed to the RateLimiter when requesting a token.
|
||||
* Fixed a bug which might cause process crash when I/O error happens when reading an index block in MultiGet().
|
||||
|
||||
### New Features
|
||||
* DB::GetLiveFilesStorageInfo is ready for production use.
|
||||
* Add new stats PREFETCHED_BYTES_DISCARDED which records number of prefetched bytes discarded by RocksDB FilePrefetchBuffer on destruction and POLL_WAIT_MICROS records wait time for FS::Poll API completion.
|
||||
* RemoteCompaction supports table_properties_collector_factories override on compaction worker.
|
||||
* Start tracking SST unique id in MANIFEST, which will be used to verify with SST properties during DB open to make sure the SST file is not overwritten or misplaced. A db option `verify_sst_unique_id_in_manifest` is introduced to enable/disable the verification, if enabled all SST files will be opened during DB-open to verify the unique id (default is false), so it's recommended to use it with `max_open_files = -1` to pre-open the files.
|
||||
* Added the ability to concurrently read data blocks from multiple files in a level in batched MultiGet. This can be enabled by setting the async_io option in ReadOptions. Using this feature requires a FileSystem that supports ReadAsync (PosixFileSystem is not supported yet for this), and for RocksDB to be compiled with folly and c++20.
|
||||
* Charge memory usage of file metadata. RocksDB holds one file metadata structure in-memory per on-disk table file. If an operation reserving memory for file metadata exceeds the avaible space left in the block
|
||||
cache at some point (i.e, causing a cache full under `LRUCacheOptions::strict_capacity_limit` = true), creation will fail with `Status::MemoryLimit()`. To opt in this feature, enable charging `CacheEntryRole::kFileMetadata` in `BlockBasedTableOptions::cache_usage_options`.
|
||||
|
||||
### Public API changes
|
||||
* Add rollback_deletion_type_callback to TransactionDBOptions so that write-prepared transactions know whether to issue a Delete or SingleDelete to cancel a previous key written during prior prepare phase. The PR aims to prevent mixing SingleDeletes and Deletes for the same key that can lead to undefined behaviors for write-prepared transactions.
|
||||
* EXPERIMENTAL: Add new API AbortIO in file_system to abort the read requests submitted asynchronously.
|
||||
* CompactionFilter::Decision has a new value: kRemoveWithSingleDelete. If CompactionFilter returns this decision, then CompactionIterator will use `SingleDelete` to mark a key as removed.
|
||||
* Renamed CompactionFilter::Decision::kRemoveWithSingleDelete to kPurge since the latter sounds more general and hides the implementation details of how compaction iterator handles keys.
|
||||
* Added ability to specify functions for Prepare and Validate to OptionsTypeInfo. Added methods to OptionTypeInfo to set the functions via an API. These methods are intended for RocksDB plugin developers for configuration management.
|
||||
* Added a new immutable db options, enforce_single_del_contracts. If set to false (default is true), compaction will NOT fail due to a single delete followed by a delete for the same key. The purpose of this temporay option is to help existing use cases migrate.
|
||||
* Introduce `BlockBasedTableOptions::cache_usage_options` and use that to replace `BlockBasedTableOptions::reserve_table_builder_memory` and `BlockBasedTableOptions::reserve_table_reader_memory`.
|
||||
* Changed `GetUniqueIdFromTableProperties` to return a 128-bit unique identifier, which will be the standard size now. The old functionality (192-bit) is available from `GetExtendedUniqueIdFromTableProperties`. Both functions are no longer "experimental" and are ready for production use.
|
||||
* In IOOptions, mark `prio` as deprecated for future removal.
|
||||
* In `file_system.h`, mark `IOPriority` as deprecated for future removal.
|
||||
* Add an option, `CompressionOptions::use_zstd_dict_trainer`, to indicate whether zstd dictionary trainer should be used for generating zstd compression dictionaries. The default value of this option is true for backward compatibility. When this option is set to false, zstd API `ZDICT_finalizeDictionary` is used to generate compression dictionaries.
|
||||
* Seek API which positions itself every LevelIterator on the correct data block in the correct SST file which can be parallelized if ReadOptions.async_io option is enabled.
|
||||
* Add new stat number_async_seek in PerfContext that indicates number of async calls made by seek to prefetch data.
|
||||
* Add support for user-defined timestamps to read only DB.
|
||||
|
||||
## 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.
|
||||
* Fixed unnecessary block cache contention when queries within a MultiGet batch and across parallel batches access the same data block, which previously could cause severely degraded performance in this unusual case. (In more typical MultiGet cases, this fix is expected to yield a small or negligible performance improvement.)
|
||||
|
||||
### Behavior changes
|
||||
* Enforce the existing contract of SingleDelete so that SingleDelete cannot be mixed with Delete because it leads to undefined behavior. Fix a number of unit tests that violate the contract but happen to pass.
|
||||
* ldb `--try_load_options` default to true if `--db` is specified and not creating a new DB, the user can still explicitly disable that by `--try_load_options=false` (or explicitly enable that by `--try_load_options`).
|
||||
* During Flush write or Compaction write/read, the WriteController is used to determine whether DB writes are stalled or slowed down. The priority (Env::IOPriority) can then be determined accordingly and be passed in IOOptions to the file system.
|
||||
|
||||
### Performance Improvements
|
||||
* Avoid calling malloc_usable_size() in LRU Cache's mutex.
|
||||
* Reduce DB mutex holding time when finding obsolete files to delete. When a file is trivial moved to another level, the internal files will be referenced twice internally and sometimes opened twice too. If a deletion candidate file is not the last reference, we need to destroy the reference and close the file but not deleting the file. Right now we determine it by building a set of all live files. With the improvement, we check the file against all live LSM-tree versions instead.
|
||||
|
||||
## 7.2.0 (04/15/2022)
|
||||
### Bug Fixes
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
BASH_EXISTS := $(shell which bash)
|
||||
SHELL := $(shell which bash)
|
||||
include common.mk
|
||||
include python.mk
|
||||
|
||||
CLEAN_FILES = # deliberately empty, so we can append below.
|
||||
CFLAGS += ${EXTRA_CFLAGS}
|
||||
@@ -136,13 +136,6 @@ CXXFLAGS += $(PLATFORM_SHARED_CFLAGS) -DROCKSDB_DLL
|
||||
CFLAGS += $(PLATFORM_SHARED_CFLAGS) -DROCKSDB_DLL
|
||||
endif
|
||||
|
||||
ifeq ($(USE_COROUTINES), 1)
|
||||
USE_FOLLY = 1
|
||||
OPT += -DUSE_COROUTINES
|
||||
ROCKSDB_CXX_STANDARD = c++2a
|
||||
USE_RTTI = 1
|
||||
endif
|
||||
|
||||
# if we're compiling for release, compile without debug code (-DNDEBUG)
|
||||
ifeq ($(DEBUG_LEVEL),0)
|
||||
OPT += -DNDEBUG
|
||||
@@ -233,7 +226,6 @@ dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
|
||||
export ROCKSDB_NO_FBCODE="$(ROCKSDB_NO_FBCODE)"; \
|
||||
export USE_CLANG="$(USE_CLANG)"; \
|
||||
export LIB_MODE="$(LIB_MODE)"; \
|
||||
export ROCKSDB_CXX_STANDARD="$(ROCKSDB_CXX_STANDARD)"; \
|
||||
"$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
# this file is generated by the previous line to set build flags and sources
|
||||
include make_config.mk
|
||||
@@ -348,8 +340,6 @@ endif
|
||||
# ASAN doesn't work well with jemalloc. If we're compiling with ASAN, we should use regular malloc.
|
||||
ifdef COMPILE_WITH_ASAN
|
||||
DISABLE_JEMALLOC=1
|
||||
ASAN_OPTIONS?=detect_stack_use_after_return=1
|
||||
export ASAN_OPTIONS
|
||||
EXEC_LDFLAGS += -fsanitize=address
|
||||
PLATFORM_CCFLAGS += -fsanitize=address
|
||||
PLATFORM_CXXFLAGS += -fsanitize=address
|
||||
@@ -461,8 +451,6 @@ ifeq ($(USE_FOLLY),1)
|
||||
endif
|
||||
PLATFORM_CCFLAGS += -DUSE_FOLLY -DFOLLY_NO_CONFIG
|
||||
PLATFORM_CXXFLAGS += -DUSE_FOLLY -DFOLLY_NO_CONFIG
|
||||
# TODO: fix linking with fbcode compiler config
|
||||
PLATFORM_LDFLAGS += -lglog
|
||||
endif
|
||||
|
||||
ifdef TEST_CACHE_LINE_SIZE
|
||||
@@ -608,6 +596,9 @@ am__v_CCH_1 =
|
||||
check-headers: $(HEADER_OK_FILES)
|
||||
|
||||
# options_settable_test doesn't pass with UBSAN as we use hack in the test
|
||||
ifdef COMPILE_WITH_UBSAN
|
||||
TESTS := $(shell echo $(TESTS) | sed 's/\boptions_settable_test\b//g')
|
||||
endif
|
||||
ifdef ASSERT_STATUS_CHECKED
|
||||
# TODO: finish fixing all tests to pass this check
|
||||
TESTS_FAILING_ASC = \
|
||||
@@ -642,6 +633,7 @@ TESTS_PLATFORM_DEPENDENT := \
|
||||
db_basic_test \
|
||||
db_blob_basic_test \
|
||||
db_encryption_test \
|
||||
db_test2 \
|
||||
external_sst_file_basic_test \
|
||||
auto_roll_logger_test \
|
||||
bloom_test \
|
||||
@@ -663,6 +655,7 @@ TESTS_PLATFORM_DEPENDENT := \
|
||||
rate_limiter_test \
|
||||
perf_context_test \
|
||||
iostats_context_test \
|
||||
db_wal_test \
|
||||
|
||||
# Sort ROCKSDBTESTS_SUBSET for filtering, except db_test is special (expensive)
|
||||
# so is placed first (out-of-order)
|
||||
@@ -803,10 +796,17 @@ $(SHARED4): $(LIB_OBJECTS)
|
||||
$(AM_V_CCLD) $(CXX) $(PLATFORM_SHARED_LDFLAGS)$(SHARED3) $(LIB_OBJECTS) $(LDFLAGS) -o $@
|
||||
endif # PLATFORM_SHARED_EXT
|
||||
|
||||
.PHONY: check clean coverage ldb_tests package dbg gen-pc build_size \
|
||||
release tags tags0 valgrind_check format static_lib shared_lib all \
|
||||
rocksdbjavastatic rocksdbjava install install-static install-shared \
|
||||
uninstall analyze tools tools_lib check-headers checkout_folly
|
||||
.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 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 \
|
||||
blackbox_crash_test_with_ts whitebox_crash_test_with_ts \
|
||||
blackbox_crash_test_with_multiops_wc_txn \
|
||||
blackbox_crash_test_with_multiops_wp_txn
|
||||
|
||||
|
||||
all: $(LIBRARY) $(BENCHMARKS) tools tools_lib test_libs $(TESTS)
|
||||
|
||||
@@ -843,6 +843,18 @@ coverage: clean
|
||||
# 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),
|
||||
# and create a randomly-named rocksdb.XXXX directory therein.
|
||||
# We'll use that directory in the "make check" rules.
|
||||
ifeq ($(TMPD),)
|
||||
TMPDIR := $(shell echo $${TMPDIR:-/tmp})
|
||||
TMPD := $(shell f=/dev/shm; test -k $$f || f=$(TMPDIR); \
|
||||
perl -le 'use File::Temp "tempdir";' \
|
||||
-e 'print tempdir("'$$f'/rocksdb.XXXX", CLEANUP => 0)')
|
||||
endif
|
||||
endif
|
||||
|
||||
# Run all tests in parallel, accumulating per-test logs in t/log-*.
|
||||
#
|
||||
# Each t/run-* file is a tiny generated bourne shell script that invokes one of
|
||||
@@ -882,7 +894,7 @@ $(parallel_tests):
|
||||
TEST_SCRIPT=t/run-$$TEST_BINARY-$${TEST_NAME//\//-}; \
|
||||
printf '%s\n' \
|
||||
'#!/bin/sh' \
|
||||
"d=\$(TEST_TMPDIR)$$TEST_SCRIPT" \
|
||||
"d=\$(TMPD)$$TEST_SCRIPT" \
|
||||
'mkdir -p $$d' \
|
||||
"TEST_TMPDIR=\$$d $(DRIVER) ./$$TEST_BINARY --gtest_filter=$$TEST_NAME" \
|
||||
> $$TEST_SCRIPT; \
|
||||
@@ -942,6 +954,7 @@ endif
|
||||
|
||||
.PHONY: check_0
|
||||
check_0:
|
||||
$(AM_V_GEN)export TEST_TMPDIR=$(TMPD); \
|
||||
printf '%s\n' '' \
|
||||
'To monitor subtest <duration,pass/fail,name>,' \
|
||||
' run "make watch-log" in a separate window' ''; \
|
||||
@@ -952,8 +965,7 @@ check_0:
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| grep -E -v '$(EXCLUDE_TESTS_REGEX)' \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG --eta --gnu \
|
||||
--tmpdir=$(TEST_TMPDIR) '{} $(parallel_redir)' ; \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG --eta --gnu '{} $(parallel_redir)' ; \
|
||||
parallel_retcode=$$? ; \
|
||||
awk '{ if ($$7 != 0 || $$8 != 0) { if ($$7 == "Exitval") { h = $$0; } else { if (!f) print h; print; f = 1 } } } END { if(f) exit 1; }' < LOG ; \
|
||||
awk_retcode=$$?; \
|
||||
@@ -964,6 +976,7 @@ valgrind-exclude-regexp = InlineSkipTest.ConcurrentInsert|TransactionStressTest.
|
||||
.PHONY: valgrind_check_0
|
||||
valgrind_check_0: test_log_prefix := valgrind_
|
||||
valgrind_check_0:
|
||||
$(AM_V_GEN)export TEST_TMPDIR=$(TMPD); \
|
||||
printf '%s\n' '' \
|
||||
'To monitor subtest <duration,pass/fail,name>,' \
|
||||
' run "make watch-log" in a separate window' ''; \
|
||||
@@ -975,11 +988,10 @@ valgrind_check_0:
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| grep -E -v '$(valgrind-exclude-regexp)' \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG --eta --gnu \
|
||||
--tmpdir=$(TEST_TMPDIR) \
|
||||
'(if [[ "{}" == "./"* ]] ; then $(DRIVER) {}; else {}; fi) \
|
||||
'(if [[ "{}" == "./"* ]] ; then $(DRIVER) {}; else {}; fi) \
|
||||
$(parallel_redir)' \
|
||||
|
||||
CLEAN_FILES += t LOG $(TEST_TMPDIR)
|
||||
CLEAN_FILES += t LOG $(TMPD)
|
||||
|
||||
# When running parallel "make check", you can monitor its progress
|
||||
# from another window.
|
||||
@@ -1002,12 +1014,12 @@ check: all
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
$(MAKE) T="$$t" check_0; \
|
||||
$(MAKE) T="$$t" TMPD=$(TMPD) check_0; \
|
||||
else \
|
||||
for t in $(TESTS); do \
|
||||
echo "===== Running $$t (`date`)"; ./$$t || exit 1; done; \
|
||||
fi
|
||||
rm -rf $(TEST_TMPDIR)
|
||||
rm -rf $(TMPD)
|
||||
ifneq ($(PLATFORM), OS_AIX)
|
||||
$(PYTHON) tools/check_all_python.py
|
||||
ifeq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
@@ -1034,31 +1046,31 @@ ldb_tests: ldb
|
||||
include crash_test.mk
|
||||
|
||||
asan_check: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) check -j32
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) check -j32
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
whitebox_asan_crash_test: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) whitebox_crash_test
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) whitebox_crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
blackbox_asan_crash_test: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) blackbox_crash_test
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) blackbox_crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_atomic_flush: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_atomic_flush
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_atomic_flush
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_txn: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_txn
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_txn
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_best_efforts_recovery: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_best_efforts_recovery
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_best_efforts_recovery
|
||||
$(MAKE) clean
|
||||
|
||||
ubsan_check: clean
|
||||
@@ -1104,11 +1116,11 @@ valgrind_test_some:
|
||||
valgrind_check: $(TESTS)
|
||||
$(MAKE) DRIVER="$(VALGRIND_VER) $(VALGRIND_OPTS)" gen_parallel_tests
|
||||
$(AM_V_GEN)if test "$(J)" != 1 \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
$(MAKE) \
|
||||
DRIVER="$(VALGRIND_VER) $(VALGRIND_OPTS)" valgrind_check_0; \
|
||||
$(MAKE) TMPD=$(TMPD) \
|
||||
DRIVER="$(VALGRIND_VER) $(VALGRIND_OPTS)" valgrind_check_0; \
|
||||
else \
|
||||
for t in $(filter-out %skiplist_test options_settable_test,$(TESTS)); do \
|
||||
$(VALGRIND_VER) $(VALGRIND_OPTS) ./$$t; \
|
||||
@@ -1128,6 +1140,27 @@ valgrind_check_some: $(ROCKSDBTESTS_SUBSET)
|
||||
fi; \
|
||||
done
|
||||
|
||||
ifneq ($(PAR_TEST),)
|
||||
parloop:
|
||||
ret_bad=0; \
|
||||
for t in $(PAR_TEST); do \
|
||||
echo "===== Running $$t in parallel $(NUM_PAR) (`date`)";\
|
||||
if [ $(db_test) -eq 1 ]; then \
|
||||
seq $(J) | v="$$t" build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{}; export TEST_TMPDIR=$$s;' \
|
||||
'timeout 2m ./db_test --gtest_filter=$$v >> $$s/log-{} 2>1'; \
|
||||
else\
|
||||
seq $(J) | v="./$$t" build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{};' \
|
||||
'export TEST_TMPDIR=$$s; timeout 10m $$v >> $$s/log-{} 2>1'; \
|
||||
fi; \
|
||||
ret_code=$$?; \
|
||||
if [ $$ret_code -ne 0 ]; then \
|
||||
ret_bad=$$ret_code; \
|
||||
echo $$t exited with $$ret_code; \
|
||||
fi; \
|
||||
done; \
|
||||
exit $$ret_bad;
|
||||
endif
|
||||
|
||||
test_names = \
|
||||
./db_test --gtest_list_tests \
|
||||
| perl -n \
|
||||
@@ -1135,6 +1168,24 @@ test_names = \
|
||||
-e '/^(\s*)(\S+)/; !$$1 and do {$$p=$$2; break};' \
|
||||
-e 'print qq! $$p$$2!'
|
||||
|
||||
parallel_check: $(TESTS)
|
||||
$(AM_V_GEN)if test "$(J)" > 1 \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
echo Running in parallel $(J); \
|
||||
else \
|
||||
echo "Need to have GNU Parallel and J > 1"; exit 1; \
|
||||
fi; \
|
||||
ret_bad=0; \
|
||||
echo $(J);\
|
||||
echo Test Dir: $(TMPD); \
|
||||
seq $(J) | build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{}; rm -rf $$s; mkdir $$s'; \
|
||||
$(MAKE) PAR_TEST="$(shell $(test_names))" TMPD=$(TMPD) \
|
||||
J=$(J) db_test=1 parloop; \
|
||||
$(MAKE) PAR_TEST="$(filter-out db_test, $(TESTS))" \
|
||||
TMPD=$(TMPD) J=$(J) db_test=0 parloop;
|
||||
|
||||
analyze: clean
|
||||
USE_CLANG=1 $(MAKE) analyze_incremental
|
||||
|
||||
@@ -1380,9 +1431,6 @@ db_blob_basic_test: $(OBJ_DIR)/db/blob/db_blob_basic_test.o $(TEST_LIBRARY) $(LI
|
||||
db_blob_compaction_test: $(OBJ_DIR)/db/blob/db_blob_compaction_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_readonly_with_timestamp_test: $(OBJ_DIR)/db/db_readonly_with_timestamp_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_with_timestamp_basic_test: $(OBJ_DIR)/db/db_with_timestamp_basic_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1596,6 +1644,9 @@ random_access_file_reader_test: $(OBJ_DIR)/file/random_access_file_reader_test.o
|
||||
file_reader_writer_test: $(OBJ_DIR)/util/file_reader_writer_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
block_based_filter_block_test: $(OBJ_DIR)/table/block_based/block_based_filter_block_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
block_based_table_reader_test: table/block_based/block_based_table_reader_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1776,9 +1827,6 @@ write_prepared_transaction_test: $(OBJ_DIR)/utilities/transactions/write_prepare
|
||||
write_unprepared_transaction_test: $(OBJ_DIR)/utilities/transactions/write_unprepared_transaction_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
timestamped_snapshot_test: $(OBJ_DIR)/utilities/transactions/timestamped_snapshot_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
sst_dump: $(OBJ_DIR)/tools/sst_dump.o $(TOOLS_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1863,9 +1911,6 @@ blob_file_garbage_test: $(OBJ_DIR)/db/blob/blob_file_garbage_test.o $(TEST_LIBRA
|
||||
blob_file_reader_test: $(OBJ_DIR)/db/blob/blob_file_reader_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_source_test: $(OBJ_DIR)/db/blob/blob_source_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_garbage_meter_test: $(OBJ_DIR)/db/blob/blob_garbage_meter_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1881,7 +1926,7 @@ testutil_test: $(OBJ_DIR)/test_util/testutil_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
io_tracer_test: $(OBJ_DIR)/trace_replay/io_tracer_test.o $(OBJ_DIR)/trace_replay/io_tracer.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
prefetch_test: $(OBJ_DIR)/file/prefetch_test.o $(OBJ_DIR)/tools/io_tracer_parser_tool.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
prefetch_test: $(OBJ_DIR)/file/prefetch_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
io_tracer_parser_test: $(OBJ_DIR)/tools/io_tracer_parser_test.o $(OBJ_DIR)/tools/io_tracer_parser_tool.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
@@ -1907,10 +1952,6 @@ db_basic_bench: $(OBJ_DIR)/microbench/db_basic_bench.o $(LIBRARY)
|
||||
|
||||
cache_reservation_manager_test: $(OBJ_DIR)/cache/cache_reservation_manager_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
wide_column_serialization_test: $(OBJ_DIR)/db/wide/wide_column_serialization_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
PREFIX ?= /usr/local
|
||||
@@ -2073,7 +2114,6 @@ ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
ROCKSDBJNILIB = librocksdbjni-openbsd$(ARCH).so
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-openbsd$(ARCH).jar
|
||||
endif
|
||||
export SHA256_CMD
|
||||
|
||||
zlib-$(ZLIB_VER).tar.gz:
|
||||
curl --fail --output zlib-$(ZLIB_VER).tar.gz --location ${ZLIB_DOWNLOAD_BASE}/zlib-$(ZLIB_VER).tar.gz
|
||||
@@ -2206,7 +2246,7 @@ JAR_CMD := jar
|
||||
endif
|
||||
endif
|
||||
rocksdbjavastatic_javalib:
|
||||
cd java; $(MAKE) javalib
|
||||
cd java; SHA256_CMD='$(SHA256_CMD)' $(MAKE) javalib
|
||||
rm -f java/target/$(ROCKSDBJNILIB)
|
||||
$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC \
|
||||
-o ./java/target/$(ROCKSDBJNILIB) $(ALL_JNI_NATIVE_SOURCES) \
|
||||
@@ -2320,7 +2360,7 @@ rocksdbjava: $(LIB_OBJECTS)
|
||||
ifeq ($(JAVA_HOME),)
|
||||
$(error JAVA_HOME is not set)
|
||||
endif
|
||||
$(AM_V_GEN)cd java; $(MAKE) javalib;
|
||||
$(AM_V_GEN)cd java; SHA256_CMD='$(SHA256_CMD)' $(MAKE) javalib;
|
||||
$(AM_V_at)rm -f ./java/target/$(ROCKSDBJNILIB)
|
||||
$(AM_V_at)$(CXX) $(CXXFLAGS) -I./java/. -I./java/rocksjni $(JAVA_INCLUDE) $(ROCKSDB_PLUGIN_JNI_CXX_INCLUDEFLAGS) -shared -fPIC -o ./java/target/$(ROCKSDBJNILIB) $(ALL_JNI_NATIVE_SOURCES) $(LIB_OBJECTS) $(JAVA_LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_V_at)cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
@@ -2332,13 +2372,14 @@ jclean:
|
||||
cd java;$(MAKE) clean;
|
||||
|
||||
jtest_compile: rocksdbjava
|
||||
cd java;$(MAKE) java_test
|
||||
cd java; SHA256_CMD='$(SHA256_CMD)' $(MAKE) java_test
|
||||
|
||||
jtest_run:
|
||||
cd java;$(MAKE) run_test
|
||||
|
||||
jtest: rocksdbjava
|
||||
cd java;$(MAKE) sample test
|
||||
cd java;$(MAKE) sample; SHA256_CMD='$(SHA256_CMD)' $(MAKE) test;
|
||||
$(PYTHON) tools/check_all_python.py # TODO peterd: find a better place for this check in CI targets
|
||||
|
||||
jdb_bench:
|
||||
cd java;$(MAKE) db_bench;
|
||||
@@ -2359,46 +2400,10 @@ checkout_folly:
|
||||
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 beacd86d63cd71c904632262e6c36f60874d78ba
|
||||
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
|
||||
@# NOTE: this hack is required for clang in some cases
|
||||
perl -pi -e 's/int rv = syscall/int rv = (int)syscall/' third-party/folly/folly/detail/Futex.cpp
|
||||
@# NOTE: this hack is required for gcc in some cases
|
||||
perl -pi -e 's/(__has_include.<experimental.memory_resource>.)/__cpp_rtti && $$1/' third-party/folly/folly/memory/MemoryResource.h
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Build size testing
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
REPORT_BUILD_STATISTIC?=echo STATISTIC:
|
||||
|
||||
build_size:
|
||||
# === normal build, static ===
|
||||
$(MAKE) clean
|
||||
$(MAKE) static_lib
|
||||
$(REPORT_BUILD_STATISTIC) rocksdb.build_size.static_lib $$(stat --printf="%s" librocksdb.a)
|
||||
strip librocksdb.a
|
||||
$(REPORT_BUILD_STATISTIC) rocksdb.build_size.static_lib_stripped $$(stat --printf="%s" librocksdb.a)
|
||||
# === normal build, shared ===
|
||||
$(MAKE) clean
|
||||
$(MAKE) shared_lib
|
||||
$(REPORT_BUILD_STATISTIC) rocksdb.build_size.shared_lib $$(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
strip `readlink -f librocksdb.so`
|
||||
$(REPORT_BUILD_STATISTIC) rocksdb.build_size.shared_lib_stripped $$(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
# === lite build, static ===
|
||||
$(MAKE) clean
|
||||
$(MAKE) LITE=1 static_lib
|
||||
$(REPORT_BUILD_STATISTIC) rocksdb.build_size.static_lib_lite $$(stat --printf="%s" librocksdb.a)
|
||||
strip librocksdb.a
|
||||
$(REPORT_BUILD_STATISTIC) rocksdb.build_size.static_lib_lite_stripped $$(stat --printf="%s" librocksdb.a)
|
||||
# === lite build, shared ===
|
||||
$(MAKE) clean
|
||||
$(MAKE) LITE=1 shared_lib
|
||||
$(REPORT_BUILD_STATISTIC) rocksdb.build_size.shared_lib_lite $$(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
strip `readlink -f librocksdb.so`
|
||||
$(REPORT_BUILD_STATISTIC) rocksdb.build_size.shared_lib_lite_stripped $$(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Platform-specific compilation
|
||||
|
||||
@@ -4,4 +4,3 @@ This is the list of all known third-party plugins for RocksDB. If something is m
|
||||
* [HDFS](https://github.com/riversand963/rocksdb-hdfs-env): an Env used for interacting with HDFS. Migrated from main RocksDB repo
|
||||
* [ZenFS](https://github.com/westerndigitalcorporation/zenfs): a file system for zoned block devices
|
||||
* [RADOS](https://github.com/riversand963/rocksdb-rados-env): an Env used for interacting with RADOS. Migrated from RocksDB main repo.
|
||||
* [PMEM](https://github.com/pmem/pmem-rocksdb-plugin): a collection of plugins to enable Persistent Memory on RocksDB.
|
||||
@@ -15,7 +15,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"cache/cache_reservation_manager.cc",
|
||||
"cache/clock_cache.cc",
|
||||
"cache/compressed_secondary_cache.cc",
|
||||
"cache/fast_lru_cache.cc",
|
||||
"cache/lru_cache.cc",
|
||||
"cache/sharded_cache.cc",
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
@@ -30,7 +29,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"db/blob/blob_log_format.cc",
|
||||
"db/blob/blob_log_sequential_reader.cc",
|
||||
"db/blob/blob_log_writer.cc",
|
||||
"db/blob/blob_source.cc",
|
||||
"db/blob/prefetch_buffer_collection.cc",
|
||||
"db/builder.cc",
|
||||
"db/c.cc",
|
||||
@@ -92,7 +90,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"db/version_set.cc",
|
||||
"db/wal_edit.cc",
|
||||
"db/wal_manager.cc",
|
||||
"db/wide/wide_column_serialization.cc",
|
||||
"db/write_batch.cc",
|
||||
"db/write_batch_base.cc",
|
||||
"db/write_controller.cc",
|
||||
@@ -166,6 +163,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"table/adaptive/adaptive_table_factory.cc",
|
||||
"table/block_based/binary_search_index_reader.cc",
|
||||
"table/block_based/block.cc",
|
||||
"table/block_based/block_based_filter_block.cc",
|
||||
"table/block_based/block_based_table_builder.cc",
|
||||
"table/block_based/block_based_table_factory.cc",
|
||||
"table/block_based/block_based_table_iterator.cc",
|
||||
@@ -225,9 +223,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"trace_replay/trace_record_handler.cc",
|
||||
"trace_replay/trace_record_result.cc",
|
||||
"trace_replay/trace_replay.cc",
|
||||
"util/async_file_reader.cc",
|
||||
"util/build_version.cc",
|
||||
"util/cleanable.cc",
|
||||
"util/coding.cc",
|
||||
"util/compaction_job_stats_impl.cc",
|
||||
"util/comparator.cc",
|
||||
@@ -329,14 +325,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"utilities/wal_filter.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index_internal.cc",
|
||||
], deps=[
|
||||
"//folly/container:f14_hash",
|
||||
"//folly/experimental/coro:blocking_wait",
|
||||
"//folly/experimental/coro:collect",
|
||||
"//folly/experimental/coro:coroutine",
|
||||
"//folly/experimental/coro:task",
|
||||
"//folly/synchronization:distributed_mutex",
|
||||
], headers=None, link_whole=False, extra_test_libs=False)
|
||||
], deps=["//folly/container:f14_hash"], headers=None, link_whole=False, extra_test_libs=False)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[
|
||||
"cache/cache.cc",
|
||||
@@ -345,7 +334,6 @@ cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[
|
||||
"cache/cache_reservation_manager.cc",
|
||||
"cache/clock_cache.cc",
|
||||
"cache/compressed_secondary_cache.cc",
|
||||
"cache/fast_lru_cache.cc",
|
||||
"cache/lru_cache.cc",
|
||||
"cache/sharded_cache.cc",
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
@@ -360,7 +348,6 @@ cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[
|
||||
"db/blob/blob_log_format.cc",
|
||||
"db/blob/blob_log_sequential_reader.cc",
|
||||
"db/blob/blob_log_writer.cc",
|
||||
"db/blob/blob_source.cc",
|
||||
"db/blob/prefetch_buffer_collection.cc",
|
||||
"db/builder.cc",
|
||||
"db/c.cc",
|
||||
@@ -422,7 +409,6 @@ cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[
|
||||
"db/version_set.cc",
|
||||
"db/wal_edit.cc",
|
||||
"db/wal_manager.cc",
|
||||
"db/wide/wide_column_serialization.cc",
|
||||
"db/write_batch.cc",
|
||||
"db/write_batch_base.cc",
|
||||
"db/write_controller.cc",
|
||||
@@ -496,6 +482,7 @@ cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[
|
||||
"table/adaptive/adaptive_table_factory.cc",
|
||||
"table/block_based/binary_search_index_reader.cc",
|
||||
"table/block_based/block.cc",
|
||||
"table/block_based/block_based_filter_block.cc",
|
||||
"table/block_based/block_based_table_builder.cc",
|
||||
"table/block_based/block_based_table_factory.cc",
|
||||
"table/block_based/block_based_table_iterator.cc",
|
||||
@@ -555,9 +542,7 @@ cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[
|
||||
"trace_replay/trace_record_handler.cc",
|
||||
"trace_replay/trace_record_result.cc",
|
||||
"trace_replay/trace_replay.cc",
|
||||
"util/async_file_reader.cc",
|
||||
"util/build_version.cc",
|
||||
"util/cleanable.cc",
|
||||
"util/coding.cc",
|
||||
"util/compaction_job_stats_impl.cc",
|
||||
"util/comparator.cc",
|
||||
@@ -659,18 +644,10 @@ cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[
|
||||
"utilities/wal_filter.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index_internal.cc",
|
||||
], deps=[
|
||||
"//folly/container:f14_hash",
|
||||
"//folly/experimental/coro:blocking_wait",
|
||||
"//folly/experimental/coro:collect",
|
||||
"//folly/experimental/coro:coroutine",
|
||||
"//folly/experimental/coro:task",
|
||||
"//folly/synchronization:distributed_mutex",
|
||||
], headers=None, link_whole=True, extra_test_libs=False)
|
||||
], deps=["//folly/container:f14_hash"], headers=None, link_whole=True, extra_test_libs=False)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_test_lib", srcs=[
|
||||
"db/db_test_util.cc",
|
||||
"db/db_with_timestamp_test_util.cc",
|
||||
"table/mock_table.cc",
|
||||
"test_util/mock_time_env.cc",
|
||||
"test_util/testharness.cc",
|
||||
@@ -4802,8 +4779,8 @@ cpp_unittest_wrapper(name="blob_garbage_meter_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="blob_source_test",
|
||||
srcs=["db/blob/blob_source_test.cc"],
|
||||
cpp_unittest_wrapper(name="block_based_filter_block_test",
|
||||
srcs=["table/block_based/block_based_filter_block_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
@@ -5180,12 +5157,6 @@ cpp_unittest_wrapper(name="db_rate_limiter_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="db_readonly_with_timestamp_test",
|
||||
srcs=["db/db_readonly_with_timestamp_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="db_secondary_test",
|
||||
srcs=["db/db_secondary_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
@@ -5770,12 +5741,6 @@ cpp_unittest_wrapper(name="timer_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="timestamped_snapshot_test",
|
||||
srcs=["utilities/transactions/timestamped_snapshot_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="trace_analyzer_test",
|
||||
srcs=["tools/trace_analyzer_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
@@ -5824,12 +5789,6 @@ cpp_unittest_wrapper(name="wal_manager_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="wide_column_serialization_test",
|
||||
srcs=["db/wide/wide_column_serialization_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="work_queue_test",
|
||||
srcs=["util/work_queue_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
|
||||
@@ -145,13 +145,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=[
|
||||
"//folly/container:f14_hash",
|
||||
"//folly/experimental/coro:blocking_wait",
|
||||
"//folly/experimental/coro:collect",
|
||||
"//folly/experimental/coro:coroutine",
|
||||
"//folly/experimental/coro:task",
|
||||
"//folly/synchronization:distributed_mutex"])
|
||||
deps=["//folly/container:f14_hash"])
|
||||
# rocksdb_whole_archive_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_whole_archive_lib",
|
||||
@@ -159,13 +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=[
|
||||
"//folly/container:f14_hash",
|
||||
"//folly/experimental/coro:blocking_wait",
|
||||
"//folly/experimental/coro:collect",
|
||||
"//folly/experimental/coro:coroutine",
|
||||
"//folly/experimental/coro:task",
|
||||
"//folly/synchronization:distributed_mutex"],
|
||||
deps=["//folly/container:f14_hash"],
|
||||
headers=None,
|
||||
extra_external_deps="",
|
||||
link_whole=True)
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
# This source code is licensed under both the GPLv2 (found in the
|
||||
# COPYING file in the root directory) and Apache 2.0 License
|
||||
# (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
'''Access the results of benchmark runs
|
||||
Send these results on to OpenSearch graphing service
|
||||
'''
|
||||
|
||||
import argparse
|
||||
import itertools
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import requests
|
||||
from dateutil import parser
|
||||
import logging
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
class Configuration:
|
||||
opensearch_user = os.environ['ES_USER']
|
||||
opensearch_pass = os.environ['ES_PASS']
|
||||
|
||||
class BenchmarkResultException(Exception):
|
||||
def __init__(self, message, content):
|
||||
super().__init__(self, message)
|
||||
self.content = content
|
||||
|
||||
|
||||
class BenchmarkUtils:
|
||||
|
||||
expected_keys = ['ops_sec', 'mb_sec', 'total_size_gb', 'level0_size_gb', 'sum_gb', 'write_amplification',
|
||||
'write_mbps', 'usec_op', 'percentile_50', 'percentile_75',
|
||||
'percentile_99', 'percentile_99.9', 'percentile_99.99', 'uptime',
|
||||
'stall_time', 'stall_percent', 'test_name', 'test_date', 'rocksdb_version',
|
||||
'job_id', 'timestamp']
|
||||
|
||||
metric_keys = ['ops_sec', 'mb_sec', 'total_size_gb', 'level0_size_gb', 'sum_gb', 'write_amplification',
|
||||
'write_mbps', 'usec_op', 'percentile_50', 'percentile_75',
|
||||
'percentile_99', 'percentile_99.9', 'percentile_99.99', 'uptime',
|
||||
'stall_time', 'stall_percent']
|
||||
|
||||
def sanity_check(row):
|
||||
if not 'test_name' in row:
|
||||
return False
|
||||
if row['test_name'] == '':
|
||||
return False
|
||||
if not 'test_date' in row:
|
||||
return False
|
||||
if not 'ops_sec' in row:
|
||||
return False
|
||||
try:
|
||||
v = int(row['ops_sec'])
|
||||
except (ValueError, TypeError):
|
||||
return False
|
||||
return True
|
||||
|
||||
def conform_opensearch(row):
|
||||
(dt, _) = parser.parse(row['test_date'], fuzzy_with_tokens=True)
|
||||
row['test_date'] = dt.isoformat()
|
||||
return dict((key.replace('.', '_'), value)
|
||||
for (key, value) in row.items())
|
||||
|
||||
|
||||
class ResultParser:
|
||||
def __init__(self, field="(\w|[+-:.])+", intrafield="(\s)+", separator="\t"):
|
||||
self.field = re.compile(field)
|
||||
self.intra = re.compile(intrafield)
|
||||
self.sep = re.compile(separator)
|
||||
|
||||
def line(self, l_in: str):
|
||||
'''Parse a line into items
|
||||
Being clever about separators
|
||||
'''
|
||||
l = l_in
|
||||
row = []
|
||||
while l != '':
|
||||
match_item = self.field.match(l)
|
||||
if match_item:
|
||||
item = match_item.group(0)
|
||||
row.append(item)
|
||||
l = l[len(item):]
|
||||
else:
|
||||
match_intra = self.intra.match(l)
|
||||
if match_intra:
|
||||
intra = match_intra.group(0)
|
||||
# Count the separators
|
||||
# If there are >1 then generate extra blank fields
|
||||
# White space with no true separators fakes up a single separator
|
||||
tabbed = self.sep.split(intra)
|
||||
sep_count = len(tabbed) - 1
|
||||
if sep_count == 0:
|
||||
sep_count = 1
|
||||
for i in range(sep_count-1):
|
||||
row.append('')
|
||||
l = l[len(intra):]
|
||||
else:
|
||||
raise BenchmarkResultException(
|
||||
'Invalid TSV line', f"{l_in} at {l}")
|
||||
return row
|
||||
|
||||
def parse(self, lines):
|
||||
'''Parse something that iterates lines'''
|
||||
rows = [self.line(line) for line in lines]
|
||||
header = rows[0]
|
||||
width = len(header)
|
||||
records = [{k: v for (k, v) in itertools.zip_longest(
|
||||
header, row[:width])} for row in rows[1:]]
|
||||
return records
|
||||
|
||||
|
||||
def load_report_from_tsv(filename: str):
|
||||
file = open(filename, 'r')
|
||||
contents = file.readlines()
|
||||
file.close()
|
||||
parser = ResultParser()
|
||||
report = parser.parse(contents)
|
||||
logging.debug(f"Loaded TSV Report: {report}")
|
||||
return report
|
||||
|
||||
|
||||
def push_report_to_opensearch(report, esdocument):
|
||||
sanitized = [BenchmarkUtils.conform_opensearch(row)
|
||||
for row in report if BenchmarkUtils.sanity_check(row)]
|
||||
logging.debug(f"upload {len(sanitized)} benchmarks to opensearch")
|
||||
for single_benchmark in sanitized:
|
||||
logging.debug(f"upload benchmark: {single_benchmark}")
|
||||
response = requests.post(
|
||||
esdocument,
|
||||
json=single_benchmark, auth=(os.environ['ES_USER'], os.environ['ES_PASS']))
|
||||
logging.debug(
|
||||
f"Sent to OpenSearch, status: {response.status_code}, result: {response.text}")
|
||||
response.raise_for_status()
|
||||
|
||||
|
||||
def main():
|
||||
'''Tool for fetching, parsing and uploading benchmark results to OpenSearch / ElasticSearch
|
||||
This tool will
|
||||
|
||||
(1) Open a local tsv benchmark report file
|
||||
(2) Upload to OpenSearch document, via https/JSON
|
||||
'''
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='CircleCI benchmark scraper.')
|
||||
|
||||
# --tsvfile is the name of the file to read results from
|
||||
# --esdocument is the ElasticSearch document to push these results into
|
||||
#
|
||||
parser.add_argument('--tsvfile', default='build_tools/circle_api_scraper_input.txt',
|
||||
help='File from which to read tsv report')
|
||||
parser.add_argument('--esdocument', help='ElasticSearch/OpenSearch document URL to upload report into')
|
||||
|
||||
args = parser.parse_args()
|
||||
logging.debug(f"Arguments: {args}")
|
||||
reports = load_report_from_tsv(args.tsvfile)
|
||||
push_report_to_opensearch(reports, args.esdocument)
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
@@ -469,7 +469,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_MEMKIND; then
|
||||
# Test whether memkind library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $LDFLAGS -x c++ - -o test.o -lmemkind 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -lmemkind -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <memkind.h>
|
||||
int main() {
|
||||
memkind_malloc(MEMKIND_DAX_KMEM, 1024);
|
||||
@@ -800,6 +800,9 @@ if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_UINT128_EXTENSION"
|
||||
fi
|
||||
|
||||
# thread_local is part of C++11 and later (TODO: clean up this define)
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_SUPPORT_THREAD_LOCAL"
|
||||
|
||||
if [ "$FBCODE_BUILD" != "true" -a "$PLATFORM" = OS_LINUX ]; then
|
||||
$CXX $COMMON_FLAGS $PLATFORM_SHARED_CFLAGS -x c++ -c - -o test_dl.o 2>/dev/null <<EOF
|
||||
void dummy_func() {}
|
||||
|
||||
@@ -5,31 +5,25 @@
|
||||
|
||||
BAD=""
|
||||
|
||||
git grep -n 'namespace rocksdb' -- '*.[ch]*'
|
||||
git grep 'namespace rocksdb' -- '*.[ch]*'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo "^^^^^ Do not hardcode namespace rocksdb. Use ROCKSDB_NAMESPACE"
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n -i 'nocommit' -- ':!build_tools/check-sources.sh'
|
||||
git grep -i 'nocommit' -- ':!build_tools/check-sources.sh'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo "^^^^^ Code was not intended to be committed"
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n 'include <rocksdb/' -- ':!build_tools/check-sources.sh'
|
||||
git grep '<rocksdb/' -- ':!build_tools/check-sources.sh'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo '^^^^^ Use double-quotes as in #include "rocksdb/something.h"'
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n 'include "include/rocksdb/' -- ':!build_tools/check-sources.sh'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo '^^^^^ Use #include "rocksdb/something.h" instead of #include "include/rocksdb/something.h"'
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n 'using namespace' -- ':!build_tools' ':!docs' \
|
||||
git grep 'using namespace' -- ':!build_tools' ':!docs' \
|
||||
':!third-party/folly/folly/lang/Align.h' \
|
||||
':!third-party/gtest-1.8.1/fused-src/gtest/gtest.h'
|
||||
if [ "$?" != "1" ]; then
|
||||
@@ -37,12 +31,6 @@ if [ "$?" != "1" ]; then
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n -P "[\x80-\xFF]" -- ':!docs' ':!*.md'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo '^^^^ Use only ASCII characters in source files'
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
if [ "$BAD" ]; then
|
||||
exit 1
|
||||
fi
|
||||
@@ -9,7 +9,7 @@ BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/73a237ac5bc0a5f5d67b39b8d253cfebaab88684
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/ec6573523b0ce55ef6373a4801189027cf07bb2c/1.9.1/platform009/7f3b187
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/64c58a207d2495e83abc57a500a956df09b79a7c/1.4.x/platform009/ba86d1f
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/824d0a8a5abb5b121afd1b35fc3896407ea50092/2.2.0/platform009/7f3b187
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/b62912d333ef33f9760efa6219dbe3fe6abb3b0e/master/platform009/c305944
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/d9aef9feb850b168a68736420f217b01cce11a89/master/platform009/c305944
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/0af65f71e23a67bf65dc91b11f95caa39325c432/2.0.11/platform009/7f3b187
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/02486dac347645d31dce116f44e1de3177315be2/1.4/platform009/5191652
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/2e0ec671e550bfca347300bf3f789d9c0fff24ad/2018_U5/platform009/7f3b187
|
||||
@@ -20,7 +20,3 @@ VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/6ae525939ad02e5e676855082fbbc7828d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/162efd9561a3d21f6869f4814011e9cf1b3ff4dc/5.3.4/platform009/a6271c4
|
||||
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
|
||||
GLOG_BASE=/mnt/gvfs/third-party2/glog/32d751bd5673375b438158717ab6a57c1cc57e3d/0.3.2_fb/platform009/10a364d/
|
||||
FMT_BASE=/mnt/gvfs/third-party2/fmt/ce0c25f67165f4d2c22a29b8ef50f5600d7873ca/6.1.1/platform009/7f3b187/
|
||||
DBL_CONV_BASE=/mnt/gvfs/third-party2/double_conversion/109b3d9696d71f1048678cd7da1e22505470543d/20141126/platform009/7f3b187/
|
||||
LIBEVENT_BASE=/mnt/gvfs/third-party2/libevent/4a4d3a79a76c2439b6bd471bf3586b3481dde75e/1.4.14b_hphp/platform009/7f3b187/
|
||||
@@ -21,48 +21,38 @@ LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_SNAPPY; then
|
||||
# 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
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
# 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
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
if ! test $ROCKSDB_DISABLE_ZLIB; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
fi
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_BZIP; then
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
fi
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_LZ4; then
|
||||
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.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZSTD; then
|
||||
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
|
||||
CFLAGS+=" -DZSTD -DZSTD_STATIC_LINKING_ONLY"
|
||||
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
|
||||
CFLAGS+=" -DZSTD -DZSTD_STATIC_LINKING_ONLY"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
@@ -147,7 +137,7 @@ else
|
||||
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 -DHAVE_SSE42"
|
||||
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"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
|
||||
@@ -14,7 +14,7 @@ source "$BASEDIR/dependencies_platform009.sh"
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/9.3.0 -I $LIBGCC_BASE/include/c++/9.3.0/backward"
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/9.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
@@ -27,38 +27,28 @@ else
|
||||
MAYBE_PIC=_pic
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_SNAPPY; then
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
fi
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZLIB; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
fi
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_BZIP; then
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
fi
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_LZ4; then
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZSTD; then
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
fi
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
@@ -70,18 +60,6 @@ BENCHMARK_LIBS=" $BENCHMARK_BASE/lib/libbenchmark${MAYBE_PIC}.a"
|
||||
|
||||
BOOST_INCLUDE=" -I $BOOST_BASE/include/"
|
||||
|
||||
GLOG_INCLUDE=" -I $GLOG_BASE/include/"
|
||||
GLOG_LIBS=" $GLOG_BASE/lib/libglog${MAYBE_PIC}.a"
|
||||
|
||||
FMT_INCLUDE=" -I $FMT_BASE/include/"
|
||||
FMT_LIBS=" $FMT_BASE/lib/libfmt${MAYBE_PIC}.a"
|
||||
|
||||
DBL_CONV_INCLUDE=" -I $DBL_CONV_BASE/include/"
|
||||
DBL_CONV_LIBS=" $DBL_CONV_BASE/lib/libdouble-conversion${MAYBE_PIC}.a"
|
||||
|
||||
LIBEVENT_INCLUDE=" -I $LIBEVENT_BASE/include/"
|
||||
LIBEVENT_LIBS=" $LIBEVENT_BASE/lib/libevent${MAYBE_PIC}.a"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc${MAYBE_PIC}.a"
|
||||
@@ -113,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 $BOOST_INCLUDE $GLOG_INCLUDE $FMT_INCLUDE $DBL_CONV_INCLUDE $LIBEVENT_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"
|
||||
|
||||
@@ -156,7 +134,7 @@ else
|
||||
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 -DHAVE_SSE42 -DROCKSDB_IOURING_PRESENT"
|
||||
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 $BENCHMARK_LIBS"
|
||||
|
||||
@@ -29,38 +29,28 @@ else
|
||||
MAYBE_PIC=_pic
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_SNAPPY; then
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
fi
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZLIB; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
fi
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_BZIP; then
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
fi
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_LZ4; then
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZSTD; then
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
fi
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
@@ -118,19 +108,10 @@ if [ -z "$USE_CLANG" ]; then
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
AR="$GCC_BASE/bin/gcc-ar"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS -nostdinc -nostdlib"
|
||||
CFLAGS+=" -I$GCC_BASE/include"
|
||||
CFLAGS+=" -isystem $GCC_BASE/lib/gcc/x86_64-redhat-linux-gnu/11.2.1/include"
|
||||
CFLAGS+=" -isystem $GCC_BASE/lib/gcc/x86_64-redhat-linux-gnu/11.2.1/install-tools/include"
|
||||
CFLAGS+=" -isystem $GCC_BASE/lib/gcc/x86_64-redhat-linux-gnu/11.2.1/include-fixed/"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -I$GLIBC_INCLUDE"
|
||||
CFLAGS+=" -I$LIBGCC_BASE/include"
|
||||
CFLAGS+=" -I$LIBGCC_BASE/include/c++/11.x/"
|
||||
CFLAGS+=" -I$LIBGCC_BASE/include/c++/11.x/x86_64-facebook-linux/"
|
||||
CFLAGS+=" -I$LIBGCC_BASE/include/c++/11.x/backward"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE -I$GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
@@ -154,7 +135,7 @@ 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 -DHAVE_SSE42 -DROCKSDB_IOURING_PRESENT"
|
||||
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 $BENCHMARK_LIBS"
|
||||
|
||||
Vendored
+2
-50
@@ -34,61 +34,13 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionType::kDouble, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
comp_sec_cache_options_type_info = {
|
||||
{"capacity",
|
||||
{offsetof(struct CompressedSecondaryCacheOptions, capacity),
|
||||
OptionType::kSizeT, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
{"num_shard_bits",
|
||||
{offsetof(struct CompressedSecondaryCacheOptions, num_shard_bits),
|
||||
OptionType::kInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
{"compression_type",
|
||||
{offsetof(struct CompressedSecondaryCacheOptions, compression_type),
|
||||
OptionType::kCompressionType, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
{"compress_format_version",
|
||||
{offsetof(struct CompressedSecondaryCacheOptions,
|
||||
compress_format_version),
|
||||
OptionType::kUInt32T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
};
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status SecondaryCache::CreateFromString(
|
||||
const ConfigOptions& config_options, const std::string& value,
|
||||
std::shared_ptr<SecondaryCache>* result) {
|
||||
if (value.find("compressed_secondary_cache://") == 0) {
|
||||
std::string args = value;
|
||||
args.erase(0, std::strlen("compressed_secondary_cache://"));
|
||||
Status status;
|
||||
std::shared_ptr<SecondaryCache> sec_cache;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
CompressedSecondaryCacheOptions sec_cache_opts;
|
||||
status = OptionTypeInfo::ParseStruct(config_options, "",
|
||||
&comp_sec_cache_options_type_info, "",
|
||||
args, &sec_cache_opts);
|
||||
if (status.ok()) {
|
||||
sec_cache = NewCompressedSecondaryCache(sec_cache_opts);
|
||||
}
|
||||
|
||||
#else
|
||||
(void)config_options;
|
||||
status = Status::NotSupported(
|
||||
"Cannot load compressed secondary cache in LITE mode ", args);
|
||||
#endif //! ROCKSDB_LITE
|
||||
|
||||
if (status.ok()) {
|
||||
result->swap(sec_cache);
|
||||
}
|
||||
return status;
|
||||
} else {
|
||||
return LoadSharedObject<SecondaryCache>(config_options, value, nullptr,
|
||||
result);
|
||||
}
|
||||
return LoadSharedObject<SecondaryCache>(config_options, value, nullptr,
|
||||
result);
|
||||
}
|
||||
|
||||
Status Cache::CreateFromString(const ConfigOptions& config_options,
|
||||
|
||||
Vendored
+3
-20
@@ -12,7 +12,6 @@
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
#include "cache/fast_lru_cache.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "monitoring/histogram.h"
|
||||
#include "port/port.h"
|
||||
@@ -26,7 +25,6 @@
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
#include "table/block_based/cachable_entry.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/distributed_mutex.h"
|
||||
#include "util/gflags_compat.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/mutexlock.h"
|
||||
@@ -78,7 +76,7 @@ DEFINE_string(secondary_cache_uri, "",
|
||||
static class std::shared_ptr<ROCKSDB_NAMESPACE::SecondaryCache> secondary_cache;
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
DEFINE_string(cache_type, "lru_cache", "Type of block cache.");
|
||||
DEFINE_bool(use_clock_cache, false, "");
|
||||
|
||||
// ## BEGIN stress_cache_key sub-tool options ##
|
||||
// See class StressCacheKey below.
|
||||
@@ -281,17 +279,13 @@ class CacheBench {
|
||||
if (max_key > (static_cast<uint64_t>(1) << max_log_)) max_log_++;
|
||||
}
|
||||
|
||||
if (FLAGS_cache_type == "clock_cache") {
|
||||
if (FLAGS_use_clock_cache) {
|
||||
cache_ = NewClockCache(FLAGS_cache_size, FLAGS_num_shard_bits);
|
||||
if (!cache_) {
|
||||
fprintf(stderr, "Clock cache not supported.\n");
|
||||
exit(1);
|
||||
}
|
||||
} else if (FLAGS_cache_type == "fast_lru_cache") {
|
||||
cache_ = NewFastLRUCache(
|
||||
FLAGS_cache_size, FLAGS_value_bytes, FLAGS_num_shard_bits,
|
||||
false /*strict_capacity_limit*/, kDefaultCacheMetadataChargePolicy);
|
||||
} else if (FLAGS_cache_type == "lru_cache") {
|
||||
} else {
|
||||
LRUCacheOptions opts(FLAGS_cache_size, FLAGS_num_shard_bits, false, 0.5);
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (!FLAGS_secondary_cache_uri.empty()) {
|
||||
@@ -309,9 +303,6 @@ class CacheBench {
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
cache_ = NewLRUCache(opts);
|
||||
} else {
|
||||
fprintf(stderr, "Cache type not supported.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,15 +579,7 @@ class CacheBench {
|
||||
}
|
||||
|
||||
void PrintEnv() const {
|
||||
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
|
||||
printf(
|
||||
"WARNING: Optimization is disabled: benchmarks unnecessarily slow\n");
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
printf("WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
|
||||
#endif
|
||||
printf("RocksDB version : %d.%d\n", kMajorVersion, kMinorVersion);
|
||||
printf("DMutex impl name : %s\n", DMutex::kName());
|
||||
printf("Number of threads : %u\n", FLAGS_threads);
|
||||
printf("Ops per thread : %" PRIu64 "\n", FLAGS_ops_per_thread);
|
||||
printf("Cache size : %s\n",
|
||||
|
||||
Vendored
-2
@@ -22,7 +22,6 @@ std::array<std::string, kNumCacheEntryRoles> kCacheEntryRoleToCamelString{{
|
||||
"CompressionDictionaryBuildingBuffer",
|
||||
"FilterConstruction",
|
||||
"BlockBasedTableReader",
|
||||
"FileMetadata",
|
||||
"Misc",
|
||||
}};
|
||||
|
||||
@@ -37,7 +36,6 @@ std::array<std::string, kNumCacheEntryRoles> kCacheEntryRoleToHyphenString{{
|
||||
"compression-dictionary-building-buffer",
|
||||
"filter-construction",
|
||||
"block-based-table-reader",
|
||||
"file-metadata",
|
||||
"misc",
|
||||
}};
|
||||
|
||||
|
||||
Vendored
-1
@@ -180,5 +180,4 @@ template class CacheReservationManagerImpl<
|
||||
template class CacheReservationManagerImpl<CacheEntryRole::kFilterConstruction>;
|
||||
template class CacheReservationManagerImpl<CacheEntryRole::kMisc>;
|
||||
template class CacheReservationManagerImpl<CacheEntryRole::kWriteBuffer>;
|
||||
template class CacheReservationManagerImpl<CacheEntryRole::kFileMetadata>;
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
-28
@@ -36,12 +36,6 @@ class CacheReservationManager {
|
||||
};
|
||||
virtual ~CacheReservationManager() {}
|
||||
virtual Status UpdateCacheReservation(std::size_t new_memory_used) = 0;
|
||||
// TODO(hx235): replace the usage of
|
||||
// `UpdateCacheReservation(memory_used_delta, increase)` with
|
||||
// `UpdateCacheReservation(new_memory_used)` so that we only have one
|
||||
// `UpdateCacheReservation` function
|
||||
virtual Status UpdateCacheReservation(std::size_t memory_used_delta,
|
||||
bool increase) = 0;
|
||||
virtual Status MakeCacheReservation(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle>
|
||||
@@ -134,11 +128,6 @@ class CacheReservationManagerImpl
|
||||
// On keeping dummy entries the same, it always returns Status::OK().
|
||||
Status UpdateCacheReservation(std::size_t new_memory_used) override;
|
||||
|
||||
Status UpdateCacheReservation(std::size_t /* memory_used_delta */,
|
||||
bool /* increase */) override {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
// One of the two ways of reserving cache space and releasing is done through
|
||||
// destruction of CacheReservationHandle.
|
||||
// See UpdateCacheReservation() for the other way.
|
||||
@@ -265,23 +254,6 @@ class ConcurrentCacheReservationManager
|
||||
std::lock_guard<std::mutex> lock(cache_res_mgr_mu_);
|
||||
return cache_res_mgr_->UpdateCacheReservation(new_memory_used);
|
||||
}
|
||||
|
||||
inline Status UpdateCacheReservation(std::size_t memory_used_delta,
|
||||
bool increase) override {
|
||||
std::lock_guard<std::mutex> lock(cache_res_mgr_mu_);
|
||||
std::size_t total_mem_used = cache_res_mgr_->GetTotalMemoryUsed();
|
||||
Status s;
|
||||
if (!increase) {
|
||||
assert(total_mem_used >= memory_used_delta);
|
||||
s = cache_res_mgr_->UpdateCacheReservation(total_mem_used -
|
||||
memory_used_delta);
|
||||
} else {
|
||||
s = cache_res_mgr_->UpdateCacheReservation(total_mem_used +
|
||||
memory_used_delta);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
inline Status MakeCacheReservation(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle> *handle)
|
||||
|
||||
Vendored
+43
-136
@@ -14,53 +14,31 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cache/clock_cache.h"
|
||||
#include "cache/fast_lru_cache.h"
|
||||
#include "cache/lru_cache.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
|
||||
// Conversions between numeric keys/values and the types expected by Cache.
|
||||
std::string EncodeKey16Bytes(int k) {
|
||||
std::string result;
|
||||
PutFixed32(&result, k);
|
||||
result.append(std::string(12, 'a')); // Because we need a 16B output, we
|
||||
// add a 12-byte padding.
|
||||
return result;
|
||||
}
|
||||
|
||||
int DecodeKey16Bytes(const Slice& k) {
|
||||
assert(k.size() == 16);
|
||||
return DecodeFixed32(k.data()); // Decodes only the first 4 bytes of k.
|
||||
}
|
||||
|
||||
std::string EncodeKey32Bits(int k) {
|
||||
static std::string EncodeKey(int k) {
|
||||
std::string result;
|
||||
PutFixed32(&result, k);
|
||||
return result;
|
||||
}
|
||||
|
||||
int DecodeKey32Bits(const Slice& k) {
|
||||
static int DecodeKey(const Slice& k) {
|
||||
assert(k.size() == 4);
|
||||
return DecodeFixed32(k.data());
|
||||
}
|
||||
|
||||
void* EncodeValue(uintptr_t v) { return reinterpret_cast<void*>(v); }
|
||||
|
||||
int DecodeValue(void* v) {
|
||||
static void* EncodeValue(uintptr_t v) { return reinterpret_cast<void*>(v); }
|
||||
static int DecodeValue(void* v) {
|
||||
return static_cast<int>(reinterpret_cast<uintptr_t>(v));
|
||||
}
|
||||
|
||||
const std::string kLRU = "lru";
|
||||
const std::string kClock = "clock";
|
||||
const std::string kFast = "fast";
|
||||
|
||||
void dumbDeleter(const Slice& /*key*/, void* /*value*/) {}
|
||||
|
||||
@@ -69,19 +47,12 @@ void eraseDeleter(const Slice& /*key*/, void* value) {
|
||||
cache->Erase("foo");
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class CacheTest : public testing::TestWithParam<std::string> {
|
||||
public:
|
||||
static CacheTest* current_;
|
||||
static std::string type_;
|
||||
|
||||
static void Deleter(const Slice& key, void* v) {
|
||||
if (type_ == kFast) {
|
||||
current_->deleted_keys_.push_back(DecodeKey16Bytes(key));
|
||||
} else {
|
||||
current_->deleted_keys_.push_back(DecodeKey32Bits(key));
|
||||
}
|
||||
current_->deleted_keys_.push_back(DecodeKey(key));
|
||||
current_->deleted_values_.push_back(DecodeValue(v));
|
||||
}
|
||||
|
||||
@@ -100,7 +71,6 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
: cache_(NewCache(kCacheSize, kNumShardBits, false)),
|
||||
cache2_(NewCache(kCacheSize2, kNumShardBits2, false)) {
|
||||
current_ = this;
|
||||
type_ = GetParam();
|
||||
}
|
||||
|
||||
~CacheTest() override {}
|
||||
@@ -113,11 +83,6 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
if (type == kClock) {
|
||||
return NewClockCache(capacity);
|
||||
}
|
||||
if (type == kFast) {
|
||||
return NewFastLRUCache(
|
||||
capacity, 1 /*estimated_value_size*/, -1 /*num_shard_bits*/,
|
||||
false /*strict_capacity_limit*/, kDefaultCacheMetadataChargePolicy);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -138,35 +103,9 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
return NewClockCache(capacity, num_shard_bits, strict_capacity_limit,
|
||||
charge_policy);
|
||||
}
|
||||
if (type == kFast) {
|
||||
return NewFastLRUCache(capacity, 1 /*estimated_value_size*/,
|
||||
num_shard_bits, strict_capacity_limit,
|
||||
charge_policy);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// These functions encode/decode keys in tests cases that use
|
||||
// int keys.
|
||||
// Currently, FastLRUCache requires keys to be 16B long, whereas
|
||||
// LRUCache and ClockCache don't, so the encoding depends on
|
||||
// the cache type.
|
||||
std::string EncodeKey(int k) {
|
||||
if (GetParam() == kFast) {
|
||||
return EncodeKey16Bytes(k);
|
||||
} else {
|
||||
return EncodeKey32Bits(k);
|
||||
}
|
||||
}
|
||||
|
||||
int DecodeKey(const Slice& k) {
|
||||
if (GetParam() == kFast) {
|
||||
return DecodeKey16Bytes(k);
|
||||
} else {
|
||||
return DecodeKey32Bits(k);
|
||||
}
|
||||
}
|
||||
|
||||
int Lookup(std::shared_ptr<Cache> cache, int key) {
|
||||
Cache::Handle* handle = cache->Lookup(EncodeKey(key));
|
||||
const int r = (handle == nullptr) ? -1 : DecodeValue(cache->Value(handle));
|
||||
@@ -210,18 +149,11 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
Erase(cache2_, key);
|
||||
}
|
||||
};
|
||||
|
||||
CacheTest* CacheTest::current_;
|
||||
std::string CacheTest::type_;
|
||||
|
||||
class LRUCacheTest : public CacheTest {};
|
||||
|
||||
TEST_P(CacheTest, UsageTest) {
|
||||
if (GetParam() == kFast) {
|
||||
ROCKSDB_GTEST_BYPASS("FastLRUCache requires 16 byte keys.");
|
||||
return;
|
||||
}
|
||||
|
||||
// cache is std::shared_ptr and will be automatically cleaned up.
|
||||
const uint64_t kCapacity = 100000;
|
||||
auto cache = NewCache(kCapacity, 8, false, kDontChargeCacheMetadata);
|
||||
@@ -251,7 +183,7 @@ TEST_P(CacheTest, UsageTest) {
|
||||
|
||||
// make sure the cache will be overloaded
|
||||
for (uint64_t i = 1; i < kCapacity; ++i) {
|
||||
auto key = std::to_string(i);
|
||||
auto key = ToString(i);
|
||||
ASSERT_OK(cache->Insert(key, reinterpret_cast<void*>(value), key.size() + 5,
|
||||
dumbDeleter));
|
||||
ASSERT_OK(precise_cache->Insert(key, reinterpret_cast<void*>(value),
|
||||
@@ -266,11 +198,6 @@ TEST_P(CacheTest, UsageTest) {
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, PinnedUsageTest) {
|
||||
if (GetParam() == kFast) {
|
||||
ROCKSDB_GTEST_BYPASS("FastLRUCache requires 16 byte keys.");
|
||||
return;
|
||||
}
|
||||
|
||||
// cache is std::shared_ptr and will be automatically cleaned up.
|
||||
const uint64_t kCapacity = 200000;
|
||||
auto cache = NewCache(kCapacity, 8, false, kDontChargeCacheMetadata);
|
||||
@@ -328,7 +255,7 @@ TEST_P(CacheTest, PinnedUsageTest) {
|
||||
|
||||
// check that overloading the cache does not change the pinned usage
|
||||
for (uint64_t i = 1; i < 2 * kCapacity; ++i) {
|
||||
auto key = std::to_string(i);
|
||||
auto key = ToString(i);
|
||||
ASSERT_OK(cache->Insert(key, reinterpret_cast<void*>(value), key.size() + 5,
|
||||
dumbDeleter));
|
||||
ASSERT_OK(precise_cache->Insert(key, reinterpret_cast<void*>(value),
|
||||
@@ -523,22 +450,12 @@ TEST_P(CacheTest, EvictionPolicyRef) {
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, EvictEmptyCache) {
|
||||
if (GetParam() == kFast) {
|
||||
ROCKSDB_GTEST_BYPASS("FastLRUCache requires 16 byte keys.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert item large than capacity to trigger eviction on empty cache.
|
||||
auto cache = NewCache(1, 0, false);
|
||||
ASSERT_OK(cache->Insert("foo", nullptr, 10, dumbDeleter));
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, EraseFromDeleter) {
|
||||
if (GetParam() == kFast) {
|
||||
ROCKSDB_GTEST_BYPASS("FastLRUCache requires 16 byte keys.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Have deleter which will erase item from cache, which will re-enter
|
||||
// the cache at that point.
|
||||
std::shared_ptr<Cache> cache = NewCache(10, 0, false);
|
||||
@@ -607,9 +524,9 @@ TEST_P(CacheTest, NewId) {
|
||||
|
||||
class Value {
|
||||
public:
|
||||
explicit Value(int v) : v_(v) {}
|
||||
explicit Value(size_t v) : v_(v) { }
|
||||
|
||||
int v_;
|
||||
size_t v_;
|
||||
};
|
||||
|
||||
namespace {
|
||||
@@ -657,8 +574,8 @@ TEST_P(CacheTest, SetCapacity) {
|
||||
std::shared_ptr<Cache> cache = NewCache(5, 0, false);
|
||||
std::vector<Cache::Handle*> handles(10);
|
||||
// Insert 5 entries, but not releasing.
|
||||
for (int i = 0; i < 5; i++) {
|
||||
std::string key = EncodeKey(i + 1);
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
std::string key = ToString(i+1);
|
||||
Status s = cache->Insert(key, new Value(i + 1), 1, &deleter, &handles[i]);
|
||||
ASSERT_TRUE(s.ok());
|
||||
}
|
||||
@@ -672,14 +589,14 @@ TEST_P(CacheTest, SetCapacity) {
|
||||
// insert 5 more elements to cache, then release 5,
|
||||
// then decrease capacity to 7, final capacity should be 7
|
||||
// and usage should be 7
|
||||
for (int i = 5; i < 10; i++) {
|
||||
std::string key = EncodeKey(i + 1);
|
||||
for (size_t i = 5; i < 10; i++) {
|
||||
std::string key = ToString(i+1);
|
||||
Status s = cache->Insert(key, new Value(i + 1), 1, &deleter, &handles[i]);
|
||||
ASSERT_TRUE(s.ok());
|
||||
}
|
||||
ASSERT_EQ(10U, cache->GetCapacity());
|
||||
ASSERT_EQ(10U, cache->GetUsage());
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
cache->Release(handles[i]);
|
||||
}
|
||||
ASSERT_EQ(10U, cache->GetCapacity());
|
||||
@@ -689,7 +606,7 @@ TEST_P(CacheTest, SetCapacity) {
|
||||
ASSERT_EQ(7, cache->GetUsage());
|
||||
|
||||
// release remaining 5 to keep valgrind happy
|
||||
for (int i = 5; i < 10; i++) {
|
||||
for (size_t i = 5; i < 10; i++) {
|
||||
cache->Release(handles[i]);
|
||||
}
|
||||
|
||||
@@ -703,8 +620,8 @@ TEST_P(LRUCacheTest, SetStrictCapacityLimit) {
|
||||
std::shared_ptr<Cache> cache = NewCache(5, 0, false);
|
||||
std::vector<Cache::Handle*> handles(10);
|
||||
Status s;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
std::string key = EncodeKey(i + 1);
|
||||
for (size_t i = 0; i < 10; i++) {
|
||||
std::string key = ToString(i + 1);
|
||||
s = cache->Insert(key, new Value(i + 1), 1, &deleter, &handles[i]);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_NE(nullptr, handles[i]);
|
||||
@@ -712,7 +629,7 @@ TEST_P(LRUCacheTest, SetStrictCapacityLimit) {
|
||||
ASSERT_EQ(10, cache->GetUsage());
|
||||
|
||||
// test2: set the flag to true. Insert and check if it fails.
|
||||
std::string extra_key = EncodeKey(100);
|
||||
std::string extra_key = "extra";
|
||||
Value* extra_value = new Value(0);
|
||||
cache->SetStrictCapacityLimit(true);
|
||||
Cache::Handle* handle;
|
||||
@@ -721,14 +638,14 @@ TEST_P(LRUCacheTest, SetStrictCapacityLimit) {
|
||||
ASSERT_EQ(nullptr, handle);
|
||||
ASSERT_EQ(10, cache->GetUsage());
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (size_t i = 0; i < 10; i++) {
|
||||
cache->Release(handles[i]);
|
||||
}
|
||||
|
||||
// test3: init with flag being true.
|
||||
std::shared_ptr<Cache> cache2 = NewCache(5, 0, true);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
std::string key = EncodeKey(i + 1);
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
std::string key = ToString(i + 1);
|
||||
s = cache2->Insert(key, new Value(i + 1), 1, &deleter, &handles[i]);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_NE(nullptr, handles[i]);
|
||||
@@ -743,7 +660,7 @@ TEST_P(LRUCacheTest, SetStrictCapacityLimit) {
|
||||
ASSERT_EQ(5, cache2->GetUsage());
|
||||
ASSERT_EQ(nullptr, cache2->Lookup(extra_key));
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
cache2->Release(handles[i]);
|
||||
}
|
||||
}
|
||||
@@ -757,15 +674,15 @@ TEST_P(CacheTest, OverCapacity) {
|
||||
std::vector<Cache::Handle*> handles(n+1);
|
||||
|
||||
// Insert n+1 entries, but not releasing.
|
||||
for (int i = 0; i < static_cast<int>(n + 1); i++) {
|
||||
std::string key = EncodeKey(i + 1);
|
||||
for (size_t i = 0; i < n + 1; i++) {
|
||||
std::string key = ToString(i+1);
|
||||
Status s = cache->Insert(key, new Value(i + 1), 1, &deleter, &handles[i]);
|
||||
ASSERT_TRUE(s.ok());
|
||||
}
|
||||
|
||||
// Guess what's in the cache now?
|
||||
for (int i = 0; i < static_cast<int>(n + 1); i++) {
|
||||
std::string key = EncodeKey(i + 1);
|
||||
for (size_t i = 0; i < n + 1; i++) {
|
||||
std::string key = ToString(i+1);
|
||||
auto h = cache->Lookup(key);
|
||||
ASSERT_TRUE(h != nullptr);
|
||||
if (h) cache->Release(h);
|
||||
@@ -773,7 +690,7 @@ TEST_P(CacheTest, OverCapacity) {
|
||||
|
||||
// the cache is over capacity since nothing could be evicted
|
||||
ASSERT_EQ(n + 1U, cache->GetUsage());
|
||||
for (int i = 0; i < static_cast<int>(n + 1); i++) {
|
||||
for (size_t i = 0; i < n + 1; i++) {
|
||||
cache->Release(handles[i]);
|
||||
}
|
||||
// Make sure eviction is triggered.
|
||||
@@ -785,14 +702,14 @@ TEST_P(CacheTest, OverCapacity) {
|
||||
// element 0 is evicted and the rest is there
|
||||
// This is consistent with the LRU policy since the element 0
|
||||
// was released first
|
||||
for (int i = 0; i < static_cast<int>(n + 1); i++) {
|
||||
std::string key = EncodeKey(i + 1);
|
||||
for (size_t i = 0; i < n + 1; i++) {
|
||||
std::string key = ToString(i+1);
|
||||
auto h = cache->Lookup(key);
|
||||
if (h) {
|
||||
ASSERT_NE(static_cast<size_t>(i), 0U);
|
||||
ASSERT_NE(i, 0U);
|
||||
cache->Release(h);
|
||||
} else {
|
||||
ASSERT_EQ(static_cast<size_t>(i), 0U);
|
||||
ASSERT_EQ(i, 0U);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -818,7 +735,7 @@ TEST_P(CacheTest, ApplyToAllCacheEntriesTest) {
|
||||
std::sort(inserted.begin(), inserted.end());
|
||||
std::sort(legacy_callback_state.begin(), legacy_callback_state.end());
|
||||
ASSERT_EQ(inserted.size(), legacy_callback_state.size());
|
||||
for (int i = 0; i < static_cast<int>(inserted.size()); ++i) {
|
||||
for (size_t i = 0; i < inserted.size(); ++i) {
|
||||
EXPECT_EQ(inserted[i], legacy_callback_state[i]);
|
||||
}
|
||||
}
|
||||
@@ -827,9 +744,9 @@ TEST_P(CacheTest, ApplyToAllEntriesTest) {
|
||||
std::vector<std::string> callback_state;
|
||||
const auto callback = [&](const Slice& key, void* value, size_t charge,
|
||||
Cache::DeleterFn deleter) {
|
||||
callback_state.push_back(std::to_string(DecodeKey(key)) + "," +
|
||||
std::to_string(DecodeValue(value)) + "," +
|
||||
std::to_string(charge));
|
||||
callback_state.push_back(ToString(DecodeKey(key)) + "," +
|
||||
ToString(DecodeValue(value)) + "," +
|
||||
ToString(charge));
|
||||
assert(deleter == &CacheTest::Deleter);
|
||||
};
|
||||
|
||||
@@ -838,15 +755,15 @@ TEST_P(CacheTest, ApplyToAllEntriesTest) {
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Insert(i, i * 2, i + 1);
|
||||
inserted.push_back(std::to_string(i) + "," + std::to_string(i * 2) + "," +
|
||||
std::to_string(i + 1));
|
||||
inserted.push_back(ToString(i) + "," + ToString(i * 2) + "," +
|
||||
ToString(i + 1));
|
||||
}
|
||||
cache_->ApplyToAllEntries(callback, /*opts*/ {});
|
||||
|
||||
std::sort(inserted.begin(), inserted.end());
|
||||
std::sort(callback_state.begin(), callback_state.end());
|
||||
ASSERT_EQ(inserted.size(), callback_state.size());
|
||||
for (int i = 0; i < static_cast<int>(inserted.size()); ++i) {
|
||||
for (size_t i = 0; i < inserted.size(); ++i) {
|
||||
EXPECT_EQ(inserted[i], callback_state[i]);
|
||||
}
|
||||
}
|
||||
@@ -861,10 +778,8 @@ TEST_P(CacheTest, ApplyToAllEntriesDuringResize) {
|
||||
constexpr int kSpecialCharge = 2;
|
||||
constexpr int kNotSpecialCharge = 1;
|
||||
constexpr int kSpecialCount = 100;
|
||||
size_t expected_usage = 0;
|
||||
for (int i = 0; i < kSpecialCount; ++i) {
|
||||
Insert(i, i * 2, kSpecialCharge);
|
||||
expected_usage += kSpecialCharge;
|
||||
}
|
||||
|
||||
// For callback
|
||||
@@ -885,17 +800,12 @@ TEST_P(CacheTest, ApplyToAllEntriesDuringResize) {
|
||||
});
|
||||
|
||||
// In parallel, add more entries, enough to cause resize but not enough
|
||||
// to cause ejections. (Note: if any cache shard is over capacity, there
|
||||
// will be ejections)
|
||||
for (int i = kSpecialCount * 1; i < kSpecialCount * 5; ++i) {
|
||||
// to cause ejections
|
||||
for (int i = kSpecialCount * 1; i < kSpecialCount * 6; ++i) {
|
||||
Insert(i, i * 2, kNotSpecialCharge);
|
||||
expected_usage += kNotSpecialCharge;
|
||||
}
|
||||
|
||||
apply_thread.join();
|
||||
// verify no evictions
|
||||
ASSERT_EQ(cache_->GetUsage(), expected_usage);
|
||||
// verify everything seen in ApplyToAllEntries
|
||||
ASSERT_EQ(special_count, kSpecialCount);
|
||||
}
|
||||
|
||||
@@ -928,18 +838,15 @@ TEST_P(CacheTest, GetChargeAndDeleter) {
|
||||
std::shared_ptr<Cache> (*new_clock_cache_func)(
|
||||
size_t, int, bool, CacheMetadataChargePolicy) = NewClockCache;
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, CacheTest,
|
||||
testing::Values(kLRU, kClock, kFast));
|
||||
testing::Values(kLRU, kClock));
|
||||
#else
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, CacheTest,
|
||||
testing::Values(kLRU, kFast));
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, CacheTest, testing::Values(kLRU));
|
||||
#endif // SUPPORT_CLOCK_CACHE
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, LRUCacheTest,
|
||||
testing::Values(kLRU, kFast));
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, LRUCacheTest, testing::Values(kLRU));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Vendored
+8
-8
@@ -39,7 +39,7 @@ std::shared_ptr<Cache> NewClockCache(
|
||||
#include "port/port.h"
|
||||
#include "tbb/concurrent_hash_map.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/distributed_mutex.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -368,7 +368,7 @@ class ClockCacheShard final : public CacheShard {
|
||||
|
||||
// Guards list_, head_, and recycle_. In addition, updating table_ also has
|
||||
// to hold the mutex, to avoid the cache being in inconsistent state.
|
||||
mutable DMutex mutex_;
|
||||
mutable port::Mutex mutex_;
|
||||
|
||||
// The circular list of cache handles. Initially the list is empty. Once a
|
||||
// handle is needed by insertion, and no more handles are available in
|
||||
@@ -431,7 +431,7 @@ void ClockCacheShard::ApplyToSomeEntries(
|
||||
DeleterFn deleter)>& callback,
|
||||
uint32_t average_entries_per_lock, uint32_t* state) {
|
||||
assert(average_entries_per_lock > 0);
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
// Figure out the range to iterate, update `state`
|
||||
size_t list_size = list_.size();
|
||||
@@ -532,7 +532,7 @@ bool ClockCacheShard::Unref(CacheHandle* handle, bool set_usage,
|
||||
pinned_usage_.fetch_sub(total_charge, std::memory_order_relaxed);
|
||||
// Cleanup if it is the last reference.
|
||||
if (!InCache(flags)) {
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
RecycleHandle(handle, context);
|
||||
}
|
||||
}
|
||||
@@ -598,7 +598,7 @@ bool ClockCacheShard::EvictFromCache(size_t charge, CleanupContext* context) {
|
||||
void ClockCacheShard::SetCapacity(size_t capacity) {
|
||||
CleanupContext context;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
capacity_.store(capacity, std::memory_order_relaxed);
|
||||
EvictFromCache(0, &context);
|
||||
}
|
||||
@@ -618,7 +618,7 @@ CacheHandle* ClockCacheShard::Insert(
|
||||
uint32_t meta_charge =
|
||||
CacheHandle::CalcMetadataCharge(key, metadata_charge_policy_);
|
||||
size_t total_charge = charge + meta_charge;
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
bool success = EvictFromCache(total_charge, context);
|
||||
bool strict = strict_capacity_limit_.load(std::memory_order_relaxed);
|
||||
if (!success && (strict || !hold_reference)) {
|
||||
@@ -744,7 +744,7 @@ void ClockCacheShard::Erase(const Slice& key, uint32_t hash) {
|
||||
|
||||
bool ClockCacheShard::EraseAndConfirm(const Slice& key, uint32_t hash,
|
||||
CleanupContext* context) {
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
HashTable::accessor accessor;
|
||||
bool erased = false;
|
||||
if (table_.find(accessor, ClockCacheKey(key, hash))) {
|
||||
@@ -758,7 +758,7 @@ bool ClockCacheShard::EraseAndConfirm(const Slice& key, uint32_t hash,
|
||||
void ClockCacheShard::EraseUnRefEntries() {
|
||||
CleanupContext context;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
table_.clear();
|
||||
for (auto& handle : list_) {
|
||||
UnsetInCache(&handle, &context);
|
||||
|
||||
+31
-71
@@ -10,8 +10,6 @@
|
||||
|
||||
#include "memory/jemalloc_nodump_allocator.h"
|
||||
#include "memory/memory_allocator.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/compression.h"
|
||||
@@ -81,7 +79,37 @@ class CompressedSecondaryCacheTest : public testing::Test {
|
||||
|
||||
void SetFailCreate(bool fail) { fail_create_ = fail; }
|
||||
|
||||
void BasicTestHelper(std::shared_ptr<SecondaryCache> sec_cache) {
|
||||
void BasicTest(bool sec_cache_is_compressed, bool use_jemalloc) {
|
||||
CompressedSecondaryCacheOptions opts;
|
||||
opts.capacity = 2048;
|
||||
opts.num_shard_bits = 0;
|
||||
opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
if (use_jemalloc) {
|
||||
JemallocAllocatorOptions jopts;
|
||||
std::shared_ptr<MemoryAllocator> allocator;
|
||||
std::string msg;
|
||||
if (JemallocNodumpAllocator::IsSupported(&msg)) {
|
||||
Status s = NewJemallocNodumpAllocator(jopts, &allocator);
|
||||
if (s.ok()) {
|
||||
opts.memory_allocator = allocator;
|
||||
}
|
||||
} else {
|
||||
ROCKSDB_GTEST_BYPASS("JEMALLOC not supported");
|
||||
}
|
||||
}
|
||||
std::shared_ptr<SecondaryCache> sec_cache =
|
||||
NewCompressedSecondaryCache(opts);
|
||||
|
||||
bool is_in_sec_cache{true};
|
||||
// Lookup an non-existent key.
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle0 =
|
||||
@@ -132,38 +160,6 @@ class CompressedSecondaryCacheTest : public testing::Test {
|
||||
sec_cache.reset();
|
||||
}
|
||||
|
||||
void BasicTest(bool sec_cache_is_compressed, bool use_jemalloc) {
|
||||
CompressedSecondaryCacheOptions opts;
|
||||
opts.capacity = 2048;
|
||||
opts.num_shard_bits = 0;
|
||||
opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
if (use_jemalloc) {
|
||||
JemallocAllocatorOptions jopts;
|
||||
std::shared_ptr<MemoryAllocator> allocator;
|
||||
std::string msg;
|
||||
if (JemallocNodumpAllocator::IsSupported(&msg)) {
|
||||
Status s = NewJemallocNodumpAllocator(jopts, &allocator);
|
||||
if (s.ok()) {
|
||||
opts.memory_allocator = allocator;
|
||||
}
|
||||
} else {
|
||||
ROCKSDB_GTEST_BYPASS("JEMALLOC not supported");
|
||||
}
|
||||
}
|
||||
std::shared_ptr<SecondaryCache> sec_cache =
|
||||
NewCompressedSecondaryCache(opts);
|
||||
}
|
||||
|
||||
void FailsTest(bool sec_cache_is_compressed) {
|
||||
CompressedSecondaryCacheOptions secondary_cache_opts;
|
||||
if (sec_cache_is_compressed) {
|
||||
@@ -551,42 +547,6 @@ TEST_F(CompressedSecondaryCacheTest,
|
||||
BasicTest(true, true);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
TEST_F(CompressedSecondaryCacheTest, BasicTestFromStringWithNoCompression) {
|
||||
std::string sec_cache_uri =
|
||||
"compressed_secondary_cache://"
|
||||
"capacity=2048;num_shard_bits=0;compression_type=kNoCompression";
|
||||
std::shared_ptr<SecondaryCache> sec_cache;
|
||||
Status s = SecondaryCache::CreateFromString(ConfigOptions(), sec_cache_uri,
|
||||
&sec_cache);
|
||||
EXPECT_OK(s);
|
||||
BasicTestHelper(sec_cache);
|
||||
}
|
||||
|
||||
TEST_F(CompressedSecondaryCacheTest, BasicTestFromStringWithCompression) {
|
||||
std::string sec_cache_uri;
|
||||
if (LZ4_Supported()) {
|
||||
sec_cache_uri =
|
||||
"compressed_secondary_cache://"
|
||||
"capacity=2048;num_shard_bits=0;compression_type=kLZ4Compression;"
|
||||
"compress_format_version=2";
|
||||
} else {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
sec_cache_uri =
|
||||
"compressed_secondary_cache://"
|
||||
"capacity=2048;num_shard_bits=0;compression_type=kNoCompression";
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache> sec_cache;
|
||||
Status s = SecondaryCache::CreateFromString(ConfigOptions(), sec_cache_uri,
|
||||
&sec_cache);
|
||||
EXPECT_OK(s);
|
||||
BasicTestHelper(sec_cache);
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(CompressedSecondaryCacheTest, FailsTestWithNoCompression) {
|
||||
FailsTest(false);
|
||||
}
|
||||
|
||||
Vendored
-500
@@ -1,500 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "cache/fast_lru_cache.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "port/lang.h"
|
||||
#include "util/distributed_mutex.h"
|
||||
|
||||
#define KEY_LENGTH \
|
||||
16 // TODO(guido) Make use of this symbol in other parts of the source code
|
||||
// (e.g., cache_key.h, cache_test.cc, etc.)
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace fast_lru_cache {
|
||||
|
||||
LRUHandleTable::LRUHandleTable(int hash_bits)
|
||||
: length_bits_(hash_bits),
|
||||
list_(new LRUHandle* [size_t{1} << length_bits_] {}) {}
|
||||
|
||||
LRUHandleTable::~LRUHandleTable() {
|
||||
ApplyToEntriesRange(
|
||||
[](LRUHandle* h) {
|
||||
if (!h->HasRefs()) {
|
||||
h->Free();
|
||||
}
|
||||
},
|
||||
0, uint32_t{1} << length_bits_);
|
||||
}
|
||||
|
||||
LRUHandle* LRUHandleTable::Lookup(const Slice& key, uint32_t hash) {
|
||||
return *FindPointer(key, hash);
|
||||
}
|
||||
|
||||
inline LRUHandle** LRUHandleTable::Head(uint32_t hash) {
|
||||
return &list_[hash >> (32 - length_bits_)];
|
||||
}
|
||||
|
||||
LRUHandle* LRUHandleTable::Insert(LRUHandle* h) {
|
||||
LRUHandle** ptr = FindPointer(h->key(), h->hash);
|
||||
LRUHandle* old = *ptr;
|
||||
h->next_hash = (old == nullptr ? nullptr : old->next_hash);
|
||||
*ptr = h;
|
||||
return old;
|
||||
}
|
||||
|
||||
LRUHandle* LRUHandleTable::Remove(const Slice& key, uint32_t hash) {
|
||||
LRUHandle** ptr = FindPointer(key, hash);
|
||||
LRUHandle* result = *ptr;
|
||||
if (result != nullptr) {
|
||||
*ptr = result->next_hash;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LRUHandle** LRUHandleTable::FindPointer(const Slice& key, uint32_t hash) {
|
||||
LRUHandle** ptr = &list_[hash >> (32 - length_bits_)];
|
||||
while (*ptr != nullptr && ((*ptr)->hash != hash || key != (*ptr)->key())) {
|
||||
ptr = &(*ptr)->next_hash;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
LRUCacheShard::LRUCacheShard(size_t capacity, size_t estimated_value_size,
|
||||
bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy)
|
||||
: capacity_(0),
|
||||
strict_capacity_limit_(strict_capacity_limit),
|
||||
table_(
|
||||
GetHashBits(capacity, estimated_value_size, metadata_charge_policy)),
|
||||
usage_(0),
|
||||
lru_usage_(0) {
|
||||
set_metadata_charge_policy(metadata_charge_policy);
|
||||
// Make empty circular linked list.
|
||||
lru_.next = &lru_;
|
||||
lru_.prev = &lru_;
|
||||
lru_low_pri_ = &lru_;
|
||||
SetCapacity(capacity);
|
||||
}
|
||||
|
||||
void LRUCacheShard::EraseUnRefEntries() {
|
||||
autovector<LRUHandle*> last_reference_list;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
while (lru_.next != &lru_) {
|
||||
LRUHandle* old = lru_.next;
|
||||
// LRU list contains only elements which can be evicted.
|
||||
assert(old->InCache() && !old->HasRefs());
|
||||
LRU_Remove(old);
|
||||
table_.Remove(old->key(), old->hash);
|
||||
old->SetInCache(false);
|
||||
assert(usage_ >= old->total_charge);
|
||||
usage_ -= old->total_charge;
|
||||
last_reference_list.push_back(old);
|
||||
}
|
||||
}
|
||||
|
||||
// Free the entries here outside of mutex for performance reasons.
|
||||
for (auto entry : last_reference_list) {
|
||||
entry->Free();
|
||||
}
|
||||
}
|
||||
|
||||
void LRUCacheShard::ApplyToSomeEntries(
|
||||
const std::function<void(const Slice& key, void* value, size_t charge,
|
||||
DeleterFn deleter)>& callback,
|
||||
uint32_t average_entries_per_lock, uint32_t* state) {
|
||||
// The state is essentially going to be the starting hash, which works
|
||||
// nicely even if we resize between calls because we use upper-most
|
||||
// hash bits for table indexes.
|
||||
DMutexLock l(mutex_);
|
||||
uint32_t length_bits = table_.GetLengthBits();
|
||||
uint32_t length = uint32_t{1} << length_bits;
|
||||
|
||||
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).
|
||||
assert(average_entries_per_lock < length || *state == 0);
|
||||
|
||||
uint32_t index_begin = *state >> (32 - length_bits);
|
||||
uint32_t index_end = index_begin + average_entries_per_lock;
|
||||
if (index_end >= length) {
|
||||
// Going to end
|
||||
index_end = length;
|
||||
*state = UINT32_MAX;
|
||||
} else {
|
||||
*state = index_end << (32 - length_bits);
|
||||
}
|
||||
|
||||
table_.ApplyToEntriesRange(
|
||||
[callback,
|
||||
metadata_charge_policy = metadata_charge_policy_](LRUHandle* h) {
|
||||
callback(h->key(), h->value, h->GetCharge(metadata_charge_policy),
|
||||
h->deleter);
|
||||
},
|
||||
index_begin, index_end);
|
||||
}
|
||||
|
||||
void LRUCacheShard::LRU_Remove(LRUHandle* e) {
|
||||
assert(e->next != nullptr);
|
||||
assert(e->prev != nullptr);
|
||||
e->next->prev = e->prev;
|
||||
e->prev->next = e->next;
|
||||
e->prev = e->next = nullptr;
|
||||
assert(lru_usage_ >= e->total_charge);
|
||||
lru_usage_ -= e->total_charge;
|
||||
}
|
||||
|
||||
void LRUCacheShard::LRU_Insert(LRUHandle* e) {
|
||||
assert(e->next == nullptr);
|
||||
assert(e->prev == nullptr);
|
||||
// Inset "e" to head of LRU list.
|
||||
e->next = &lru_;
|
||||
e->prev = lru_.prev;
|
||||
e->prev->next = e;
|
||||
e->next->prev = e;
|
||||
lru_usage_ += e->total_charge;
|
||||
}
|
||||
|
||||
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.
|
||||
assert(old->InCache() && !old->HasRefs());
|
||||
LRU_Remove(old);
|
||||
table_.Remove(old->key(), old->hash);
|
||||
old->SetInCache(false);
|
||||
assert(usage_ >= old->total_charge);
|
||||
usage_ -= old->total_charge;
|
||||
deleted->push_back(old);
|
||||
}
|
||||
}
|
||||
|
||||
int LRUCacheShard::GetHashBits(
|
||||
size_t capacity, size_t estimated_value_size,
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(
|
||||
new char[sizeof(LRUHandle) - 1 + KEY_LENGTH]);
|
||||
e->key_length = KEY_LENGTH;
|
||||
e->deleter = nullptr;
|
||||
e->refs = 0;
|
||||
e->flags = 0;
|
||||
e->refs = 0;
|
||||
|
||||
e->CalcTotalCharge(estimated_value_size, metadata_charge_policy);
|
||||
size_t num_entries = capacity / e->total_charge;
|
||||
e->Free();
|
||||
int num_hash_bits = 0;
|
||||
while (num_entries >>= 1) {
|
||||
++num_hash_bits;
|
||||
}
|
||||
return num_hash_bits;
|
||||
}
|
||||
|
||||
void LRUCacheShard::SetCapacity(size_t capacity) {
|
||||
autovector<LRUHandle*> last_reference_list;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
capacity_ = capacity;
|
||||
EvictFromLRU(0, &last_reference_list);
|
||||
}
|
||||
|
||||
// Free the entries here outside of mutex for performance reasons.
|
||||
for (auto entry : last_reference_list) {
|
||||
entry->Free();
|
||||
}
|
||||
}
|
||||
|
||||
void LRUCacheShard::SetStrictCapacityLimit(bool strict_capacity_limit) {
|
||||
DMutexLock l(mutex_);
|
||||
strict_capacity_limit_ = strict_capacity_limit;
|
||||
}
|
||||
|
||||
Status LRUCacheShard::InsertItem(LRUHandle* e, Cache::Handle** handle,
|
||||
bool free_handle_on_fail) {
|
||||
Status s = Status::OK();
|
||||
autovector<LRUHandle*> last_reference_list;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
|
||||
// Free the space following strict LRU policy until enough space
|
||||
// is freed or the lru list is empty.
|
||||
EvictFromLRU(e->total_charge, &last_reference_list);
|
||||
|
||||
if ((usage_ + e->total_charge) > capacity_ &&
|
||||
(strict_capacity_limit_ || handle == nullptr)) {
|
||||
e->SetInCache(false);
|
||||
if (handle == nullptr) {
|
||||
// Don't insert the entry but still return ok, as if the entry inserted
|
||||
// into cache and get evicted immediately.
|
||||
last_reference_list.push_back(e);
|
||||
} else {
|
||||
if (free_handle_on_fail) {
|
||||
delete[] reinterpret_cast<char*>(e);
|
||||
*handle = nullptr;
|
||||
}
|
||||
s = Status::Incomplete("Insert failed due to LRU cache being full.");
|
||||
}
|
||||
} else {
|
||||
// Insert into the cache. Note that the cache might get larger than its
|
||||
// capacity if not enough space was freed up.
|
||||
LRUHandle* old = table_.Insert(e);
|
||||
usage_ += e->total_charge;
|
||||
if (old != nullptr) {
|
||||
s = Status::OkOverwritten();
|
||||
assert(old->InCache());
|
||||
old->SetInCache(false);
|
||||
if (!old->HasRefs()) {
|
||||
// old is on LRU because it's in cache and its reference count is 0.
|
||||
LRU_Remove(old);
|
||||
assert(usage_ >= old->total_charge);
|
||||
usage_ -= old->total_charge;
|
||||
last_reference_list.push_back(old);
|
||||
}
|
||||
}
|
||||
if (handle == nullptr) {
|
||||
LRU_Insert(e);
|
||||
} else {
|
||||
// If caller already holds a ref, no need to take one here.
|
||||
if (!e->HasRefs()) {
|
||||
e->Ref();
|
||||
}
|
||||
*handle = reinterpret_cast<Cache::Handle*>(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Free the entries here outside of mutex for performance reasons.
|
||||
for (auto entry : last_reference_list) {
|
||||
entry->Free();
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Cache::Handle* LRUCacheShard::Lookup(const Slice& key, uint32_t hash) {
|
||||
LRUHandle* e = nullptr;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
e = table_.Lookup(key, hash);
|
||||
if (e != nullptr) {
|
||||
assert(e->InCache());
|
||||
if (!e->HasRefs()) {
|
||||
// The entry is in LRU since it's in hash and has no external references
|
||||
LRU_Remove(e);
|
||||
}
|
||||
e->Ref();
|
||||
}
|
||||
}
|
||||
return reinterpret_cast<Cache::Handle*>(e);
|
||||
}
|
||||
|
||||
bool LRUCacheShard::Ref(Cache::Handle* h) {
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(h);
|
||||
DMutexLock l(mutex_);
|
||||
// To create another reference - entry must be already externally referenced.
|
||||
assert(e->HasRefs());
|
||||
e->Ref();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LRUCacheShard::Release(Cache::Handle* handle, bool erase_if_last_ref) {
|
||||
if (handle == nullptr) {
|
||||
return false;
|
||||
}
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(handle);
|
||||
bool last_reference = false;
|
||||
{
|
||||
DMutexLock 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_ || 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.
|
||||
LRU_Insert(e);
|
||||
last_reference = false;
|
||||
}
|
||||
}
|
||||
// If it was the last reference, then decrement the cache usage.
|
||||
if (last_reference) {
|
||||
assert(usage_ >= e->total_charge);
|
||||
usage_ -= e->total_charge;
|
||||
}
|
||||
}
|
||||
|
||||
// Free the entry here outside of mutex for performance reasons.
|
||||
if (last_reference) {
|
||||
e->Free();
|
||||
}
|
||||
return last_reference;
|
||||
}
|
||||
|
||||
Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
size_t charge, Cache::DeleterFn deleter,
|
||||
Cache::Handle** handle,
|
||||
Cache::Priority /*priority*/) {
|
||||
if (key.size() != KEY_LENGTH) {
|
||||
return Status::NotSupported("FastLRUCache only supports key size " +
|
||||
std::to_string(KEY_LENGTH) + "B");
|
||||
}
|
||||
|
||||
// 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()]);
|
||||
|
||||
e->value = value;
|
||||
e->flags = 0;
|
||||
e->deleter = deleter;
|
||||
e->key_length = key.size();
|
||||
e->hash = hash;
|
||||
e->refs = 0;
|
||||
e->next = e->prev = nullptr;
|
||||
e->SetInCache(true);
|
||||
e->CalcTotalCharge(charge, metadata_charge_policy_);
|
||||
memcpy(e->key_data, key.data(), key.size());
|
||||
|
||||
return InsertItem(e, handle, /* free_handle_on_fail */ true);
|
||||
}
|
||||
|
||||
void LRUCacheShard::Erase(const Slice& key, uint32_t hash) {
|
||||
LRUHandle* e;
|
||||
bool last_reference = false;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
e = table_.Remove(key, hash);
|
||||
if (e != nullptr) {
|
||||
assert(e->InCache());
|
||||
e->SetInCache(false);
|
||||
if (!e->HasRefs()) {
|
||||
// The entry is in LRU since it's in hash and has no external references
|
||||
LRU_Remove(e);
|
||||
assert(usage_ >= e->total_charge);
|
||||
usage_ -= e->total_charge;
|
||||
last_reference = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Free the entry here outside of mutex for performance reasons.
|
||||
// last_reference will only be true if e != nullptr.
|
||||
if (last_reference) {
|
||||
e->Free();
|
||||
}
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::GetUsage() const {
|
||||
DMutexLock l(mutex_);
|
||||
return usage_;
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::GetPinnedUsage() const {
|
||||
DMutexLock l(mutex_);
|
||||
assert(usage_ >= lru_usage_);
|
||||
return usage_ - lru_usage_;
|
||||
}
|
||||
|
||||
std::string LRUCacheShard::GetPrintableOptions() const { return std::string{}; }
|
||||
|
||||
LRUCache::LRUCache(size_t capacity, size_t estimated_value_size,
|
||||
int num_shard_bits, bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy)
|
||||
: ShardedCache(capacity, num_shard_bits, strict_capacity_limit) {
|
||||
num_shards_ = 1 << num_shard_bits;
|
||||
shards_ = reinterpret_cast<LRUCacheShard*>(
|
||||
port::cacheline_aligned_alloc(sizeof(LRUCacheShard) * num_shards_));
|
||||
size_t per_shard = (capacity + (num_shards_ - 1)) / num_shards_;
|
||||
for (int i = 0; i < num_shards_; i++) {
|
||||
new (&shards_[i])
|
||||
LRUCacheShard(per_shard, estimated_value_size, strict_capacity_limit,
|
||||
metadata_charge_policy);
|
||||
}
|
||||
}
|
||||
|
||||
LRUCache::~LRUCache() {
|
||||
if (shards_ != nullptr) {
|
||||
assert(num_shards_ > 0);
|
||||
for (int i = 0; i < num_shards_; i++) {
|
||||
shards_[i].~LRUCacheShard();
|
||||
}
|
||||
port::cacheline_aligned_free(shards_);
|
||||
}
|
||||
}
|
||||
|
||||
CacheShard* LRUCache::GetShard(uint32_t shard) {
|
||||
return reinterpret_cast<CacheShard*>(&shards_[shard]);
|
||||
}
|
||||
|
||||
const CacheShard* LRUCache::GetShard(uint32_t shard) const {
|
||||
return reinterpret_cast<CacheShard*>(&shards_[shard]);
|
||||
}
|
||||
|
||||
void* LRUCache::Value(Handle* handle) {
|
||||
return reinterpret_cast<const LRUHandle*>(handle)->value;
|
||||
}
|
||||
|
||||
size_t LRUCache::GetCharge(Handle* handle) const {
|
||||
CacheMetadataChargePolicy metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
if (num_shards_ > 0) {
|
||||
metadata_charge_policy = shards_[0].metadata_charge_policy_;
|
||||
}
|
||||
return reinterpret_cast<const LRUHandle*>(handle)->GetCharge(
|
||||
metadata_charge_policy);
|
||||
}
|
||||
|
||||
Cache::DeleterFn LRUCache::GetDeleter(Handle* handle) const {
|
||||
auto h = reinterpret_cast<const LRUHandle*>(handle);
|
||||
return h->deleter;
|
||||
}
|
||||
|
||||
uint32_t LRUCache::GetHash(Handle* handle) const {
|
||||
return reinterpret_cast<const LRUHandle*>(handle)->hash;
|
||||
}
|
||||
|
||||
void LRUCache::DisownData() {
|
||||
// Leak data only if that won't generate an ASAN/valgrind warning.
|
||||
if (!kMustFreeHeapAllocations) {
|
||||
shards_ = nullptr;
|
||||
num_shards_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fast_lru_cache
|
||||
|
||||
std::shared_ptr<Cache> NewFastLRUCache(
|
||||
size_t capacity, size_t estimated_value_size, int num_shard_bits,
|
||||
bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
if (num_shard_bits >= 20) {
|
||||
return nullptr; // The cache cannot be sharded into too many fine pieces.
|
||||
}
|
||||
if (num_shard_bits < 0) {
|
||||
num_shard_bits = GetDefaultCacheShardBits(capacity);
|
||||
}
|
||||
return std::make_shared<fast_lru_cache::LRUCache>(
|
||||
capacity, estimated_value_size, num_shard_bits, strict_capacity_limit,
|
||||
metadata_charge_policy);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
-311
@@ -1,311 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "cache/sharded_cache.h"
|
||||
#include "port/lang.h"
|
||||
#include "port/malloc.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/distributed_mutex.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace fast_lru_cache {
|
||||
|
||||
// An experimental (under development!) alternative to LRUCache
|
||||
|
||||
struct LRUHandle {
|
||||
void* value;
|
||||
Cache::DeleterFn deleter;
|
||||
LRUHandle* next_hash;
|
||||
LRUHandle* next;
|
||||
LRUHandle* prev;
|
||||
size_t total_charge; // TODO(opt): Only allow uint32_t?
|
||||
size_t key_length;
|
||||
// The hash of key(). Used for fast sharding and comparisons.
|
||||
uint32_t hash;
|
||||
// The number of external refs to this entry. The cache itself is not counted.
|
||||
uint32_t refs;
|
||||
|
||||
enum Flags : uint8_t {
|
||||
// Whether this entry is referenced by the hash table.
|
||||
IN_CACHE = (1 << 0),
|
||||
};
|
||||
uint8_t flags;
|
||||
|
||||
// Beginning of the key (MUST BE THE LAST FIELD IN THIS STRUCT!)
|
||||
char key_data[1];
|
||||
|
||||
Slice key() const { return Slice(key_data, key_length); }
|
||||
|
||||
// Increase the reference count by 1.
|
||||
void Ref() { refs++; }
|
||||
|
||||
// Just reduce the reference count by 1. Return true if it was last reference.
|
||||
bool Unref() {
|
||||
assert(refs > 0);
|
||||
refs--;
|
||||
return refs == 0;
|
||||
}
|
||||
|
||||
// Return true if there are external refs, false otherwise.
|
||||
bool HasRefs() const { return refs > 0; }
|
||||
|
||||
bool InCache() const { return flags & IN_CACHE; }
|
||||
|
||||
void SetInCache(bool in_cache) {
|
||||
if (in_cache) {
|
||||
flags |= IN_CACHE;
|
||||
} else {
|
||||
flags &= ~IN_CACHE;
|
||||
}
|
||||
}
|
||||
|
||||
void Free() {
|
||||
assert(refs == 0);
|
||||
if (deleter) {
|
||||
(*deleter)(key(), value);
|
||||
}
|
||||
delete[] reinterpret_cast<char*>(this);
|
||||
}
|
||||
|
||||
// Calculate the memory usage by metadata.
|
||||
inline size_t CalcMetaCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) const {
|
||||
if (metadata_charge_policy != kFullChargeCacheMetadata) {
|
||||
return 0;
|
||||
} else {
|
||||
#ifdef ROCKSDB_MALLOC_USABLE_SIZE
|
||||
return malloc_usable_size(
|
||||
const_cast<void*>(static_cast<const void*>(this)));
|
||||
#else
|
||||
// This is the size that is used when a new handle is created.
|
||||
return sizeof(LRUHandle) - 1 + key_length;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
inline void CalcTotalCharge(
|
||||
size_t charge, CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
total_charge = charge + CalcMetaCharge(metadata_charge_policy);
|
||||
}
|
||||
|
||||
inline size_t GetCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) const {
|
||||
size_t meta_charge = CalcMetaCharge(metadata_charge_policy);
|
||||
assert(total_charge >= meta_charge);
|
||||
return total_charge - meta_charge;
|
||||
}
|
||||
};
|
||||
|
||||
// We provide our own simple hash table since it removes a whole bunch
|
||||
// of porting hacks and is also faster than some of the built-in hash
|
||||
// table implementations in some of the compiler/runtime combinations
|
||||
// we have tested. E.g., readrandom speeds up by ~5% over the g++
|
||||
// 4.4.3's builtin hashtable.
|
||||
class LRUHandleTable {
|
||||
public:
|
||||
explicit LRUHandleTable(int hash_bits);
|
||||
~LRUHandleTable();
|
||||
|
||||
LRUHandle* Lookup(const Slice& key, uint32_t hash);
|
||||
LRUHandle* Insert(LRUHandle* h);
|
||||
LRUHandle* Remove(const Slice& key, uint32_t hash);
|
||||
|
||||
template <typename T>
|
||||
void ApplyToEntriesRange(T func, uint32_t index_begin, uint32_t index_end) {
|
||||
for (uint32_t i = index_begin; i < index_end; i++) {
|
||||
LRUHandle* h = list_[i];
|
||||
while (h != nullptr) {
|
||||
auto n = h->next_hash;
|
||||
assert(h->InCache());
|
||||
func(h);
|
||||
h = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int GetLengthBits() const { return length_bits_; }
|
||||
|
||||
// Return the address of the head of the chain in the bucket given
|
||||
// by the hash.
|
||||
inline LRUHandle** Head(uint32_t hash);
|
||||
|
||||
private:
|
||||
// Return a pointer to slot that points to a cache entry that
|
||||
// matches key/hash. If there is no such cache entry, return a
|
||||
// pointer to the trailing slot in the corresponding linked list.
|
||||
LRUHandle** FindPointer(const Slice& key, uint32_t hash);
|
||||
|
||||
// Number of hash bits (upper because lower bits used for sharding)
|
||||
// used for table index. Length == 1 << length_bits_
|
||||
int length_bits_;
|
||||
|
||||
// The table consists of an array of buckets where each bucket is
|
||||
// a linked list of cache entries that hash into the bucket.
|
||||
std::unique_ptr<LRUHandle*[]> list_;
|
||||
};
|
||||
|
||||
// A single shard of sharded cache.
|
||||
class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
public:
|
||||
LRUCacheShard(size_t capacity, size_t estimated_value_size,
|
||||
bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy);
|
||||
~LRUCacheShard() override = default;
|
||||
|
||||
// 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.
|
||||
void SetCapacity(size_t capacity) override;
|
||||
|
||||
// Set the flag to reject insertion if cache if full.
|
||||
void SetStrictCapacityLimit(bool strict_capacity_limit) override;
|
||||
|
||||
// Like Cache methods, but with an extra "hash" parameter.
|
||||
Status Insert(const Slice& key, uint32_t hash, void* value, size_t charge,
|
||||
Cache::DeleterFn deleter, Cache::Handle** handle,
|
||||
Cache::Priority priority) override;
|
||||
|
||||
Status Insert(const Slice& key, uint32_t hash, void* value,
|
||||
const Cache::CacheItemHelper* helper, size_t charge,
|
||||
Cache::Handle** handle, Cache::Priority priority) override {
|
||||
return Insert(key, hash, value, charge, helper->del_cb, handle, priority);
|
||||
}
|
||||
|
||||
Cache::Handle* Lookup(const Slice& key, uint32_t hash,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
const Cache::CreateCallback& /*create_cb*/,
|
||||
Cache::Priority /*priority*/, bool /*wait*/,
|
||||
Statistics* /*stats*/) override {
|
||||
return Lookup(key, hash);
|
||||
}
|
||||
Cache::Handle* Lookup(const Slice& key, uint32_t hash) override;
|
||||
|
||||
bool Release(Cache::Handle* handle, bool /*useful*/,
|
||||
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 {}
|
||||
|
||||
bool Ref(Cache::Handle* handle) override;
|
||||
bool Release(Cache::Handle* handle, bool erase_if_last_ref = false) override;
|
||||
void Erase(const Slice& key, uint32_t hash) override;
|
||||
|
||||
size_t GetUsage() const override;
|
||||
size_t GetPinnedUsage() const override;
|
||||
|
||||
void ApplyToSomeEntries(
|
||||
const std::function<void(const Slice& key, void* value, size_t charge,
|
||||
DeleterFn deleter)>& callback,
|
||||
uint32_t average_entries_per_lock, uint32_t* state) override;
|
||||
|
||||
void EraseUnRefEntries() override;
|
||||
|
||||
std::string GetPrintableOptions() const override;
|
||||
|
||||
private:
|
||||
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
|
||||
// nullptr.
|
||||
Status InsertItem(LRUHandle* item, Cache::Handle** handle,
|
||||
bool free_handle_on_fail);
|
||||
|
||||
void LRU_Remove(LRUHandle* e);
|
||||
void LRU_Insert(LRUHandle* e);
|
||||
|
||||
// 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_.
|
||||
void EvictFromLRU(size_t charge, autovector<LRUHandle*>* deleted);
|
||||
|
||||
// Returns the number of bits used to hash an element in the per-shard
|
||||
// table.
|
||||
static int GetHashBits(size_t capacity, size_t estimated_value_size,
|
||||
CacheMetadataChargePolicy metadata_charge_policy);
|
||||
|
||||
// Initialized before use.
|
||||
size_t capacity_;
|
||||
|
||||
// Whether to reject insertion if cache reaches its full capacity.
|
||||
bool strict_capacity_limit_;
|
||||
|
||||
// Dummy head of LRU list.
|
||||
// lru.prev is newest entry, lru.next is oldest entry.
|
||||
// LRU contains items which can be evicted, ie reference only by cache
|
||||
LRUHandle lru_;
|
||||
|
||||
// Pointer to head of low-pri pool in LRU list.
|
||||
LRUHandle* lru_low_pri_;
|
||||
|
||||
// ------------^^^^^^^^^^^^^-----------
|
||||
// Not frequently modified data members
|
||||
// ------------------------------------
|
||||
//
|
||||
// We separate data members that are updated frequently from the ones that
|
||||
// are not frequently updated so that they don't share the same cache line
|
||||
// which will lead into false cache sharing
|
||||
//
|
||||
// ------------------------------------
|
||||
// Frequently modified data members
|
||||
// ------------vvvvvvvvvvvvv-----------
|
||||
LRUHandleTable table_;
|
||||
|
||||
// Memory size for entries residing in the cache.
|
||||
size_t usage_;
|
||||
|
||||
// Memory size for entries residing only in the LRU list.
|
||||
size_t lru_usage_;
|
||||
|
||||
// mutex_ protects the following state.
|
||||
// We don't count mutex_ as the cache's internal state so semantically we
|
||||
// don't mind mutex_ invoking the non-const actions.
|
||||
mutable DMutex mutex_;
|
||||
};
|
||||
|
||||
class LRUCache
|
||||
#ifdef NDEBUG
|
||||
final
|
||||
#endif
|
||||
: public ShardedCache {
|
||||
public:
|
||||
LRUCache(size_t capacity, size_t estimated_value_size, int num_shard_bits,
|
||||
bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy =
|
||||
kDontChargeCacheMetadata);
|
||||
~LRUCache() override;
|
||||
const char* Name() const override { return "LRUCache"; }
|
||||
CacheShard* GetShard(uint32_t shard) override;
|
||||
const CacheShard* GetShard(uint32_t shard) const override;
|
||||
void* Value(Handle* handle) override;
|
||||
size_t GetCharge(Handle* handle) const override;
|
||||
uint32_t GetHash(Handle* handle) const override;
|
||||
DeleterFn GetDeleter(Handle* handle) const override;
|
||||
void DisownData() override;
|
||||
|
||||
private:
|
||||
LRUCacheShard* shards_ = nullptr;
|
||||
int num_shards_ = 0;
|
||||
};
|
||||
} // namespace fast_lru_cache
|
||||
|
||||
std::shared_ptr<Cache> NewFastLRUCache(
|
||||
size_t capacity, size_t estimated_value_size, int num_shard_bits,
|
||||
bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+57
-57
@@ -16,10 +16,9 @@
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "port/lang.h"
|
||||
#include "util/distributed_mutex.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace lru_cache {
|
||||
|
||||
LRUHandleTable::LRUHandleTable(int max_upper_hash_bits)
|
||||
: length_bits_(/* historical starting size*/ 4),
|
||||
@@ -135,7 +134,7 @@ LRUCacheShard::LRUCacheShard(
|
||||
void LRUCacheShard::EraseUnRefEntries() {
|
||||
autovector<LRUHandle*> last_reference_list;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
while (lru_.next != &lru_) {
|
||||
LRUHandle* old = lru_.next;
|
||||
// LRU list contains only elements which can be evicted.
|
||||
@@ -143,8 +142,9 @@ void LRUCacheShard::EraseUnRefEntries() {
|
||||
LRU_Remove(old);
|
||||
table_.Remove(old->key(), old->hash);
|
||||
old->SetInCache(false);
|
||||
assert(usage_ >= old->total_charge);
|
||||
usage_ -= old->total_charge;
|
||||
size_t total_charge = old->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(usage_ >= total_charge);
|
||||
usage_ -= total_charge;
|
||||
last_reference_list.push_back(old);
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ void LRUCacheShard::ApplyToSomeEntries(
|
||||
// The state is essentially going to be the starting hash, which works
|
||||
// nicely even if we resize between calls because we use upper-most
|
||||
// hash bits for table indexes.
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
uint32_t length_bits = table_.GetLengthBits();
|
||||
uint32_t length = uint32_t{1} << length_bits;
|
||||
|
||||
@@ -181,25 +181,23 @@ void LRUCacheShard::ApplyToSomeEntries(
|
||||
}
|
||||
|
||||
table_.ApplyToEntriesRange(
|
||||
[callback,
|
||||
metadata_charge_policy = metadata_charge_policy_](LRUHandle* h) {
|
||||
[callback](LRUHandle* h) {
|
||||
DeleterFn deleter = h->IsSecondaryCacheCompatible()
|
||||
? h->info_.helper->del_cb
|
||||
: h->info_.deleter;
|
||||
callback(h->key(), h->value, h->GetCharge(metadata_charge_policy),
|
||||
deleter);
|
||||
callback(h->key(), h->value, h->charge, deleter);
|
||||
},
|
||||
index_begin, index_end);
|
||||
}
|
||||
|
||||
void LRUCacheShard::TEST_GetLRUList(LRUHandle** lru, LRUHandle** lru_low_pri) {
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
*lru = &lru_;
|
||||
*lru_low_pri = lru_low_pri_;
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::TEST_GetLRUSize() {
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
LRUHandle* lru_handle = lru_.next;
|
||||
size_t lru_size = 0;
|
||||
while (lru_handle != &lru_) {
|
||||
@@ -210,7 +208,7 @@ size_t LRUCacheShard::TEST_GetLRUSize() {
|
||||
}
|
||||
|
||||
double LRUCacheShard::GetHighPriPoolRatio() {
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
return high_pri_pool_ratio_;
|
||||
}
|
||||
|
||||
@@ -223,17 +221,19 @@ void LRUCacheShard::LRU_Remove(LRUHandle* e) {
|
||||
e->next->prev = e->prev;
|
||||
e->prev->next = e->next;
|
||||
e->prev = e->next = nullptr;
|
||||
assert(lru_usage_ >= e->total_charge);
|
||||
lru_usage_ -= e->total_charge;
|
||||
size_t total_charge = e->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(lru_usage_ >= total_charge);
|
||||
lru_usage_ -= total_charge;
|
||||
if (e->InHighPriPool()) {
|
||||
assert(high_pri_pool_usage_ >= e->total_charge);
|
||||
high_pri_pool_usage_ -= e->total_charge;
|
||||
assert(high_pri_pool_usage_ >= total_charge);
|
||||
high_pri_pool_usage_ -= total_charge;
|
||||
}
|
||||
}
|
||||
|
||||
void LRUCacheShard::LRU_Insert(LRUHandle* e) {
|
||||
assert(e->next == nullptr);
|
||||
assert(e->prev == nullptr);
|
||||
size_t total_charge = e->CalcTotalCharge(metadata_charge_policy_);
|
||||
if (high_pri_pool_ratio_ > 0 && (e->IsHighPri() || e->HasHit())) {
|
||||
// Inset "e" to head of LRU list.
|
||||
e->next = &lru_;
|
||||
@@ -241,7 +241,7 @@ void LRUCacheShard::LRU_Insert(LRUHandle* e) {
|
||||
e->prev->next = e;
|
||||
e->next->prev = e;
|
||||
e->SetInHighPriPool(true);
|
||||
high_pri_pool_usage_ += e->total_charge;
|
||||
high_pri_pool_usage_ += total_charge;
|
||||
MaintainPoolSize();
|
||||
} else {
|
||||
// Insert "e" to the head of low-pri pool. Note that when
|
||||
@@ -253,7 +253,7 @@ void LRUCacheShard::LRU_Insert(LRUHandle* e) {
|
||||
e->SetInHighPriPool(false);
|
||||
lru_low_pri_ = e;
|
||||
}
|
||||
lru_usage_ += e->total_charge;
|
||||
lru_usage_ += total_charge;
|
||||
}
|
||||
|
||||
void LRUCacheShard::MaintainPoolSize() {
|
||||
@@ -262,8 +262,10 @@ void LRUCacheShard::MaintainPoolSize() {
|
||||
lru_low_pri_ = lru_low_pri_->next;
|
||||
assert(lru_low_pri_ != &lru_);
|
||||
lru_low_pri_->SetInHighPriPool(false);
|
||||
assert(high_pri_pool_usage_ >= lru_low_pri_->total_charge);
|
||||
high_pri_pool_usage_ -= lru_low_pri_->total_charge;
|
||||
size_t total_charge =
|
||||
lru_low_pri_->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(high_pri_pool_usage_ >= total_charge);
|
||||
high_pri_pool_usage_ -= total_charge;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,8 +278,9 @@ void LRUCacheShard::EvictFromLRU(size_t charge,
|
||||
LRU_Remove(old);
|
||||
table_.Remove(old->key(), old->hash);
|
||||
old->SetInCache(false);
|
||||
assert(usage_ >= old->total_charge);
|
||||
usage_ -= old->total_charge;
|
||||
size_t old_total_charge = old->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(usage_ >= old_total_charge);
|
||||
usage_ -= old_total_charge;
|
||||
deleted->push_back(old);
|
||||
}
|
||||
}
|
||||
@@ -285,7 +288,7 @@ void LRUCacheShard::EvictFromLRU(size_t charge,
|
||||
void LRUCacheShard::SetCapacity(size_t capacity) {
|
||||
autovector<LRUHandle*> last_reference_list;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
capacity_ = capacity;
|
||||
high_pri_pool_capacity_ = capacity_ * high_pri_pool_ratio_;
|
||||
EvictFromLRU(0, &last_reference_list);
|
||||
@@ -304,7 +307,7 @@ void LRUCacheShard::SetCapacity(size_t capacity) {
|
||||
}
|
||||
|
||||
void LRUCacheShard::SetStrictCapacityLimit(bool strict_capacity_limit) {
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
strict_capacity_limit_ = strict_capacity_limit;
|
||||
}
|
||||
|
||||
@@ -312,15 +315,16 @@ Status LRUCacheShard::InsertItem(LRUHandle* e, Cache::Handle** handle,
|
||||
bool free_handle_on_fail) {
|
||||
Status s = Status::OK();
|
||||
autovector<LRUHandle*> last_reference_list;
|
||||
size_t total_charge = e->CalcTotalCharge(metadata_charge_policy_);
|
||||
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
|
||||
// Free the space following strict LRU policy until enough space
|
||||
// is freed or the lru list is empty.
|
||||
EvictFromLRU(e->total_charge, &last_reference_list);
|
||||
EvictFromLRU(total_charge, &last_reference_list);
|
||||
|
||||
if ((usage_ + e->total_charge) > capacity_ &&
|
||||
if ((usage_ + total_charge) > capacity_ &&
|
||||
(strict_capacity_limit_ || handle == nullptr)) {
|
||||
e->SetInCache(false);
|
||||
if (handle == nullptr) {
|
||||
@@ -338,7 +342,7 @@ Status LRUCacheShard::InsertItem(LRUHandle* e, Cache::Handle** handle,
|
||||
// Insert into the cache. Note that the cache might get larger than its
|
||||
// capacity if not enough space was freed up.
|
||||
LRUHandle* old = table_.Insert(e);
|
||||
usage_ += e->total_charge;
|
||||
usage_ += total_charge;
|
||||
if (old != nullptr) {
|
||||
s = Status::OkOverwritten();
|
||||
assert(old->InCache());
|
||||
@@ -346,8 +350,10 @@ Status LRUCacheShard::InsertItem(LRUHandle* e, Cache::Handle** handle,
|
||||
if (!old->HasRefs()) {
|
||||
// old is on LRU because it's in cache and its reference count is 0.
|
||||
LRU_Remove(old);
|
||||
assert(usage_ >= old->total_charge);
|
||||
usage_ -= old->total_charge;
|
||||
size_t old_total_charge =
|
||||
old->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(usage_ >= old_total_charge);
|
||||
usage_ -= old_total_charge;
|
||||
last_reference_list.push_back(old);
|
||||
}
|
||||
}
|
||||
@@ -384,7 +390,7 @@ void LRUCacheShard::Promote(LRUHandle* e) {
|
||||
e->SetIncomplete(false);
|
||||
e->SetInCache(true);
|
||||
e->value = secondary_handle->Value();
|
||||
e->CalcTotalCharge(secondary_handle->Size(), metadata_charge_policy_);
|
||||
e->charge = secondary_handle->Size();
|
||||
delete secondary_handle;
|
||||
|
||||
// This call could fail if the cache is over capacity and
|
||||
@@ -402,9 +408,8 @@ void LRUCacheShard::Promote(LRUHandle* e) {
|
||||
} else {
|
||||
// Since the secondary cache lookup failed, mark the item as not in cache
|
||||
// Don't charge the cache as its only metadata that'll shortly be released
|
||||
DMutexLock l(mutex_);
|
||||
// TODO
|
||||
e->CalcTotalCharge(0, metadata_charge_policy_);
|
||||
MutexLock l(&mutex_);
|
||||
e->charge = 0;
|
||||
e->SetInCache(false);
|
||||
}
|
||||
}
|
||||
@@ -416,7 +421,7 @@ Cache::Handle* LRUCacheShard::Lookup(
|
||||
bool wait, Statistics* stats) {
|
||||
LRUHandle* e = nullptr;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
e = table_.Lookup(key, hash);
|
||||
if (e != nullptr) {
|
||||
assert(e->InCache());
|
||||
@@ -489,7 +494,7 @@ Cache::Handle* LRUCacheShard::Lookup(
|
||||
|
||||
bool LRUCacheShard::Ref(Cache::Handle* h) {
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(h);
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
// To create another reference - entry must be already externally referenced.
|
||||
assert(e->HasRefs());
|
||||
e->Ref();
|
||||
@@ -497,7 +502,7 @@ bool LRUCacheShard::Ref(Cache::Handle* h) {
|
||||
}
|
||||
|
||||
void LRUCacheShard::SetHighPriorityPoolRatio(double high_pri_pool_ratio) {
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
high_pri_pool_ratio_ = high_pri_pool_ratio;
|
||||
high_pri_pool_capacity_ = capacity_ * high_pri_pool_ratio_;
|
||||
MaintainPoolSize();
|
||||
@@ -510,7 +515,7 @@ bool LRUCacheShard::Release(Cache::Handle* handle, bool erase_if_last_ref) {
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(handle);
|
||||
bool last_reference = false;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
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.
|
||||
@@ -532,8 +537,9 @@ bool LRUCacheShard::Release(Cache::Handle* handle, bool erase_if_last_ref) {
|
||||
// usage. If value is null in the latter case, taht means the lookup
|
||||
// failed and we didn't charge the cache.
|
||||
if (last_reference && (!e->IsSecondaryCacheCompatible() || e->value)) {
|
||||
assert(usage_ >= e->total_charge);
|
||||
usage_ -= e->total_charge;
|
||||
size_t total_charge = e->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(usage_ >= total_charge);
|
||||
usage_ -= total_charge;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,6 +572,7 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
#endif // __SANITIZE_THREAD__
|
||||
e->info_.deleter = deleter;
|
||||
}
|
||||
e->charge = charge;
|
||||
e->key_length = key.size();
|
||||
e->hash = hash;
|
||||
e->refs = 0;
|
||||
@@ -573,7 +580,6 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
e->SetInCache(true);
|
||||
e->SetPriority(priority);
|
||||
memcpy(e->key_data, key.data(), key.size());
|
||||
e->CalcTotalCharge(charge, metadata_charge_policy_);
|
||||
|
||||
return InsertItem(e, handle, /* free_handle_on_fail */ true);
|
||||
}
|
||||
@@ -582,7 +588,7 @@ void LRUCacheShard::Erase(const Slice& key, uint32_t hash) {
|
||||
LRUHandle* e;
|
||||
bool last_reference = false;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
e = table_.Remove(key, hash);
|
||||
if (e != nullptr) {
|
||||
assert(e->InCache());
|
||||
@@ -590,8 +596,9 @@ void LRUCacheShard::Erase(const Slice& key, uint32_t hash) {
|
||||
if (!e->HasRefs()) {
|
||||
// The entry is in LRU since it's in hash and has no external references
|
||||
LRU_Remove(e);
|
||||
assert(usage_ >= e->total_charge);
|
||||
usage_ -= e->total_charge;
|
||||
size_t total_charge = e->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(usage_ >= total_charge);
|
||||
usage_ -= total_charge;
|
||||
last_reference = true;
|
||||
}
|
||||
}
|
||||
@@ -606,7 +613,7 @@ void LRUCacheShard::Erase(const Slice& key, uint32_t hash) {
|
||||
|
||||
bool LRUCacheShard::IsReady(Cache::Handle* handle) {
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(handle);
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
bool ready = true;
|
||||
if (e->IsPending()) {
|
||||
assert(secondary_cache_);
|
||||
@@ -617,12 +624,12 @@ bool LRUCacheShard::IsReady(Cache::Handle* handle) {
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::GetUsage() const {
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
return usage_;
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::GetPinnedUsage() const {
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
assert(usage_ >= lru_usage_);
|
||||
return usage_ - lru_usage_;
|
||||
}
|
||||
@@ -631,7 +638,7 @@ std::string LRUCacheShard::GetPrintableOptions() const {
|
||||
const int kBufferSize = 200;
|
||||
char buffer[kBufferSize];
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
MutexLock l(&mutex_);
|
||||
snprintf(buffer, kBufferSize, " high_pri_pool_ratio: %.3lf\n",
|
||||
high_pri_pool_ratio_);
|
||||
}
|
||||
@@ -682,12 +689,7 @@ void* LRUCache::Value(Handle* handle) {
|
||||
}
|
||||
|
||||
size_t LRUCache::GetCharge(Handle* handle) const {
|
||||
CacheMetadataChargePolicy metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
if (num_shards_ > 0) {
|
||||
metadata_charge_policy = shards_[0].metadata_charge_policy_;
|
||||
}
|
||||
return reinterpret_cast<const LRUHandle*>(handle)->GetCharge(
|
||||
metadata_charge_policy);
|
||||
return reinterpret_cast<const LRUHandle*>(handle)->charge;
|
||||
}
|
||||
|
||||
Cache::DeleterFn LRUCache::GetDeleter(Handle* handle) const {
|
||||
@@ -757,8 +759,6 @@ void LRUCache::WaitAll(std::vector<Handle*>& handles) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lru_cache
|
||||
|
||||
std::shared_ptr<Cache> NewLRUCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
|
||||
Vendored
+10
-31
@@ -17,10 +17,8 @@
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/distributed_mutex.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace lru_cache {
|
||||
|
||||
// LRU cache implementation. This class is not thread-safe.
|
||||
|
||||
@@ -67,7 +65,7 @@ struct LRUHandle {
|
||||
};
|
||||
LRUHandle* next;
|
||||
LRUHandle* prev;
|
||||
size_t total_charge; // TODO(opt): Only allow uint32_t?
|
||||
size_t charge; // TODO(opt): Only allow uint32_t?
|
||||
size_t key_length;
|
||||
// The hash of key(). Used for fast sharding and comparisons.
|
||||
uint32_t hash;
|
||||
@@ -210,32 +208,19 @@ struct LRUHandle {
|
||||
delete[] reinterpret_cast<char*>(this);
|
||||
}
|
||||
|
||||
inline size_t CalcuMetaCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) const {
|
||||
if (metadata_charge_policy != kFullChargeCacheMetadata) {
|
||||
return 0;
|
||||
} else {
|
||||
// Calculate the memory usage by metadata.
|
||||
inline size_t CalcTotalCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
size_t meta_charge = 0;
|
||||
if (metadata_charge_policy == kFullChargeCacheMetadata) {
|
||||
#ifdef ROCKSDB_MALLOC_USABLE_SIZE
|
||||
return malloc_usable_size(
|
||||
const_cast<void*>(static_cast<const void*>(this)));
|
||||
meta_charge += malloc_usable_size(static_cast<void*>(this));
|
||||
#else
|
||||
// This is the size that is used when a new handle is created.
|
||||
return sizeof(LRUHandle) - 1 + key_length;
|
||||
meta_charge += sizeof(LRUHandle) - 1 + key_length;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the memory usage by metadata.
|
||||
inline void CalcTotalCharge(
|
||||
size_t charge, CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
total_charge = charge + CalcuMetaCharge(metadata_charge_policy);
|
||||
}
|
||||
|
||||
inline size_t GetCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) const {
|
||||
size_t meta_charge = CalcuMetaCharge(metadata_charge_policy);
|
||||
assert(total_charge >= meta_charge);
|
||||
return total_charge - meta_charge;
|
||||
return charge + meta_charge;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -454,7 +439,7 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
// mutex_ protects the following state.
|
||||
// We don't count mutex_ as the cache's internal state so semantically we
|
||||
// don't mind mutex_ invoking the non-const actions.
|
||||
mutable DMutex mutex_;
|
||||
mutable port::Mutex mutex_;
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache_;
|
||||
};
|
||||
@@ -494,10 +479,4 @@ class LRUCache
|
||||
std::shared_ptr<SecondaryCache> secondary_cache_;
|
||||
};
|
||||
|
||||
} // namespace lru_cache
|
||||
|
||||
using LRUCache = lru_cache::LRUCache;
|
||||
using LRUHandle = lru_cache::LRUHandle;
|
||||
using LRUCacheShard = lru_cache::LRUCacheShard;
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
-48
@@ -9,7 +9,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "cache/cache_key.h"
|
||||
#include "cache/fast_lru_cache.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "port/port.h"
|
||||
@@ -206,53 +205,6 @@ TEST_F(LRUCacheTest, EntriesWithPriority) {
|
||||
ValidateLRUList({"e", "f", "g", "Z", "d"}, 2);
|
||||
}
|
||||
|
||||
// TODO(guido) Consolidate the following FastLRUCache tests with
|
||||
// that of LRUCache.
|
||||
class FastLRUCacheTest : public testing::Test {
|
||||
public:
|
||||
FastLRUCacheTest() {}
|
||||
~FastLRUCacheTest() override { DeleteCache(); }
|
||||
|
||||
void DeleteCache() {
|
||||
if (cache_ != nullptr) {
|
||||
cache_->~LRUCacheShard();
|
||||
port::cacheline_aligned_free(cache_);
|
||||
cache_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void NewCache(size_t capacity) {
|
||||
DeleteCache();
|
||||
cache_ = reinterpret_cast<fast_lru_cache::LRUCacheShard*>(
|
||||
port::cacheline_aligned_alloc(sizeof(fast_lru_cache::LRUCacheShard)));
|
||||
new (cache_) fast_lru_cache::LRUCacheShard(
|
||||
capacity, 1 /*estimated_value_size*/, false /*strict_capacity_limit*/,
|
||||
kDontChargeCacheMetadata);
|
||||
}
|
||||
|
||||
Status Insert(const std::string& key) {
|
||||
return cache_->Insert(key, 0 /*hash*/, nullptr /*value*/, 1 /*charge*/,
|
||||
nullptr /*deleter*/, nullptr /*handle*/,
|
||||
Cache::Priority::LOW);
|
||||
}
|
||||
|
||||
Status Insert(char key, size_t len) { return Insert(std::string(len, key)); }
|
||||
|
||||
private:
|
||||
fast_lru_cache::LRUCacheShard* cache_ = nullptr;
|
||||
};
|
||||
|
||||
TEST_F(FastLRUCacheTest, ValidateKeySize) {
|
||||
NewCache(3);
|
||||
EXPECT_OK(Insert('a', 16));
|
||||
EXPECT_NOK(Insert('b', 15));
|
||||
EXPECT_OK(Insert('b', 16));
|
||||
EXPECT_NOK(Insert('c', 17));
|
||||
EXPECT_NOK(Insert('d', 1000));
|
||||
EXPECT_NOK(Insert('e', 11));
|
||||
EXPECT_NOK(Insert('f', 0));
|
||||
}
|
||||
|
||||
class TestSecondaryCache : public SecondaryCache {
|
||||
public:
|
||||
// Specifies what action to take on a lookup for a particular key
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
ifndef PYTHON
|
||||
|
||||
# Default to python3. Some distros like CentOS 8 do not have `python`.
|
||||
ifeq ($(origin PYTHON), undefined)
|
||||
PYTHON := $(shell which python3 || which python || echo python3)
|
||||
endif
|
||||
export PYTHON
|
||||
|
||||
endif
|
||||
|
||||
# To setup tmp directory, first recognize some old variables for setting
|
||||
# test tmp directory or base tmp directory. TEST_TMPDIR is usually read
|
||||
# by RocksDB tools though Env/FileSystem::GetTestDirectory.
|
||||
ifeq ($(TEST_TMPDIR),)
|
||||
TEST_TMPDIR := $(TMPD)
|
||||
endif
|
||||
ifeq ($(TEST_TMPDIR),)
|
||||
ifeq ($(BASE_TMPDIR),)
|
||||
BASE_TMPDIR :=$(TMPDIR)
|
||||
endif
|
||||
ifeq ($(BASE_TMPDIR),)
|
||||
BASE_TMPDIR :=/tmp
|
||||
endif
|
||||
# Use /dev/shm if it has the sticky bit set (otherwise, /tmp or other
|
||||
# base dir), and create a randomly-named rocksdb.XXXX directory therein.
|
||||
TEST_TMPDIR := $(shell f=/dev/shm; test -k $$f || f=$(BASE_TMPDIR); \
|
||||
perl -le 'use File::Temp "tempdir";' \
|
||||
-e 'print tempdir("'$$f'/rocksdb.XXXX", CLEANUP => 0)')
|
||||
endif
|
||||
export TEST_TMPDIR
|
||||
+3
-3
@@ -5,7 +5,7 @@
|
||||
# build DB_STRESS_CMD so it must exist prior.
|
||||
DB_STRESS_CMD?=./db_stress
|
||||
|
||||
include common.mk
|
||||
include python.mk
|
||||
|
||||
CRASHTEST_MAKE=$(MAKE) -f crash_test.mk
|
||||
CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD)
|
||||
@@ -65,10 +65,10 @@ blackbox_crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --enable_ts blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_multiops_wc_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_multiops_txn --write_policy write_committed blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(PYTHON) -u tools/db_crashtest.py --test_multiops_txn --write_policy write_committed blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_multiops_wp_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_multiops_txn --write_policy write_prepared blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(PYTHON) -u tools/db_crashtest.py --test_multiops_txn --write_policy write_prepared blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
|
||||
@@ -23,7 +23,7 @@ Status ArenaWrappedDBIter::GetProperty(std::string prop_name,
|
||||
if (prop_name == "rocksdb.iterator.super-version-number") {
|
||||
// First try to pass the value returned from inner iterator.
|
||||
if (!db_iter_->GetProperty(prop_name, prop).ok()) {
|
||||
*prop = std::to_string(sv_number_);
|
||||
*prop = ToString(sv_number_);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -96,9 +96,9 @@ class BlobIndex {
|
||||
assert(slice.size() > 0);
|
||||
type_ = static_cast<Type>(*slice.data());
|
||||
if (type_ >= Type::kUnknown) {
|
||||
return Status::Corruption(kErrorMessage,
|
||||
"Unknown blob index type: " +
|
||||
std::to_string(static_cast<char>(type_)));
|
||||
return Status::Corruption(
|
||||
kErrorMessage,
|
||||
"Unknown blob index type: " + ToString(static_cast<char>(type_)));
|
||||
}
|
||||
slice = Slice(slice.data() + 1, slice.size() - 1);
|
||||
if (HasTTL()) {
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_source.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_file_reader.h"
|
||||
#include "options/cf_options.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
BlobSource::BlobSource(const ImmutableOptions* immutable_options,
|
||||
const std::string& db_id,
|
||||
const std::string& db_session_id,
|
||||
BlobFileCache* blob_file_cache)
|
||||
: db_id_(db_id),
|
||||
db_session_id_(db_session_id),
|
||||
statistics_(immutable_options->statistics.get()),
|
||||
blob_file_cache_(blob_file_cache),
|
||||
blob_cache_(immutable_options->blob_cache) {}
|
||||
|
||||
BlobSource::~BlobSource() = default;
|
||||
|
||||
Status BlobSource::GetBlobFromCache(const Slice& cache_key,
|
||||
CachableEntry<std::string>* blob) const {
|
||||
assert(blob);
|
||||
assert(blob->IsEmpty());
|
||||
assert(blob_cache_);
|
||||
assert(!cache_key.empty());
|
||||
|
||||
Cache::Handle* cache_handle = nullptr;
|
||||
cache_handle = GetEntryFromCache(cache_key);
|
||||
if (cache_handle != nullptr) {
|
||||
blob->SetCachedValue(
|
||||
static_cast<std::string*>(blob_cache_->Value(cache_handle)),
|
||||
blob_cache_.get(), cache_handle);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
assert(blob->IsEmpty());
|
||||
|
||||
return Status::NotFound("Blob not found in cache");
|
||||
}
|
||||
|
||||
Status BlobSource::PutBlobIntoCache(const Slice& cache_key,
|
||||
CachableEntry<std::string>* cached_blob,
|
||||
PinnableSlice* blob) const {
|
||||
assert(blob);
|
||||
assert(!cache_key.empty());
|
||||
assert(blob_cache_);
|
||||
|
||||
Status s;
|
||||
const Cache::Priority priority = Cache::Priority::LOW;
|
||||
|
||||
// Objects to be put into the cache have to be heap-allocated and
|
||||
// self-contained, i.e. own their contents. The Cache has to be able to take
|
||||
// unique ownership of them. Therefore, we copy the blob into a string
|
||||
// directly, and insert that into the cache.
|
||||
std::string* buf = new std::string();
|
||||
buf->assign(blob->data(), blob->size());
|
||||
|
||||
// TODO: support custom allocators and provide a better estimated memory
|
||||
// usage using malloc_usable_size.
|
||||
Cache::Handle* cache_handle = nullptr;
|
||||
s = InsertEntryIntoCache(cache_key, buf, buf->size(), &cache_handle,
|
||||
priority);
|
||||
if (s.ok()) {
|
||||
assert(cache_handle != nullptr);
|
||||
cached_blob->SetCachedValue(buf, blob_cache_.get(), cache_handle);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Status BlobSource::GetBlob(const ReadOptions& read_options,
|
||||
const Slice& user_key, uint64_t file_number,
|
||||
uint64_t offset, uint64_t file_size,
|
||||
uint64_t value_size,
|
||||
CompressionType compression_type,
|
||||
FilePrefetchBuffer* prefetch_buffer,
|
||||
PinnableSlice* value, uint64_t* bytes_read) {
|
||||
assert(value);
|
||||
|
||||
Status s;
|
||||
|
||||
const CacheKey cache_key = GetCacheKey(file_number, file_size, offset);
|
||||
|
||||
CachableEntry<std::string> blob_entry;
|
||||
|
||||
// First, try to get the blob from the cache
|
||||
//
|
||||
// If blob cache is enabled, we'll try to read from it.
|
||||
if (blob_cache_) {
|
||||
Slice key = cache_key.AsSlice();
|
||||
s = GetBlobFromCache(key, &blob_entry);
|
||||
if (s.ok() && blob_entry.GetValue()) {
|
||||
assert(blob_entry.GetValue()->size() == value_size);
|
||||
if (bytes_read) {
|
||||
*bytes_read = value_size;
|
||||
}
|
||||
value->PinSelf(*blob_entry.GetValue());
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
assert(blob_entry.IsEmpty());
|
||||
|
||||
const bool no_io = read_options.read_tier == kBlockCacheTier;
|
||||
if (no_io) {
|
||||
return Status::Incomplete("Cannot read blob(s): no disk I/O allowed");
|
||||
}
|
||||
|
||||
// Can't find the blob from the cache. Since I/O is allowed, read from the
|
||||
// file.
|
||||
{
|
||||
CacheHandleGuard<BlobFileReader> blob_file_reader;
|
||||
s = blob_file_cache_->GetBlobFileReader(file_number, &blob_file_reader);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
assert(blob_file_reader.GetValue());
|
||||
|
||||
if (compression_type != blob_file_reader.GetValue()->GetCompressionType()) {
|
||||
return Status::Corruption("Compression type mismatch when reading blob");
|
||||
}
|
||||
|
||||
s = blob_file_reader.GetValue()->GetBlob(
|
||||
read_options, user_key, offset, value_size, compression_type,
|
||||
prefetch_buffer, value, bytes_read);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
if (blob_cache_ && read_options.fill_cache) {
|
||||
// If filling cache is allowed and a cache is configured, try to put the
|
||||
// blob to the cache.
|
||||
Slice key = cache_key.AsSlice();
|
||||
s = PutBlobIntoCache(key, &blob_entry, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
assert(s.ok());
|
||||
return s;
|
||||
}
|
||||
|
||||
bool BlobSource::TEST_BlobInCache(uint64_t file_number, uint64_t file_size,
|
||||
uint64_t offset) const {
|
||||
const CacheKey cache_key = GetCacheKey(file_number, file_size, offset);
|
||||
const Slice key = cache_key.AsSlice();
|
||||
|
||||
CachableEntry<std::string> blob_entry;
|
||||
const Status s = GetBlobFromCache(key, &blob_entry);
|
||||
|
||||
if (s.ok() && blob_entry.GetValue() != nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1,88 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "cache/cache_helpers.h"
|
||||
#include "cache/cache_key.h"
|
||||
#include "db/blob/blob_file_cache.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
#include "table/block_based/cachable_entry.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
struct ImmutableOptions;
|
||||
class Status;
|
||||
class FilePrefetchBuffer;
|
||||
class Slice;
|
||||
|
||||
// BlobSource is a class that provides universal access to blobs, regardless of
|
||||
// whether they are in the blob cache, secondary cache, or (remote) storage.
|
||||
// Depending on user settings, it always fetch blobs from multi-tier cache and
|
||||
// storage with minimal cost.
|
||||
class BlobSource {
|
||||
public:
|
||||
BlobSource(const ImmutableOptions* immutable_options,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
BlobFileCache* blob_file_cache);
|
||||
|
||||
BlobSource(const BlobSource&) = delete;
|
||||
BlobSource& operator=(const BlobSource&) = delete;
|
||||
|
||||
~BlobSource();
|
||||
|
||||
Status GetBlob(const ReadOptions& read_options, const Slice& user_key,
|
||||
uint64_t file_number, uint64_t offset, uint64_t file_size,
|
||||
uint64_t value_size, CompressionType compression_type,
|
||||
FilePrefetchBuffer* prefetch_buffer, PinnableSlice* value,
|
||||
uint64_t* bytes_read);
|
||||
|
||||
bool TEST_BlobInCache(uint64_t file_number, uint64_t file_size,
|
||||
uint64_t offset) const;
|
||||
|
||||
private:
|
||||
Status GetBlobFromCache(const Slice& cache_key,
|
||||
CachableEntry<std::string>* blob) const;
|
||||
|
||||
Status PutBlobIntoCache(const Slice& cache_key,
|
||||
CachableEntry<std::string>* cached_blob,
|
||||
PinnableSlice* blob) const;
|
||||
|
||||
inline CacheKey GetCacheKey(uint64_t file_number, uint64_t file_size,
|
||||
uint64_t offset) const {
|
||||
OffsetableCacheKey base_cache_key(db_id_, db_session_id_, file_number,
|
||||
file_size);
|
||||
return base_cache_key.WithOffset(offset);
|
||||
}
|
||||
|
||||
inline Cache::Handle* GetEntryFromCache(const Slice& key) const {
|
||||
return blob_cache_->Lookup(key, statistics_);
|
||||
}
|
||||
|
||||
inline Status InsertEntryIntoCache(const Slice& key, std::string* value,
|
||||
size_t charge,
|
||||
Cache::Handle** cache_handle,
|
||||
Cache::Priority priority) const {
|
||||
return blob_cache_->Insert(key, value, charge,
|
||||
&DeleteCacheEntry<std::string>, cache_handle,
|
||||
priority);
|
||||
}
|
||||
|
||||
const std::string db_id_;
|
||||
const std::string db_session_id_;
|
||||
|
||||
Statistics* statistics_;
|
||||
|
||||
// A cache to store blob file reader.
|
||||
BlobFileCache* blob_file_cache_;
|
||||
|
||||
// A cache to store uncompressed blobs.
|
||||
std::shared_ptr<Cache> blob_cache_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1,298 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_source.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_file_cache.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "db/blob/blob_log_writer.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/read_write_util.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "util/compression.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
|
||||
// Creates a test blob file with `num` blobs in it.
|
||||
void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
uint32_t column_family_id, bool has_ttl,
|
||||
const ExpirationRange& expiration_range_header,
|
||||
const ExpirationRange& expiration_range_footer,
|
||||
uint64_t blob_file_number, const std::vector<Slice>& keys,
|
||||
const std::vector<Slice>& blobs, CompressionType compression,
|
||||
std::vector<uint64_t>& blob_offsets,
|
||||
std::vector<uint64_t>& blob_sizes) {
|
||||
assert(!immutable_options.cf_paths.empty());
|
||||
size_t num = keys.size();
|
||||
assert(num == blobs.size());
|
||||
assert(num == blob_offsets.size());
|
||||
assert(num == blob_sizes.size());
|
||||
|
||||
const std::string blob_file_path =
|
||||
BlobFileName(immutable_options.cf_paths.front().path, blob_file_number);
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
ASSERT_OK(NewWritableFile(immutable_options.fs.get(), blob_file_path, &file,
|
||||
FileOptions()));
|
||||
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), blob_file_path, FileOptions(), immutable_options.clock));
|
||||
|
||||
constexpr Statistics* statistics = nullptr;
|
||||
constexpr bool use_fsync = false;
|
||||
constexpr bool do_flush = false;
|
||||
|
||||
BlobLogWriter blob_log_writer(std::move(file_writer), immutable_options.clock,
|
||||
statistics, blob_file_number, use_fsync,
|
||||
do_flush);
|
||||
|
||||
BlobLogHeader header(column_family_id, compression, has_ttl,
|
||||
expiration_range_header);
|
||||
|
||||
ASSERT_OK(blob_log_writer.WriteHeader(header));
|
||||
|
||||
std::vector<std::string> compressed_blobs(num);
|
||||
std::vector<Slice> blobs_to_write(num);
|
||||
if (kNoCompression == compression) {
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
blobs_to_write[i] = blobs[i];
|
||||
blob_sizes[i] = blobs[i].size();
|
||||
}
|
||||
} else {
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(compression);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
compression, sample_for_compression);
|
||||
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
ASSERT_TRUE(CompressData(blobs[i], info, compression_format_version,
|
||||
&compressed_blobs[i]));
|
||||
blobs_to_write[i] = compressed_blobs[i];
|
||||
blob_sizes[i] = compressed_blobs[i].size();
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
uint64_t key_offset = 0;
|
||||
ASSERT_OK(blob_log_writer.AddRecord(keys[i], blobs_to_write[i], &key_offset,
|
||||
&blob_offsets[i]));
|
||||
}
|
||||
|
||||
BlobLogFooter footer;
|
||||
footer.blob_count = num;
|
||||
footer.expiration_range = expiration_range_footer;
|
||||
|
||||
std::string checksum_method;
|
||||
std::string checksum_value;
|
||||
ASSERT_OK(
|
||||
blob_log_writer.AppendFooter(footer, &checksum_method, &checksum_value));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class BlobSourceTest : public DBTestBase {
|
||||
protected:
|
||||
public:
|
||||
explicit BlobSourceTest()
|
||||
: DBTestBase("blob_source_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(BlobSourceTest, GetBlobsFromCache) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.cf_paths.emplace_back(
|
||||
test::PerThreadDBPath(env_, "BlobSourceTest_GetBlobsFromCache"), 0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
LRUCacheOptions co;
|
||||
co.capacity = 2048;
|
||||
co.num_shard_bits = 2;
|
||||
co.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
options.blob_cache = NewLRUCache(co);
|
||||
options.lowest_used_cache_tier = CacheTier::kVolatileTier;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
std::string db_id;
|
||||
ASSERT_OK(db_->GetDbIdentity(db_id));
|
||||
|
||||
std::string db_session_id;
|
||||
ASSERT_OK(db_->GetDbSessionId(db_session_id));
|
||||
|
||||
ImmutableOptions immutable_options(options);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
constexpr ExpirationRange expiration_range;
|
||||
constexpr uint64_t blob_file_number = 1;
|
||||
constexpr size_t num_blobs = 16;
|
||||
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<std::string> blob_strs;
|
||||
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
key_strs.push_back("key" + std::to_string(i));
|
||||
blob_strs.push_back("blob" + std::to_string(i));
|
||||
}
|
||||
|
||||
std::vector<Slice> keys;
|
||||
std::vector<Slice> blobs;
|
||||
|
||||
uint64_t file_size = BlobLogHeader::kSize;
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
keys.push_back({key_strs[i]});
|
||||
blobs.push_back({blob_strs[i]});
|
||||
file_size += BlobLogRecord::kHeaderSize + keys[i].size() + blobs[i].size();
|
||||
}
|
||||
file_size += BlobLogFooter::kSize;
|
||||
|
||||
std::vector<uint64_t> blob_offsets(keys.size());
|
||||
std::vector<uint64_t> blob_sizes(keys.size());
|
||||
|
||||
WriteBlobFile(immutable_options, column_family_id, has_ttl, expiration_range,
|
||||
expiration_range, blob_file_number, keys, blobs, kNoCompression,
|
||||
blob_offsets, blob_sizes);
|
||||
|
||||
constexpr size_t capacity = 1024;
|
||||
std::shared_ptr<Cache> backing_cache =
|
||||
NewLRUCache(capacity); // Blob file cache
|
||||
|
||||
FileOptions file_options;
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
std::unique_ptr<BlobFileCache> blob_file_cache(new BlobFileCache(
|
||||
backing_cache.get(), &immutable_options, &file_options, column_family_id,
|
||||
blob_file_read_hist, nullptr /*IOTracer*/));
|
||||
|
||||
BlobSource blob_source(&immutable_options, db_id, db_session_id,
|
||||
blob_file_cache.get());
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
|
||||
constexpr FilePrefetchBuffer* prefetch_buffer = nullptr;
|
||||
|
||||
{
|
||||
// GetBlob
|
||||
std::vector<PinnableSlice> values(keys.size());
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
read_options.fill_cache = false;
|
||||
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
ASSERT_FALSE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
kNoCompression, prefetch_buffer, &values[i],
|
||||
&bytes_read));
|
||||
ASSERT_EQ(values[i], blobs[i]);
|
||||
ASSERT_EQ(bytes_read,
|
||||
blob_sizes[i] + keys[i].size() + BlobLogRecord::kHeaderSize);
|
||||
|
||||
ASSERT_FALSE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
}
|
||||
|
||||
read_options.fill_cache = true;
|
||||
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
ASSERT_FALSE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
kNoCompression, prefetch_buffer, &values[i],
|
||||
&bytes_read));
|
||||
ASSERT_EQ(values[i], blobs[i]);
|
||||
ASSERT_EQ(bytes_read,
|
||||
blob_sizes[i] + keys[i].size() + BlobLogRecord::kHeaderSize);
|
||||
|
||||
ASSERT_TRUE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
}
|
||||
|
||||
read_options.fill_cache = true;
|
||||
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
ASSERT_TRUE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
kNoCompression, prefetch_buffer, &values[i],
|
||||
&bytes_read));
|
||||
ASSERT_EQ(values[i], blobs[i]);
|
||||
ASSERT_EQ(bytes_read, blob_sizes[i]);
|
||||
|
||||
ASSERT_TRUE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
}
|
||||
|
||||
// Cache-only GetBlob
|
||||
read_options.read_tier = ReadTier::kBlockCacheTier;
|
||||
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
ASSERT_TRUE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
kNoCompression, prefetch_buffer, &values[i],
|
||||
&bytes_read));
|
||||
ASSERT_EQ(values[i], blobs[i]);
|
||||
ASSERT_EQ(bytes_read, blob_sizes[i]);
|
||||
|
||||
ASSERT_TRUE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
}
|
||||
}
|
||||
|
||||
options.blob_cache->EraseUnRefEntries();
|
||||
|
||||
{
|
||||
// Cache-only GetBlob
|
||||
std::vector<PinnableSlice> values(keys.size());
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
read_options.read_tier = ReadTier::kBlockCacheTier;
|
||||
read_options.fill_cache = true;
|
||||
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
ASSERT_FALSE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
|
||||
ASSERT_NOK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
kNoCompression, prefetch_buffer,
|
||||
&values[i], &bytes_read));
|
||||
ASSERT_TRUE(values[i].empty());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
|
||||
ASSERT_FALSE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -243,84 +243,6 @@ TEST_F(DBBlobCompactionTest, FilterByKeyLength) {
|
||||
Close();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBlobCompactionTest, BlobCompactWithStartingLevel) {
|
||||
Options options = GetDefaultOptions();
|
||||
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 1000;
|
||||
options.blob_file_starting_level = 5;
|
||||
options.create_if_missing = true;
|
||||
|
||||
// Open DB with fixed-prefix sst-partitioner so that compaction will cut
|
||||
// new table file when encountering a new key whose 1-byte prefix changes.
|
||||
constexpr size_t key_len = 1;
|
||||
options.sst_partitioner_factory =
|
||||
NewSstPartitionerFixedPrefixFactory(key_len);
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
constexpr size_t blob_size = 3000;
|
||||
|
||||
constexpr char first_key[] = "a";
|
||||
const std::string first_blob(blob_size, 'a');
|
||||
ASSERT_OK(Put(first_key, first_blob));
|
||||
|
||||
constexpr char second_key[] = "b";
|
||||
const std::string second_blob(2 * blob_size, 'b');
|
||||
ASSERT_OK(Put(second_key, second_blob));
|
||||
|
||||
constexpr char third_key[] = "d";
|
||||
const std::string third_blob(blob_size, 'd');
|
||||
ASSERT_OK(Put(third_key, third_blob));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
constexpr char fourth_key[] = "c";
|
||||
const std::string fourth_blob(blob_size, 'c');
|
||||
ASSERT_OK(Put(fourth_key, fourth_blob));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_EQ(0, GetBlobFileNumbers().size());
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(/*level=*/0));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(/*level=*/1));
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr));
|
||||
|
||||
// No blob file should be created since blob_file_starting_level is 5.
|
||||
ASSERT_EQ(0, GetBlobFileNumbers().size());
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(/*level=*/0));
|
||||
ASSERT_EQ(4, NumTableFilesAtLevel(/*level=*/1));
|
||||
|
||||
{
|
||||
options.blob_file_starting_level = 1;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put(first_key, first_blob));
|
||||
ASSERT_OK(Put(second_key, second_blob));
|
||||
ASSERT_OK(Put(third_key, third_blob));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put(fourth_key, fourth_blob));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_EQ(0, GetBlobFileNumbers().size());
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(/*level=*/0));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(/*level=*/1));
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr));
|
||||
// The compaction's output level equals to blob_file_starting_level.
|
||||
ASSERT_EQ(1, GetBlobFileNumbers().size());
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(/*level=*/0));
|
||||
ASSERT_EQ(4, NumTableFilesAtLevel(/*level=*/1));
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(DBBlobCompactionTest, BlindWriteFilter) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
|
||||
@@ -153,11 +153,11 @@ TEST_F(DBBlobIndexTest, Write) {
|
||||
key_values.reserve(num_key_values);
|
||||
|
||||
for (size_t i = 1; i <= num_key_values; ++i) {
|
||||
std::string key = "key" + std::to_string(i);
|
||||
std::string key = "key" + ToString(i);
|
||||
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeInlinedTTL(&blob_index, /* expiration */ 9876543210,
|
||||
"blob" + std::to_string(i));
|
||||
"blob" + ToString(i));
|
||||
|
||||
key_values.emplace_back(std::move(key), std::move(blob_index));
|
||||
}
|
||||
@@ -230,7 +230,7 @@ TEST_F(DBBlobIndexTest, Updated) {
|
||||
DestroyAndReopen(GetTestOptions());
|
||||
WriteBatch batch;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(PutBlobIndex(&batch, "key" + std::to_string(i), blob_index));
|
||||
ASSERT_OK(PutBlobIndex(&batch, "key" + ToString(i), blob_index));
|
||||
}
|
||||
ASSERT_OK(Write(&batch));
|
||||
// Avoid blob values from being purged.
|
||||
@@ -248,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" + std::to_string(i), snapshot));
|
||||
ASSERT_EQ(blob_index, GetBlobIndex("key" + ToString(i), snapshot));
|
||||
}
|
||||
ASSERT_EQ("new_value", Get("key1"));
|
||||
if (tier <= kImmutableMemtables) {
|
||||
@@ -260,7 +260,7 @@ TEST_F(DBBlobIndexTest, Updated) {
|
||||
ASSERT_EQ("NOT_FOUND", Get("key4"));
|
||||
ASSERT_EQ("a,b,c", GetImpl("key5"));
|
||||
for (int i = 6; i < 9; i++) {
|
||||
ASSERT_EQ("NOT_FOUND", Get("key" + std::to_string(i)));
|
||||
ASSERT_EQ("NOT_FOUND", Get("key" + ToString(i)));
|
||||
}
|
||||
ASSERT_EQ(blob_index, GetBlobIndex("key9"));
|
||||
dbfull()->ReleaseSnapshot(snapshot);
|
||||
@@ -301,7 +301,7 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
};
|
||||
|
||||
auto get_value = [&](int index, int version) {
|
||||
return get_key(index) + "_value" + std::to_string(version);
|
||||
return get_key(index) + "_value" + ToString(version);
|
||||
};
|
||||
|
||||
auto check_iterator = [&](Iterator* iterator, Status::Code expected_status,
|
||||
@@ -501,7 +501,7 @@ TEST_F(DBBlobIndexTest, IntegratedBlobIterate) {
|
||||
auto get_key = [](size_t index) { return ("key" + std::to_string(index)); };
|
||||
|
||||
auto get_value = [&](size_t index, size_t version) {
|
||||
return get_key(index) + "_value" + std::to_string(version);
|
||||
return get_key(index) + "_value" + ToString(version);
|
||||
};
|
||||
|
||||
auto check_iterator = [&](Iterator* iterator, Status expected_status,
|
||||
|
||||
+12
-29
@@ -37,7 +37,6 @@
|
||||
#include "table/block_based/block_based_table_builder.h"
|
||||
#include "table/format.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/stop_watch.h"
|
||||
|
||||
@@ -116,7 +115,6 @@ Status BuildTable(
|
||||
assert(fs);
|
||||
|
||||
TableProperties tp;
|
||||
bool table_file_created = false;
|
||||
if (iter->Valid() || !range_del_agg->IsEmpty()) {
|
||||
std::unique_ptr<CompactionFilter> compaction_filter;
|
||||
if (ioptions.compaction_filter_factory != nullptr &&
|
||||
@@ -160,8 +158,6 @@ Status BuildTable(
|
||||
file_checksum_func_name);
|
||||
return s;
|
||||
}
|
||||
|
||||
table_file_created = true;
|
||||
FileTypeSet tmp_set = ioptions.checksum_handoff_file_types;
|
||||
file->SetIOPriority(io_priority);
|
||||
file->SetWriteLifeTimeHint(write_hint);
|
||||
@@ -181,10 +177,7 @@ Status BuildTable(
|
||||
snapshots.empty() ? 0 : snapshots.back(), snapshot_checker);
|
||||
|
||||
std::unique_ptr<BlobFileBuilder> blob_file_builder(
|
||||
(mutable_cf_options.enable_blob_files &&
|
||||
tboptions.level_at_creation >=
|
||||
mutable_cf_options.blob_file_starting_level &&
|
||||
blob_file_additions)
|
||||
(mutable_cf_options.enable_blob_files && blob_file_additions)
|
||||
? new BlobFileBuilder(
|
||||
versions, fs, &ioptions, &mutable_cf_options, &file_options,
|
||||
job_id, tboptions.column_family_id,
|
||||
@@ -193,7 +186,6 @@ Status BuildTable(
|
||||
&blob_file_paths, blob_file_additions)
|
||||
: nullptr);
|
||||
|
||||
const std::atomic<bool> kManualCompactionCanceledFalse{false};
|
||||
CompactionIterator c_iter(
|
||||
iter, tboptions.internal_comparator.user_comparator(), &merge,
|
||||
kMaxSequenceNumber, &snapshots, earliest_write_conflict_snapshot,
|
||||
@@ -201,10 +193,11 @@ Status BuildTable(
|
||||
ShouldReportDetailedTime(env, ioptions.stats),
|
||||
true /* internal key corruption is not ok */, range_del_agg.get(),
|
||||
blob_file_builder.get(), ioptions.allow_data_in_errors,
|
||||
ioptions.enforce_single_del_contracts,
|
||||
/*manual_compaction_canceled=*/kManualCompactionCanceledFalse,
|
||||
/*compaction=*/nullptr, compaction_filter.get(),
|
||||
/*shutting_down=*/nullptr, db_options.info_log, full_history_ts_low);
|
||||
/*shutting_down=*/nullptr,
|
||||
/*manual_compaction_paused=*/nullptr,
|
||||
/*manual_compaction_canceled=*/nullptr, db_options.info_log,
|
||||
full_history_ts_low);
|
||||
|
||||
c_iter.SeekToFirst();
|
||||
for (; c_iter.Valid(); c_iter.Next()) {
|
||||
@@ -313,15 +306,6 @@ Status BuildTable(
|
||||
meta->file_checksum_func_name = file_writer->GetFileChecksumFuncName();
|
||||
file_checksum = meta->file_checksum;
|
||||
file_checksum_func_name = meta->file_checksum_func_name;
|
||||
// Set unique_id only if db_id and db_session_id exist
|
||||
if (!tboptions.db_id.empty() && !tboptions.db_session_id.empty()) {
|
||||
if (!GetSstInternalUniqueId(tboptions.db_id, tboptions.db_session_id,
|
||||
meta->fd.GetNumber(), &(meta->unique_id))
|
||||
.ok()) {
|
||||
// if failed to get unique id, just set it Null
|
||||
meta->unique_id = kNullUniqueId64x2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -343,9 +327,10 @@ Status BuildTable(
|
||||
if (s.ok() && !empty) {
|
||||
// Verify that the table is usable
|
||||
// We set for_compaction to false and don't OptimizeForCompactionTableRead
|
||||
// here because this is a special case after we finish the table building.
|
||||
// here because this is a special case after we finish the table building
|
||||
// No matter whether use_direct_io_for_flush_and_compaction is true,
|
||||
// the goal is to cache it here for further user reads.
|
||||
// we will regrad this verification as user reads since the goal is
|
||||
// to cache it here for further user reads
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<InternalIterator> it(table_cache->NewIterator(
|
||||
read_options, file_options, tboptions.internal_comparator, *meta,
|
||||
@@ -386,17 +371,15 @@ Status BuildTable(
|
||||
|
||||
constexpr IODebugContext* dbg = nullptr;
|
||||
|
||||
if (table_file_created) {
|
||||
Status ignored = fs->DeleteFile(fname, IOOptions(), dbg);
|
||||
ignored.PermitUncheckedError();
|
||||
}
|
||||
Status ignored = fs->DeleteFile(fname, IOOptions(), dbg);
|
||||
ignored.PermitUncheckedError();
|
||||
|
||||
assert(blob_file_additions || blob_file_paths.empty());
|
||||
|
||||
if (blob_file_additions) {
|
||||
for (const std::string& blob_file_path : blob_file_paths) {
|
||||
Status ignored = DeleteDBFile(&db_options, blob_file_path, dbname,
|
||||
/*force_bg=*/false, /*force_fg=*/false);
|
||||
ignored = DeleteDBFile(&db_options, blob_file_path, dbname,
|
||||
/*force_bg=*/false, /*force_fg=*/false);
|
||||
ignored.PermitUncheckedError();
|
||||
TEST_SYNC_POINT("BuildTable::AfterDeleteFile");
|
||||
}
|
||||
|
||||
+32
-318
@@ -33,6 +33,11 @@ int geteuid() {
|
||||
return result;
|
||||
}
|
||||
|
||||
// VS < 2015
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
const char* phase = "";
|
||||
@@ -164,16 +169,6 @@ static void CheckPinGetCF(rocksdb_t* db, const rocksdb_readoptions_t* options,
|
||||
rocksdb_pinnableslice_destroy(p);
|
||||
}
|
||||
|
||||
static void CheckMultiGetValues(size_t num_keys, char** values,
|
||||
size_t* values_sizes, char** errs,
|
||||
const char** expected) {
|
||||
for (size_t i = 0; i < num_keys; i++) {
|
||||
CheckNoError(errs[i]);
|
||||
CheckEqual(expected[i], values[i], values_sizes[i]);
|
||||
Free(&values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckIter(rocksdb_iterator_t* iter,
|
||||
const char* key, const char* val) {
|
||||
size_t len;
|
||||
@@ -288,16 +283,6 @@ static rocksdb_t* CheckCompaction(rocksdb_t* db, rocksdb_options_t* options,
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "baz", "bazvalue");
|
||||
|
||||
// Disable compaction
|
||||
rocksdb_disable_manual_compaction(db);
|
||||
rocksdb_compact_range(db, NULL, 0, NULL, 0);
|
||||
// should not filter anything when disabled
|
||||
CheckGet(db, roptions, "foo", "foovalue");
|
||||
CheckGet(db, roptions, "bar", "barvalue");
|
||||
CheckGet(db, roptions, "baz", "bazvalue");
|
||||
// Reenable compaction
|
||||
rocksdb_enable_manual_compaction(db);
|
||||
|
||||
// Force compaction
|
||||
rocksdb_compact_range(db, NULL, 0, NULL, 0);
|
||||
// should have filtered bar, but not foo
|
||||
@@ -381,49 +366,18 @@ static void CheckTxnGetCF(rocksdb_transaction_t* txn,
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckTxnPinGet(rocksdb_transaction_t* txn,
|
||||
const rocksdb_readoptions_t* options,
|
||||
const char* key, const char* expected) {
|
||||
rocksdb_pinnableslice_t* p = NULL;
|
||||
const char* val = NULL;
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
p = rocksdb_transaction_get_pinned(txn, options, key, strlen(key), &err);
|
||||
CheckNoError(err);
|
||||
val = rocksdb_pinnableslice_value(p, &val_len);
|
||||
CheckEqual(expected, val, val_len);
|
||||
rocksdb_pinnableslice_destroy(p);
|
||||
}
|
||||
|
||||
static void CheckTxnPinGetCF(rocksdb_transaction_t* txn,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, const char* expected) {
|
||||
rocksdb_pinnableslice_t* p = NULL;
|
||||
const char* val = NULL;
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
p = rocksdb_transaction_get_pinned_cf(txn, options, column_family, key,
|
||||
strlen(key), &err);
|
||||
CheckNoError(err);
|
||||
val = rocksdb_pinnableslice_value(p, &val_len);
|
||||
CheckEqual(expected, val, val_len);
|
||||
rocksdb_pinnableslice_destroy(p);
|
||||
}
|
||||
|
||||
static void CheckTxnDBGet(
|
||||
rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
const char* key,
|
||||
const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
val = rocksdb_transactiondb_get(txn_db, options, key, strlen(key), &val_len,
|
||||
&err);
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, val, val_len);
|
||||
Free(&val);
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
val = rocksdb_transactiondb_get(txn_db, options, key, strlen(key), &val_len, &err);
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, val, val_len);
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckTxnDBGetCF(rocksdb_transactiondb_t* txn_db,
|
||||
@@ -440,36 +394,6 @@ static void CheckTxnDBGetCF(rocksdb_transactiondb_t* txn_db,
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckTxnDBPinGet(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
const char* key, const char* expected) {
|
||||
rocksdb_pinnableslice_t* p = NULL;
|
||||
const char* val = NULL;
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
p = rocksdb_transactiondb_get_pinned(txn_db, options, key, strlen(key), &err);
|
||||
CheckNoError(err);
|
||||
val = rocksdb_pinnableslice_value(p, &val_len);
|
||||
CheckEqual(expected, val, val_len);
|
||||
rocksdb_pinnableslice_destroy(p);
|
||||
}
|
||||
|
||||
static void CheckTxnDBPinGetCF(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, const char* expected) {
|
||||
rocksdb_pinnableslice_t* p = NULL;
|
||||
const char* val = NULL;
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
p = rocksdb_transactiondb_get_pinned_cf(txn_db, options, column_family, key,
|
||||
strlen(key), &err);
|
||||
CheckNoError(err);
|
||||
val = rocksdb_pinnableslice_value(p, &val_len);
|
||||
CheckEqual(expected, val, val_len);
|
||||
rocksdb_pinnableslice_destroy(p);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
@@ -990,9 +914,24 @@ int main(int argc, char** argv) {
|
||||
char* vals[3];
|
||||
size_t vals_sizes[3];
|
||||
char* errs[3];
|
||||
const char* expected[3] = {"c", "hello", NULL};
|
||||
rocksdb_multi_get(db, roptions, 3, keys, keys_sizes, vals, vals_sizes, errs);
|
||||
CheckMultiGetValues(3, vals, vals_sizes, errs, expected);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
CheckEqual(NULL, errs[i], 0);
|
||||
switch (i) {
|
||||
case 0:
|
||||
CheckEqual("c", vals[i], vals_sizes[i]);
|
||||
break;
|
||||
case 1:
|
||||
CheckEqual("hello", vals[i], vals_sizes[i]);
|
||||
break;
|
||||
case 2:
|
||||
CheckEqual(NULL, vals[i], vals_sizes[i]);
|
||||
break;
|
||||
}
|
||||
Free(&vals[i]);
|
||||
}
|
||||
}
|
||||
|
||||
StartPhase("pin_get");
|
||||
@@ -1321,18 +1260,15 @@ int main(int argc, char** argv) {
|
||||
rocksdb_writebatch_clear(wb);
|
||||
rocksdb_writebatch_put_cf(wb, handles[1], "bar", 3, "b", 1);
|
||||
rocksdb_writebatch_put_cf(wb, handles[1], "box", 3, "c", 1);
|
||||
rocksdb_writebatch_put_cf(wb, handles[1], "buff", 4, "rocksdb", 7);
|
||||
rocksdb_writebatch_delete_cf(wb, handles[1], "bar", 3);
|
||||
rocksdb_write(db, woptions, wb, &err);
|
||||
CheckNoError(err);
|
||||
CheckGetCF(db, roptions, handles[1], "baz", NULL);
|
||||
CheckGetCF(db, roptions, handles[1], "bar", NULL);
|
||||
CheckGetCF(db, roptions, handles[1], "box", "c");
|
||||
CheckGetCF(db, roptions, handles[1], "buff", "rocksdb");
|
||||
CheckPinGetCF(db, roptions, handles[1], "baz", NULL);
|
||||
CheckPinGetCF(db, roptions, handles[1], "bar", NULL);
|
||||
CheckPinGetCF(db, roptions, handles[1], "box", "c");
|
||||
CheckPinGetCF(db, roptions, handles[1], "buff", "rocksdb");
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
|
||||
rocksdb_flush_wal(db, 1, &err);
|
||||
@@ -1363,26 +1299,6 @@ int main(int argc, char** argv) {
|
||||
Free(&vals[i]);
|
||||
}
|
||||
|
||||
{
|
||||
const char* batched_keys[4] = {"box", "buff", "barfooxx", "box"};
|
||||
const size_t batched_keys_sizes[4] = {3, 4, 8, 3};
|
||||
const char* expected_value[4] = {"c", "rocksdb", NULL, "c"};
|
||||
char* batched_errs[4];
|
||||
|
||||
rocksdb_pinnableslice_t* pvals[4];
|
||||
rocksdb_batched_multi_get_cf(db, roptions, handles[1], 4, batched_keys,
|
||||
batched_keys_sizes, pvals, batched_errs,
|
||||
false);
|
||||
const char* val;
|
||||
size_t val_len;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
val = rocksdb_pinnableslice_value(pvals[i], &val_len);
|
||||
CheckNoError(batched_errs[i]);
|
||||
CheckEqual(expected_value[i], val, val_len);
|
||||
rocksdb_pinnableslice_destroy(pvals[i]);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
unsigned char value_found = 0;
|
||||
|
||||
@@ -1414,7 +1330,7 @@ int main(int argc, char** argv) {
|
||||
for (i = 0; rocksdb_iter_valid(iter) != 0; rocksdb_iter_next(iter)) {
|
||||
i++;
|
||||
}
|
||||
CheckCondition(i == 4);
|
||||
CheckCondition(i == 3);
|
||||
rocksdb_iter_get_error(iter, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_iter_destroy(iter);
|
||||
@@ -1438,7 +1354,7 @@ int main(int argc, char** argv) {
|
||||
for (i = 0; rocksdb_iter_valid(iter) != 0; rocksdb_iter_next(iter)) {
|
||||
i++;
|
||||
}
|
||||
CheckCondition(i == 4);
|
||||
CheckCondition(i == 3);
|
||||
rocksdb_iter_get_error(iter, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_iter_destroy(iter);
|
||||
@@ -1857,9 +1773,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(262144 ==
|
||||
rocksdb_options_get_blob_compaction_readahead_size(o));
|
||||
|
||||
rocksdb_options_set_blob_file_starting_level(o, 5);
|
||||
CheckCondition(5 == rocksdb_options_get_blob_file_starting_level(o));
|
||||
|
||||
// Create a copy that should be equal to the original.
|
||||
rocksdb_options_t* copy;
|
||||
copy = rocksdb_options_create_copy(o);
|
||||
@@ -2603,9 +2516,6 @@ int main(int argc, char** argv) {
|
||||
200 ==
|
||||
rocksdb_options_get_compression_options_max_dict_buffer_bytes(co));
|
||||
|
||||
rocksdb_options_set_compression_options_use_zstd_dict_trainer(co, 0);
|
||||
CheckCondition(
|
||||
0 == rocksdb_options_get_compression_options_use_zstd_dict_trainer(co));
|
||||
rocksdb_options_destroy(co);
|
||||
}
|
||||
|
||||
@@ -2687,13 +2597,11 @@ int main(int argc, char** argv) {
|
||||
rocksdb_transactiondb_put(txn_db, woptions, "foo", 3, "hello", 5, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", "hello");
|
||||
CheckTxnDBPinGet(txn_db, roptions, "foo", "hello");
|
||||
|
||||
// delete from outside transaction
|
||||
rocksdb_transactiondb_delete(txn_db, woptions, "foo", 3, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", NULL);
|
||||
CheckTxnDBPinGet(txn_db, roptions, "foo", NULL);
|
||||
|
||||
// write batch into TransactionDB
|
||||
rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
|
||||
@@ -2705,63 +2613,24 @@ int main(int argc, char** argv) {
|
||||
rocksdb_transactiondb_write(txn_db, woptions, wb, &err);
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
CheckTxnDBGet(txn_db, roptions, "box", "c");
|
||||
CheckTxnDBPinGet(txn_db, roptions, "box", "c");
|
||||
CheckNoError(err);
|
||||
|
||||
// multi get
|
||||
{
|
||||
const char* keys[3] = {"box", "foo", "notfound"};
|
||||
const size_t keys_sizes[3] = {3, 3, 8};
|
||||
char* vals[3];
|
||||
size_t vals_sizes[3];
|
||||
char* errs[3];
|
||||
const char* expected[3] = {"c", NULL, NULL};
|
||||
rocksdb_transactiondb_multi_get(txn_db, roptions, 3, keys, keys_sizes,
|
||||
vals, vals_sizes, errs);
|
||||
CheckMultiGetValues(3, vals, vals_sizes, errs, expected);
|
||||
}
|
||||
|
||||
// begin a transaction
|
||||
txn = rocksdb_transaction_begin(txn_db, woptions, txn_options, NULL);
|
||||
// put
|
||||
rocksdb_transaction_put(txn, "foo", 3, "hello", 5, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnGet(txn, roptions, "foo", "hello");
|
||||
CheckTxnPinGet(txn, roptions, "foo", "hello");
|
||||
{
|
||||
const char* keys[3] = {"box", "foo", "notfound"};
|
||||
const size_t keys_sizes[3] = {3, 3, 8};
|
||||
char* vals[3];
|
||||
size_t vals_sizes[3];
|
||||
char* errs[3];
|
||||
const char* expected[3] = {"c", "hello", NULL};
|
||||
rocksdb_transaction_multi_get(txn, roptions, 3, keys, keys_sizes, vals,
|
||||
vals_sizes, errs);
|
||||
CheckMultiGetValues(3, vals, vals_sizes, errs, expected);
|
||||
}
|
||||
// delete
|
||||
rocksdb_transaction_delete(txn, "foo", 3, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnGet(txn, roptions, "foo", NULL);
|
||||
CheckTxnPinGet(txn, roptions, "foo", NULL);
|
||||
|
||||
rocksdb_transaction_put(txn, "foo", 3, "hello", 5, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// read from outside transaction, before commit
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", NULL);
|
||||
CheckTxnDBPinGet(txn_db, roptions, "foo", NULL);
|
||||
{
|
||||
const char* keys[3] = {"box", "foo", "notfound"};
|
||||
const size_t keys_sizes[3] = {3, 3, 8};
|
||||
char* vals[3];
|
||||
size_t vals_sizes[3];
|
||||
char* errs[3];
|
||||
const char* expected[3] = {"c", NULL, NULL};
|
||||
rocksdb_transactiondb_multi_get(txn_db, roptions, 3, keys, keys_sizes,
|
||||
vals, vals_sizes, errs);
|
||||
CheckMultiGetValues(3, vals, vals_sizes, errs, expected);
|
||||
}
|
||||
|
||||
// commit
|
||||
rocksdb_transaction_commit(txn, &err);
|
||||
@@ -2769,18 +2638,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
// read from outside transaction, after commit
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", "hello");
|
||||
CheckTxnDBPinGet(txn_db, roptions, "foo", "hello");
|
||||
{
|
||||
const char* keys[3] = {"box", "foo", "notfound"};
|
||||
const size_t keys_sizes[3] = {3, 3, 8};
|
||||
char* vals[3];
|
||||
size_t vals_sizes[3];
|
||||
char* errs[3];
|
||||
const char* expected[3] = {"c", "hello", NULL};
|
||||
rocksdb_transactiondb_multi_get(txn_db, roptions, 3, keys, keys_sizes,
|
||||
vals, vals_sizes, errs);
|
||||
CheckMultiGetValues(3, vals, vals_sizes, errs, expected);
|
||||
}
|
||||
|
||||
// reuse old transaction
|
||||
txn = rocksdb_transaction_begin(txn_db, woptions, txn_options, txn);
|
||||
@@ -2794,11 +2651,9 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", "hello");
|
||||
CheckTxnDBPinGet(txn_db, roptions, "foo", "hello");
|
||||
rocksdb_readoptions_set_snapshot(roptions, NULL);
|
||||
rocksdb_transactiondb_release_snapshot(txn_db, snapshot);
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", "hey");
|
||||
CheckTxnDBPinGet(txn_db, roptions, "foo", "hey");
|
||||
|
||||
// iterate
|
||||
rocksdb_transaction_put(txn, "bar", 3, "hi", 2, &err);
|
||||
@@ -2815,34 +2670,25 @@ int main(int argc, char** argv) {
|
||||
rocksdb_transaction_rollback(txn, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGet(txn_db, roptions, "bar", NULL);
|
||||
CheckTxnDBPinGet(txn_db, roptions, "bar", NULL);
|
||||
|
||||
// save point
|
||||
rocksdb_transaction_put(txn, "foo1", 4, "hi1", 3, &err);
|
||||
rocksdb_transaction_set_savepoint(txn);
|
||||
CheckTxnGet(txn, roptions, "foo1", "hi1");
|
||||
CheckTxnPinGet(txn, roptions, "foo1", "hi1");
|
||||
rocksdb_transaction_put(txn, "foo2", 4, "hi2", 3, &err);
|
||||
CheckTxnGet(txn, roptions, "foo2", "hi2");
|
||||
CheckTxnPinGet(txn, roptions, "foo2", "hi2");
|
||||
|
||||
// rollback to savepoint
|
||||
rocksdb_transaction_rollback_to_savepoint(txn, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnGet(txn, roptions, "foo2", NULL);
|
||||
CheckTxnGet(txn, roptions, "foo1", "hi1");
|
||||
CheckTxnPinGet(txn, roptions, "foo2", NULL);
|
||||
CheckTxnPinGet(txn, roptions, "foo1", "hi1");
|
||||
CheckTxnDBGet(txn_db, roptions, "foo1", NULL);
|
||||
CheckTxnDBGet(txn_db, roptions, "foo2", NULL);
|
||||
CheckTxnDBPinGet(txn_db, roptions, "foo1", NULL);
|
||||
CheckTxnDBPinGet(txn_db, roptions, "foo2", NULL);
|
||||
rocksdb_transaction_commit(txn, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGet(txn_db, roptions, "foo1", "hi1");
|
||||
CheckTxnDBGet(txn_db, roptions, "foo2", NULL);
|
||||
CheckTxnDBPinGet(txn_db, roptions, "foo1", "hi1");
|
||||
CheckTxnDBPinGet(txn_db, roptions, "foo2", NULL);
|
||||
|
||||
// Column families.
|
||||
rocksdb_column_family_handle_t* cfh;
|
||||
@@ -2854,130 +2700,15 @@ int main(int argc, char** argv) {
|
||||
8, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGetCF(txn_db, roptions, cfh, "cf_foo", "cf_hello");
|
||||
CheckTxnDBPinGetCF(txn_db, roptions, cfh, "cf_foo", "cf_hello");
|
||||
{
|
||||
const rocksdb_column_family_handle_t* get_handles[2] = {cfh, cfh};
|
||||
const char* keys[2] = {"cf_foo", "notfound"};
|
||||
const size_t keys_sizes[2] = {6, 8};
|
||||
char* vals[2];
|
||||
size_t vals_sizes[2];
|
||||
char* errs[2];
|
||||
const char* expected[2] = {"cf_hello", NULL};
|
||||
rocksdb_transactiondb_multi_get_cf(txn_db, roptions, get_handles, 2, keys,
|
||||
keys_sizes, vals, vals_sizes, errs);
|
||||
CheckMultiGetValues(2, vals, vals_sizes, errs, expected);
|
||||
}
|
||||
|
||||
rocksdb_transactiondb_delete_cf(txn_db, woptions, cfh, "cf_foo", 6, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGetCF(txn_db, roptions, cfh, "cf_foo", NULL);
|
||||
CheckTxnDBPinGetCF(txn_db, roptions, cfh, "cf_foo", NULL);
|
||||
|
||||
// flush
|
||||
rocksdb_flushoptions_t* flush_options = rocksdb_flushoptions_create();
|
||||
rocksdb_flushoptions_set_wait(flush_options, 1);
|
||||
rocksdb_transactiondb_flush_wal(txn_db, 1, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transactiondb_flush_cf(txn_db, flush_options, cfh, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transactiondb_flush(txn_db, flush_options, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_flushoptions_destroy(flush_options);
|
||||
|
||||
// close and destroy
|
||||
rocksdb_column_family_handle_destroy(cfh);
|
||||
rocksdb_transaction_destroy(txn);
|
||||
rocksdb_transactiondb_close(txn_db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_options_destroy(txn_options);
|
||||
rocksdb_transactiondb_options_destroy(txn_db_options);
|
||||
}
|
||||
|
||||
StartPhase("two-phase commit");
|
||||
{
|
||||
// open a TransactionDB
|
||||
txn_db_options = rocksdb_transactiondb_options_create();
|
||||
txn_options = rocksdb_transaction_options_create();
|
||||
rocksdb_options_set_create_if_missing(options, 1);
|
||||
txn_db = rocksdb_transactiondb_open(options, txn_db_options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_transaction_options_set_skip_prepare(txn_options, 0);
|
||||
txn = rocksdb_transaction_begin(txn_db, woptions, txn_options, NULL);
|
||||
rocksdb_transaction_commit(txn, &err);
|
||||
CheckCondition(err != NULL);
|
||||
Free(&err);
|
||||
err = NULL;
|
||||
rocksdb_transaction_prepare(txn, &err);
|
||||
CheckCondition(err != NULL);
|
||||
Free(&err);
|
||||
err = NULL;
|
||||
rocksdb_transaction_set_name(txn, "txn1", 4, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_prepare(txn, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_commit(txn, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_destroy(txn);
|
||||
|
||||
// prepare 2 transactions and close db.
|
||||
rocksdb_transaction_t* txn1 =
|
||||
rocksdb_transaction_begin(txn_db, woptions, txn_options, NULL);
|
||||
rocksdb_transaction_put(txn1, "bar1", 4, "1", 1, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_set_name(txn1, "txn1", 4, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_prepare(txn1, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_t* txn2 =
|
||||
rocksdb_transaction_begin(txn_db, woptions, txn_options, NULL);
|
||||
rocksdb_transaction_put(txn2, "bar2", 4, "2", 1, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_set_name(txn2, "txn2", 4, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_prepare(txn2, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_destroy(txn1);
|
||||
rocksdb_transaction_destroy(txn2);
|
||||
rocksdb_transactiondb_close(txn_db);
|
||||
rocksdb_transaction_options_destroy(txn_options);
|
||||
rocksdb_transactiondb_options_destroy(txn_db_options);
|
||||
|
||||
// reopen db and get all prepared.
|
||||
txn_db_options = rocksdb_transactiondb_options_create();
|
||||
txn_options = rocksdb_transaction_options_create();
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
txn_db = rocksdb_transactiondb_open(options, txn_db_options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnDBPinGet(txn_db, roptions, "bar1", NULL);
|
||||
CheckTxnDBPinGet(txn_db, roptions, "bar2", NULL);
|
||||
size_t cnt;
|
||||
rocksdb_transaction_t** txns =
|
||||
rocksdb_transactiondb_get_prepared_transactions(txn_db, &cnt);
|
||||
CheckCondition(cnt == 2);
|
||||
size_t i;
|
||||
for (i = 0; i < cnt; i++) {
|
||||
txn = txns[i];
|
||||
size_t name_len = 0;
|
||||
char* name = rocksdb_transaction_get_name(txn, &name_len);
|
||||
CheckCondition(name_len == 4);
|
||||
if (strncmp(name, "txn1", name_len) == 0) {
|
||||
rocksdb_transaction_commit(txn, &err);
|
||||
} else if (strncmp(name, "txn2", name_len) == 0) {
|
||||
rocksdb_transaction_rollback(txn, &err);
|
||||
}
|
||||
rocksdb_free(name);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_destroy(txn);
|
||||
}
|
||||
rocksdb_free(txns);
|
||||
CheckTxnDBGet(txn_db, roptions, "bar1", "1");
|
||||
CheckTxnDBGet(txn_db, roptions, "bar2", NULL);
|
||||
rocksdb_transactiondb_put(txn_db, woptions, "bar2", 4, "2", 1, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// close and destroy
|
||||
rocksdb_transaction_destroy(txn);
|
||||
rocksdb_transactiondb_close(txn_db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
@@ -3001,7 +2732,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_transaction_put(txn2, "key1", 4, "value1", 6, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnGet(txn1, roptions, "key", "value");
|
||||
CheckTxnPinGet(txn1, roptions, "key", "value");
|
||||
rocksdb_transaction_commit(txn1, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_commit(txn2, &err);
|
||||
@@ -3028,19 +2758,6 @@ int main(int argc, char** argv) {
|
||||
txn);
|
||||
CheckGetCF(db, roptions, cfh1, "key_cf1", "val_cf1");
|
||||
CheckTxnGetCF(txn, roptions, cfh1, "key_cf1", "val_cf1");
|
||||
CheckTxnPinGetCF(txn, roptions, cfh1, "key_cf1", "val_cf1");
|
||||
{
|
||||
const rocksdb_column_family_handle_t* get_handles[3] = {cfh1, cfh2, cfh2};
|
||||
const char* keys[3] = {"key_cf1", "key_cf2", "notfound"};
|
||||
const size_t keys_sizes[3] = {7, 7, 8};
|
||||
char* vals[3];
|
||||
size_t vals_sizes[3];
|
||||
char* errs[3];
|
||||
const char* expected[3] = {"val_cf1", "val_cf2", NULL};
|
||||
rocksdb_transaction_multi_get_cf(txn, roptions, get_handles, 3, keys,
|
||||
keys_sizes, vals, vals_sizes, errs);
|
||||
CheckMultiGetValues(3, vals, vals_sizes, errs, expected);
|
||||
}
|
||||
|
||||
// Check iterator with column family
|
||||
rocksdb_transaction_put_cf(txn, cfh1, "key1_cf", 7, "val1_cf", 7, &err);
|
||||
@@ -3086,9 +2803,6 @@ int main(int argc, char** argv) {
|
||||
CheckTxnGetCF(txn_cf, roptions, cf_handles[0], "key", "value");
|
||||
CheckTxnGetCF(txn_cf, roptions, cf_handles[1], "key_cf1", "val_cf1");
|
||||
CheckTxnGetCF(txn_cf, roptions, cf_handles[2], "key_cf2", "val_cf2");
|
||||
CheckTxnPinGetCF(txn_cf, roptions, cf_handles[0], "key", "value");
|
||||
CheckTxnPinGetCF(txn_cf, roptions, cf_handles[1], "key_cf1", "val_cf1");
|
||||
CheckTxnPinGetCF(txn_cf, roptions, cf_handles[2], "key_cf2", "val_cf2");
|
||||
rocksdb_transaction_destroy(txn_cf);
|
||||
rocksdb_options_destroy(cf_options);
|
||||
rocksdb_column_family_handle_destroy(cf_handles[0]);
|
||||
|
||||
+5
-32
@@ -136,15 +136,9 @@ Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options) {
|
||||
}
|
||||
}
|
||||
if (cf_options.compression_opts.zstd_max_train_bytes > 0) {
|
||||
if (cf_options.compression_opts.use_zstd_dict_trainer) {
|
||||
if (!ZSTD_TrainDictionarySupported()) {
|
||||
return Status::InvalidArgument(
|
||||
"zstd dictionary trainer cannot be used because ZSTD 1.1.3+ "
|
||||
"is not linked with the binary.");
|
||||
}
|
||||
} else if (!ZSTD_FinalizeDictionarySupported()) {
|
||||
if (!ZSTD_TrainDictionarySupported()) {
|
||||
return Status::InvalidArgument(
|
||||
"zstd finalizeDictionary cannot be used because ZSTD 1.4.5+ "
|
||||
"zstd dictionary trainer cannot be used because ZSTD 1.1.3+ "
|
||||
"is not linked with the binary.");
|
||||
}
|
||||
if (cf_options.compression_opts.max_dict_bytes == 0) {
|
||||
@@ -507,8 +501,7 @@ std::vector<std::string> ColumnFamilyData::GetDbPaths() const {
|
||||
return paths;
|
||||
}
|
||||
|
||||
const uint32_t ColumnFamilyData::kDummyColumnFamilyDataId =
|
||||
std::numeric_limits<uint32_t>::max();
|
||||
const uint32_t ColumnFamilyData::kDummyColumnFamilyDataId = port::kMaxUint32;
|
||||
|
||||
ColumnFamilyData::ColumnFamilyData(
|
||||
uint32_t id, const std::string& name, Version* _dummy_versions,
|
||||
@@ -619,26 +612,6 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
}
|
||||
|
||||
RecalculateWriteStallConditions(mutable_cf_options_);
|
||||
|
||||
if (cf_options.table_factory->IsInstanceOf(
|
||||
TableFactory::kBlockBasedTableName()) &&
|
||||
cf_options.table_factory->GetOptions<BlockBasedTableOptions>()) {
|
||||
const BlockBasedTableOptions* bbto =
|
||||
cf_options.table_factory->GetOptions<BlockBasedTableOptions>();
|
||||
const auto& options_overrides = bbto->cache_usage_options.options_overrides;
|
||||
const auto file_metadata_charged =
|
||||
options_overrides.at(CacheEntryRole::kFileMetadata).charged;
|
||||
if (bbto->block_cache &&
|
||||
file_metadata_charged == CacheEntryRoleOptions::Decision::kEnabled) {
|
||||
// TODO(hx235): Add a `ConcurrentCacheReservationManager` at DB scope
|
||||
// responsible for reservation of `ObsoleteFileInfo` so that we can keep
|
||||
// this `file_metadata_cache_res_mgr_` nonconcurrent
|
||||
file_metadata_cache_res_mgr_.reset(new ConcurrentCacheReservationManager(
|
||||
std::make_shared<
|
||||
CacheReservationManagerImpl<CacheEntryRole::kFileMetadata>>(
|
||||
bbto->block_cache)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DB mutex held
|
||||
@@ -853,8 +826,8 @@ int GetL0ThresholdSpeedupCompaction(int level0_file_num_compaction_trigger,
|
||||
// condition.
|
||||
// Or twice as compaction trigger, if it is smaller.
|
||||
int64_t res = std::min(twice_level0_trigger, one_fourth_trigger_slowdown);
|
||||
if (res >= std::numeric_limits<int32_t>::max()) {
|
||||
return std::numeric_limits<int32_t>::max();
|
||||
if (res >= port::kMaxInt32) {
|
||||
return port::kMaxInt32;
|
||||
} else {
|
||||
// res fits in int
|
||||
return static_cast<int>(res);
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "cache/cache_reservation_manager.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
@@ -521,10 +520,6 @@ class ColumnFamilyData {
|
||||
|
||||
ThreadLocalPtr* TEST_GetLocalSV() { return local_sv_.get(); }
|
||||
WriteBufferManager* write_buffer_mgr() { return write_buffer_manager_; }
|
||||
std::shared_ptr<CacheReservationManager>
|
||||
GetFileMetadataCacheReservationManager() {
|
||||
return file_metadata_cache_res_mgr_;
|
||||
}
|
||||
|
||||
static const uint32_t kDummyColumnFamilyDataId;
|
||||
|
||||
@@ -623,10 +618,6 @@ class ColumnFamilyData {
|
||||
bool db_paths_registered_;
|
||||
|
||||
std::string full_history_ts_low_;
|
||||
|
||||
// For charging memory usage of file metadata created for newly added files to
|
||||
// a Version associated with this CFD
|
||||
std::shared_ptr<CacheReservationManager> file_metadata_cache_res_mgr_;
|
||||
};
|
||||
|
||||
// ColumnFamilySet has interesting thread-safety requirements
|
||||
|
||||
+58
-47
@@ -383,7 +383,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
|
||||
int NumTableFilesAtLevel(int level, int cf) {
|
||||
return GetProperty(cf,
|
||||
"rocksdb.num-files-at-level" + std::to_string(level));
|
||||
"rocksdb.num-files-at-level" + ToString(level));
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -783,7 +783,7 @@ TEST_P(ColumnFamilyTest, BulkAddDrop) {
|
||||
std::vector<std::string> cf_names;
|
||||
std::vector<ColumnFamilyHandle*> cf_handles;
|
||||
for (int i = 1; i <= kNumCF; i++) {
|
||||
cf_names.push_back("cf1-" + std::to_string(i));
|
||||
cf_names.push_back("cf1-" + ToString(i));
|
||||
}
|
||||
ASSERT_OK(db_->CreateColumnFamilies(cf_options, cf_names, &cf_handles));
|
||||
for (int i = 1; i <= kNumCF; i++) {
|
||||
@@ -796,8 +796,7 @@ TEST_P(ColumnFamilyTest, BulkAddDrop) {
|
||||
}
|
||||
cf_handles.clear();
|
||||
for (int i = 1; i <= kNumCF; i++) {
|
||||
cf_descriptors.emplace_back("cf2-" + std::to_string(i),
|
||||
ColumnFamilyOptions());
|
||||
cf_descriptors.emplace_back("cf2-" + ToString(i), ColumnFamilyOptions());
|
||||
}
|
||||
ASSERT_OK(db_->CreateColumnFamilies(cf_descriptors, &cf_handles));
|
||||
for (int i = 1; i <= kNumCF; i++) {
|
||||
@@ -821,7 +820,7 @@ TEST_P(ColumnFamilyTest, DropTest) {
|
||||
Open({"default"});
|
||||
CreateColumnFamiliesAndReopen({"pikachu"});
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
ASSERT_OK(Put(1, std::to_string(i), "bar" + std::to_string(i)));
|
||||
ASSERT_OK(Put(1, ToString(i), "bar" + ToString(i)));
|
||||
}
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
@@ -1345,7 +1344,7 @@ TEST_P(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
PutRandomData(1, 10, 12000);
|
||||
PutRandomData(1, 1, 10);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(std::to_string(i + 1), 1);
|
||||
AssertFilesPerLevel(ToString(i + 1), 1);
|
||||
}
|
||||
|
||||
// SETUP column family "two" -- level style with 4 levels
|
||||
@@ -1353,7 +1352,7 @@ TEST_P(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
PutRandomData(2, 10, 12000);
|
||||
PutRandomData(2, 1, 10);
|
||||
WaitForFlush(2);
|
||||
AssertFilesPerLevel(std::to_string(i + 1), 2);
|
||||
AssertFilesPerLevel(ToString(i + 1), 2);
|
||||
}
|
||||
|
||||
// TRIGGER compaction "one"
|
||||
@@ -1417,17 +1416,18 @@ TEST_P(ColumnFamilyTest, MultipleManualCompactions) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(std::to_string(i + 1), 1);
|
||||
AssertFilesPerLevel(ToString(i + 1), 1);
|
||||
}
|
||||
std::atomic_bool cf_1_1{true};
|
||||
bool cf_1_1 = true;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ColumnFamilyTest::MultiManual:4", "ColumnFamilyTest::MultiManual:1"},
|
||||
{"ColumnFamilyTest::MultiManual:2", "ColumnFamilyTest::MultiManual:5"},
|
||||
{"ColumnFamilyTest::MultiManual:2", "ColumnFamilyTest::MultiManual:3"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1.exchange(false)) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::MultiManual:4");
|
||||
cf_1_1 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::MultiManual:3");
|
||||
}
|
||||
});
|
||||
@@ -1446,7 +1446,7 @@ TEST_P(ColumnFamilyTest, MultipleManualCompactions) {
|
||||
PutRandomData(2, 10, 12000);
|
||||
PutRandomData(2, 1, 10);
|
||||
WaitForFlush(2);
|
||||
AssertFilesPerLevel(std::to_string(i + 1), 2);
|
||||
AssertFilesPerLevel(ToString(i + 1), 2);
|
||||
}
|
||||
threads.emplace_back([&] {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::MultiManual:1");
|
||||
@@ -1514,14 +1514,15 @@ TEST_P(ColumnFamilyTest, AutomaticAndManualCompactions) {
|
||||
auto stop_token =
|
||||
dbfull()->TEST_write_controler().GetCompactionPressureToken();
|
||||
|
||||
std::atomic_bool cf_1_1{true};
|
||||
bool cf_1_1 = true;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ColumnFamilyTest::AutoManual:4", "ColumnFamilyTest::AutoManual:1"},
|
||||
{"ColumnFamilyTest::AutoManual:2", "ColumnFamilyTest::AutoManual:5"},
|
||||
{"ColumnFamilyTest::AutoManual:2", "ColumnFamilyTest::AutoManual:3"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1.exchange(false)) {
|
||||
if (cf_1_1) {
|
||||
cf_1_1 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::AutoManual:4");
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::AutoManual:3");
|
||||
}
|
||||
@@ -1532,7 +1533,7 @@ TEST_P(ColumnFamilyTest, AutomaticAndManualCompactions) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(std::to_string(i + 1), 1);
|
||||
AssertFilesPerLevel(ToString(i + 1), 1);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::AutoManual:1");
|
||||
@@ -1542,7 +1543,7 @@ TEST_P(ColumnFamilyTest, AutomaticAndManualCompactions) {
|
||||
PutRandomData(2, 10, 12000);
|
||||
PutRandomData(2, 1, 10);
|
||||
WaitForFlush(2);
|
||||
AssertFilesPerLevel(std::to_string(i + 1), 2);
|
||||
AssertFilesPerLevel(ToString(i + 1), 2);
|
||||
}
|
||||
ROCKSDB_NAMESPACE::port::Thread threads([&] {
|
||||
CompactRangeOptions compact_options;
|
||||
@@ -1614,21 +1615,23 @@ TEST_P(ColumnFamilyTest, ManualAndAutomaticCompactions) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(std::to_string(i + 1), 1);
|
||||
AssertFilesPerLevel(ToString(i + 1), 1);
|
||||
}
|
||||
std::atomic_bool cf_1_1{true};
|
||||
std::atomic_bool cf_1_2{true};
|
||||
bool cf_1_1 = true;
|
||||
bool cf_1_2 = true;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ColumnFamilyTest::ManualAuto:4", "ColumnFamilyTest::ManualAuto:1"},
|
||||
{"ColumnFamilyTest::ManualAuto:5", "ColumnFamilyTest::ManualAuto:2"},
|
||||
{"ColumnFamilyTest::ManualAuto:2", "ColumnFamilyTest::ManualAuto:3"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1.exchange(false)) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:4");
|
||||
cf_1_1 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:3");
|
||||
} else if (cf_1_2.exchange(false)) {
|
||||
} else if (cf_1_2) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:2");
|
||||
cf_1_2 = false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1647,7 +1650,7 @@ TEST_P(ColumnFamilyTest, ManualAndAutomaticCompactions) {
|
||||
PutRandomData(2, 10, 12000);
|
||||
PutRandomData(2, 1, 10);
|
||||
WaitForFlush(2);
|
||||
AssertFilesPerLevel(std::to_string(i + 1), 2);
|
||||
AssertFilesPerLevel(ToString(i + 1), 2);
|
||||
}
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:5");
|
||||
threads.join();
|
||||
@@ -1706,10 +1709,10 @@ TEST_P(ColumnFamilyTest, SameCFManualManualCompactions) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(std::to_string(i + 1), 1);
|
||||
AssertFilesPerLevel(ToString(i + 1), 1);
|
||||
}
|
||||
std::atomic_bool cf_1_1{true};
|
||||
std::atomic_bool cf_1_2{true};
|
||||
bool cf_1_1 = true;
|
||||
bool cf_1_2 = true;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ColumnFamilyTest::ManualManual:4", "ColumnFamilyTest::ManualManual:2"},
|
||||
{"ColumnFamilyTest::ManualManual:4", "ColumnFamilyTest::ManualManual:5"},
|
||||
@@ -1718,11 +1721,13 @@ TEST_P(ColumnFamilyTest, SameCFManualManualCompactions) {
|
||||
"ColumnFamilyTest::ManualManual:3"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1.exchange(false)) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualManual:4");
|
||||
cf_1_1 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualManual:3");
|
||||
} else if (cf_1_2.exchange(false)) {
|
||||
} else if (cf_1_2) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualManual:2");
|
||||
cf_1_2 = false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1743,8 +1748,8 @@ TEST_P(ColumnFamilyTest, SameCFManualManualCompactions) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(
|
||||
std::to_string(one.level0_file_num_compaction_trigger + i), 1);
|
||||
AssertFilesPerLevel(ToString(one.level0_file_num_compaction_trigger + i),
|
||||
1);
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::port::Thread threads1([&] {
|
||||
@@ -1806,10 +1811,10 @@ TEST_P(ColumnFamilyTest, SameCFManualAutomaticCompactions) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(std::to_string(i + 1), 1);
|
||||
AssertFilesPerLevel(ToString(i + 1), 1);
|
||||
}
|
||||
std::atomic_bool cf_1_1{true};
|
||||
std::atomic_bool cf_1_2{true};
|
||||
bool cf_1_1 = true;
|
||||
bool cf_1_2 = true;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ColumnFamilyTest::ManualAuto:4", "ColumnFamilyTest::ManualAuto:2"},
|
||||
{"ColumnFamilyTest::ManualAuto:4", "ColumnFamilyTest::ManualAuto:5"},
|
||||
@@ -1817,11 +1822,13 @@ TEST_P(ColumnFamilyTest, SameCFManualAutomaticCompactions) {
|
||||
{"ColumnFamilyTest::ManualAuto:1", "ColumnFamilyTest::ManualAuto:3"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1.exchange(false)) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:4");
|
||||
cf_1_1 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:3");
|
||||
} else if (cf_1_2.exchange(false)) {
|
||||
} else if (cf_1_2) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:2");
|
||||
cf_1_2 = false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1842,8 +1849,8 @@ TEST_P(ColumnFamilyTest, SameCFManualAutomaticCompactions) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(
|
||||
std::to_string(one.level0_file_num_compaction_trigger + i), 1);
|
||||
AssertFilesPerLevel(ToString(one.level0_file_num_compaction_trigger + i),
|
||||
1);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:1");
|
||||
@@ -1897,10 +1904,10 @@ TEST_P(ColumnFamilyTest, SameCFManualAutomaticCompactionsLevel) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(std::to_string(i + 1), 1);
|
||||
AssertFilesPerLevel(ToString(i + 1), 1);
|
||||
}
|
||||
std::atomic_bool cf_1_1{true};
|
||||
std::atomic_bool cf_1_2{true};
|
||||
bool cf_1_1 = true;
|
||||
bool cf_1_2 = true;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ColumnFamilyTest::ManualAuto:4", "ColumnFamilyTest::ManualAuto:2"},
|
||||
{"ColumnFamilyTest::ManualAuto:4", "ColumnFamilyTest::ManualAuto:5"},
|
||||
@@ -1910,11 +1917,13 @@ TEST_P(ColumnFamilyTest, SameCFManualAutomaticCompactionsLevel) {
|
||||
{"ColumnFamilyTest::ManualAuto:1", "ColumnFamilyTest::ManualAuto:3"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1.exchange(false)) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:4");
|
||||
cf_1_1 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:3");
|
||||
} else if (cf_1_2.exchange(false)) {
|
||||
} else if (cf_1_2) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:2");
|
||||
cf_1_2 = false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1933,8 +1942,8 @@ TEST_P(ColumnFamilyTest, SameCFManualAutomaticCompactionsLevel) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(
|
||||
std::to_string(one.level0_file_num_compaction_trigger + i), 1);
|
||||
AssertFilesPerLevel(ToString(one.level0_file_num_compaction_trigger + i),
|
||||
1);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:1");
|
||||
@@ -1989,8 +1998,8 @@ TEST_P(ColumnFamilyTest, SameCFAutomaticManualCompactions) {
|
||||
auto stop_token =
|
||||
dbfull()->TEST_write_controler().GetCompactionPressureToken();
|
||||
|
||||
std::atomic_bool cf_1_1{true};
|
||||
std::atomic_bool cf_1_2{true};
|
||||
bool cf_1_1 = true;
|
||||
bool cf_1_2 = true;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ColumnFamilyTest::AutoManual:4", "ColumnFamilyTest::AutoManual:2"},
|
||||
{"ColumnFamilyTest::AutoManual:4", "ColumnFamilyTest::AutoManual:5"},
|
||||
@@ -1998,11 +2007,13 @@ TEST_P(ColumnFamilyTest, SameCFAutomaticManualCompactions) {
|
||||
"ColumnFamilyTest::AutoManual:3"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1.exchange(false)) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::AutoManual:4");
|
||||
cf_1_1 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::AutoManual:3");
|
||||
} else if (cf_1_2.exchange(false)) {
|
||||
} else if (cf_1_2) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::AutoManual:2");
|
||||
cf_1_2 = false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2013,7 +2024,7 @@ TEST_P(ColumnFamilyTest, SameCFAutomaticManualCompactions) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(std::to_string(i + 1), 1);
|
||||
AssertFilesPerLevel(ToString(i + 1), 1);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::AutoManual:5");
|
||||
|
||||
+11
-11
@@ -91,8 +91,8 @@ TEST_F(CompactFilesTest, L0ConflictsFiles) {
|
||||
// create couple files
|
||||
// Background compaction starts and waits in BackgroundCallCompaction:0
|
||||
for (int i = 0; i < kLevel0Trigger * 4; ++i) {
|
||||
ASSERT_OK(db->Put(WriteOptions(), std::to_string(i), ""));
|
||||
ASSERT_OK(db->Put(WriteOptions(), std::to_string(100 - i), ""));
|
||||
ASSERT_OK(db->Put(WriteOptions(), ToString(i), ""));
|
||||
ASSERT_OK(db->Put(WriteOptions(), ToString(100 - i), ""));
|
||||
ASSERT_OK(db->Flush(FlushOptions()));
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ TEST_F(CompactFilesTest, MultipleLevel) {
|
||||
// create couple files in L0, L3, L4 and L5
|
||||
for (int i = 5; i > 2; --i) {
|
||||
collector->ClearFlushedFiles();
|
||||
ASSERT_OK(db->Put(WriteOptions(), std::to_string(i), ""));
|
||||
ASSERT_OK(db->Put(WriteOptions(), ToString(i), ""));
|
||||
ASSERT_OK(db->Flush(FlushOptions()));
|
||||
// Ensure background work is fully finished including listener callbacks
|
||||
// before accessing listener state.
|
||||
@@ -145,11 +145,11 @@ TEST_F(CompactFilesTest, MultipleLevel) {
|
||||
ASSERT_OK(db->CompactFiles(CompactionOptions(), l0_files, i));
|
||||
|
||||
std::string prop;
|
||||
ASSERT_TRUE(db->GetProperty(
|
||||
"rocksdb.num-files-at-level" + std::to_string(i), &prop));
|
||||
ASSERT_TRUE(
|
||||
db->GetProperty("rocksdb.num-files-at-level" + ToString(i), &prop));
|
||||
ASSERT_EQ("1", prop);
|
||||
}
|
||||
ASSERT_OK(db->Put(WriteOptions(), std::to_string(0), ""));
|
||||
ASSERT_OK(db->Put(WriteOptions(), ToString(0), ""));
|
||||
ASSERT_OK(db->Flush(FlushOptions()));
|
||||
|
||||
ColumnFamilyMetaData meta;
|
||||
@@ -218,7 +218,7 @@ TEST_F(CompactFilesTest, ObsoleteFiles) {
|
||||
|
||||
// create couple files
|
||||
for (int i = 1000; i < 2000; ++i) {
|
||||
ASSERT_OK(db->Put(WriteOptions(), std::to_string(i),
|
||||
ASSERT_OK(db->Put(WriteOptions(), ToString(i),
|
||||
std::string(kWriteBufferSize / 10, 'a' + (i % 26))));
|
||||
}
|
||||
|
||||
@@ -257,14 +257,14 @@ TEST_F(CompactFilesTest, NotCutOutputOnLevel0) {
|
||||
|
||||
// create couple files
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
ASSERT_OK(db->Put(WriteOptions(), std::to_string(i),
|
||||
ASSERT_OK(db->Put(WriteOptions(), ToString(i),
|
||||
std::string(1000, 'a' + (i % 26))));
|
||||
}
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db)->TEST_WaitForFlushMemTable());
|
||||
auto l0_files_1 = collector->GetFlushedFiles();
|
||||
collector->ClearFlushedFiles();
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
ASSERT_OK(db->Put(WriteOptions(), std::to_string(i),
|
||||
ASSERT_OK(db->Put(WriteOptions(), ToString(i),
|
||||
std::string(1000, 'a' + (i % 26))));
|
||||
}
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db)->TEST_WaitForFlushMemTable());
|
||||
@@ -295,7 +295,7 @@ TEST_F(CompactFilesTest, CapturingPendingFiles) {
|
||||
|
||||
// Create 5 files.
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
ASSERT_OK(db->Put(WriteOptions(), "key" + std::to_string(i), "value"));
|
||||
ASSERT_OK(db->Put(WriteOptions(), "key" + ToString(i), "value"));
|
||||
ASSERT_OK(db->Flush(FlushOptions()));
|
||||
}
|
||||
|
||||
@@ -465,7 +465,7 @@ TEST_F(CompactFilesTest, GetCompactionJobInfo) {
|
||||
|
||||
// create couple files
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
ASSERT_OK(db->Put(WriteOptions(), std::to_string(i),
|
||||
ASSERT_OK(db->Put(WriteOptions(), ToString(i),
|
||||
std::string(1000, 'a' + (i % 26))));
|
||||
}
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db)->TEST_WaitForFlushMemTable());
|
||||
|
||||
@@ -214,9 +214,7 @@ Compaction::Compaction(
|
||||
CompressionOptions _compression_opts, Temperature _output_temperature,
|
||||
uint32_t _max_subcompactions, std::vector<FileMetaData*> _grandparents,
|
||||
bool _manual_compaction, const std::string& _trim_ts, double _score,
|
||||
bool _deletion_compaction, CompactionReason _compaction_reason,
|
||||
BlobGarbageCollectionPolicy _blob_garbage_collection_policy,
|
||||
double _blob_garbage_collection_age_cutoff)
|
||||
bool _deletion_compaction, CompactionReason _compaction_reason)
|
||||
: input_vstorage_(vstorage),
|
||||
start_level_(_inputs[0].level),
|
||||
output_level_(_output_level),
|
||||
@@ -242,19 +240,7 @@ Compaction::Compaction(
|
||||
trim_ts_(_trim_ts),
|
||||
is_trivial_move_(false),
|
||||
compaction_reason_(_compaction_reason),
|
||||
notify_on_compaction_completion_(false),
|
||||
enable_blob_garbage_collection_(
|
||||
_blob_garbage_collection_policy == BlobGarbageCollectionPolicy::kForce
|
||||
? true
|
||||
: (_blob_garbage_collection_policy ==
|
||||
BlobGarbageCollectionPolicy::kDisable
|
||||
? false
|
||||
: mutable_cf_options()->enable_blob_garbage_collection)),
|
||||
blob_garbage_collection_age_cutoff_(
|
||||
_blob_garbage_collection_age_cutoff < 0 ||
|
||||
_blob_garbage_collection_age_cutoff > 1
|
||||
? mutable_cf_options()->blob_garbage_collection_age_cutoff
|
||||
: _blob_garbage_collection_age_cutoff) {
|
||||
notify_on_compaction_completion_(false) {
|
||||
MarkFilesBeingCompacted(true);
|
||||
if (is_manual_compaction_) {
|
||||
compaction_reason_ = CompactionReason::kManualCompaction;
|
||||
@@ -339,8 +325,8 @@ bool Compaction::IsTrivialMove() const {
|
||||
}
|
||||
|
||||
if (!(start_level_ != output_level_ && num_input_levels() == 1 &&
|
||||
input(0, 0)->fd.GetPathId() == output_path_id() &&
|
||||
InputCompressionMatchesOutput())) {
|
||||
input(0, 0)->fd.GetPathId() == output_path_id() &&
|
||||
InputCompressionMatchesOutput())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -532,7 +518,7 @@ uint64_t Compaction::OutputFilePreallocationSize() const {
|
||||
}
|
||||
}
|
||||
|
||||
if (max_output_file_size_ != std::numeric_limits<uint64_t>::max() &&
|
||||
if (max_output_file_size_ != port::kMaxUint64 &&
|
||||
(immutable_options_.compaction_style == kCompactionStyleLevel ||
|
||||
output_level() > 0)) {
|
||||
preallocation_size = std::min(max_output_file_size_, preallocation_size);
|
||||
@@ -630,7 +616,7 @@ bool Compaction::DoesInputReferenceBlobFiles() const {
|
||||
|
||||
uint64_t Compaction::MinInputFileOldestAncesterTime(
|
||||
const InternalKey* start, const InternalKey* end) const {
|
||||
uint64_t min_oldest_ancester_time = std::numeric_limits<uint64_t>::max();
|
||||
uint64_t min_oldest_ancester_time = port::kMaxUint64;
|
||||
const InternalKeyComparator& icmp =
|
||||
column_family_data()->internal_comparator();
|
||||
for (const auto& level_files : inputs_) {
|
||||
|
||||
@@ -81,10 +81,7 @@ class Compaction {
|
||||
std::vector<FileMetaData*> grandparents,
|
||||
bool manual_compaction = false, const std::string& trim_ts = "",
|
||||
double score = -1, bool deletion_compaction = false,
|
||||
CompactionReason compaction_reason = CompactionReason::kUnknown,
|
||||
BlobGarbageCollectionPolicy blob_garbage_collection_policy =
|
||||
BlobGarbageCollectionPolicy::kUseDefault,
|
||||
double blob_garbage_collection_age_cutoff = -1);
|
||||
CompactionReason compaction_reason = CompactionReason::kUnknown);
|
||||
|
||||
// No copying allowed
|
||||
Compaction(const Compaction&) = delete;
|
||||
@@ -309,14 +306,6 @@ class Compaction {
|
||||
|
||||
uint32_t max_subcompactions() const { return max_subcompactions_; }
|
||||
|
||||
bool enable_blob_garbage_collection() const {
|
||||
return enable_blob_garbage_collection_;
|
||||
}
|
||||
|
||||
double blob_garbage_collection_age_cutoff() const {
|
||||
return blob_garbage_collection_age_cutoff_;
|
||||
}
|
||||
|
||||
// start and end are sub compact range. Null if no boundary.
|
||||
// This is used to filter out some input files' ancester's time range.
|
||||
uint64_t MinInputFileOldestAncesterTime(const InternalKey* start,
|
||||
@@ -343,9 +332,8 @@ class Compaction {
|
||||
|
||||
// Get the atomic file boundaries for all files in the compaction. Necessary
|
||||
// in order to avoid the scenario described in
|
||||
// https://github.com/facebook/rocksdb/pull/4432#discussion_r221072219 and
|
||||
// plumb down appropriate key boundaries to RangeDelAggregator during
|
||||
// compaction.
|
||||
// https://github.com/facebook/rocksdb/pull/4432#discussion_r221072219 and plumb
|
||||
// down appropriate key boundaries to RangeDelAggregator during compaction.
|
||||
static std::vector<CompactionInputFiles> PopulateWithAtomicBoundaries(
|
||||
VersionStorageInfo* vstorage, std::vector<CompactionInputFiles> inputs);
|
||||
|
||||
@@ -360,7 +348,7 @@ class Compaction {
|
||||
|
||||
VersionStorageInfo* input_vstorage_;
|
||||
|
||||
const int start_level_; // the lowest level to be compacted
|
||||
const int start_level_; // the lowest level to be compacted
|
||||
const int output_level_; // levels to which output files are stored
|
||||
uint64_t max_output_file_size_;
|
||||
uint64_t max_compaction_bytes_;
|
||||
@@ -371,7 +359,7 @@ class Compaction {
|
||||
VersionEdit edit_;
|
||||
const int number_levels_;
|
||||
ColumnFamilyData* cfd_;
|
||||
Arena arena_; // Arena used to allocate space for file_levels_
|
||||
Arena arena_; // Arena used to allocate space for file_levels_
|
||||
|
||||
const uint32_t output_path_id_;
|
||||
CompressionType output_compression_;
|
||||
@@ -389,7 +377,7 @@ class Compaction {
|
||||
// State used to check for number of overlapping grandparent files
|
||||
// (grandparent == "output_level_ + 1")
|
||||
std::vector<FileMetaData*> grandparents_;
|
||||
const double score_; // score that was used to pick this compaction.
|
||||
const double score_; // score that was used to pick this compaction.
|
||||
|
||||
// Is this compaction creating a file in the bottom most level?
|
||||
const bool bottommost_level_;
|
||||
@@ -424,12 +412,6 @@ class Compaction {
|
||||
// Notify on compaction completion only if listener was notified on compaction
|
||||
// begin.
|
||||
bool notify_on_compaction_completion_;
|
||||
|
||||
// Enable/disable GC collection for blobs during compaction.
|
||||
bool enable_blob_garbage_collection_;
|
||||
|
||||
// Blob garbage collection age cutoff.
|
||||
double blob_garbage_collection_age_cutoff_;
|
||||
};
|
||||
|
||||
// Return sum of sizes of all files in `files`.
|
||||
|
||||
@@ -28,21 +28,21 @@ CompactionIterator::CompactionIterator(
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
|
||||
bool enforce_single_del_contracts,
|
||||
const std::atomic<bool>& manual_compaction_canceled,
|
||||
const Compaction* compaction, const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::atomic<bool>* manual_compaction_canceled,
|
||||
const 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, job_snapshot, snapshot_checker, env,
|
||||
report_detailed_time, expect_valid_internal_key, range_del_agg,
|
||||
blob_file_builder, allow_data_in_errors, enforce_single_del_contracts,
|
||||
manual_compaction_canceled,
|
||||
blob_file_builder, allow_data_in_errors,
|
||||
std::unique_ptr<CompactionProxy>(
|
||||
compaction ? new RealCompaction(compaction) : nullptr),
|
||||
compaction_filter, shutting_down, 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,
|
||||
@@ -52,11 +52,11 @@ CompactionIterator::CompactionIterator(
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
|
||||
bool enforce_single_del_contracts,
|
||||
const std::atomic<bool>& manual_compaction_canceled,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::atomic<bool>* manual_compaction_canceled,
|
||||
const std::shared_ptr<Logger> info_log,
|
||||
const std::string* full_history_ts_low)
|
||||
: input_(input, cmp,
|
||||
@@ -76,10 +76,10 @@ CompactionIterator::CompactionIterator(
|
||||
compaction_(std::move(compaction)),
|
||||
compaction_filter_(compaction_filter),
|
||||
shutting_down_(shutting_down),
|
||||
manual_compaction_paused_(manual_compaction_paused),
|
||||
manual_compaction_canceled_(manual_compaction_canceled),
|
||||
info_log_(info_log),
|
||||
allow_data_in_errors_(allow_data_in_errors),
|
||||
enforce_single_del_contracts_(enforce_single_del_contracts),
|
||||
timestamp_size_(cmp_ ? cmp_->timestamp_size() : 0),
|
||||
full_history_ts_low_(full_history_ts_low),
|
||||
current_user_key_sequence_(0),
|
||||
@@ -157,12 +157,10 @@ void CompactionIterator::Next() {
|
||||
Status s = ParseInternalKey(key_, &ikey_, allow_data_in_errors_);
|
||||
// MergeUntil stops when it encounters a corrupt key and does not
|
||||
// include them in the result, so we expect the keys here to be valid.
|
||||
assert(s.ok());
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_FATAL(
|
||||
info_log_, "Invalid ikey %s in compaction. %s",
|
||||
allow_data_in_errors_ ? key_.ToString(true).c_str() : "hidden",
|
||||
s.getState());
|
||||
assert(false);
|
||||
ROCKS_LOG_FATAL(info_log_, "Invalid key in compaction. %s",
|
||||
s.getState());
|
||||
}
|
||||
|
||||
// Keep current_key_ in sync.
|
||||
@@ -309,14 +307,6 @@ bool CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
// no value associated with delete
|
||||
value_.clear();
|
||||
iter_stats_.num_record_drop_user++;
|
||||
} else if (filter == CompactionFilter::Decision::kPurge) {
|
||||
// convert the current key to a single delete; key_ is pointing into
|
||||
// current_key_ at this point, so updating current_key_ updates key()
|
||||
ikey_.type = kTypeSingleDeletion;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, kTypeSingleDeletion);
|
||||
// no value associated with single delete
|
||||
value_.clear();
|
||||
iter_stats_.num_record_drop_user++;
|
||||
} else if (filter == CompactionFilter::Decision::kChangeValue) {
|
||||
if (ikey_.type == kTypeBlobIndex) {
|
||||
// value transfer from blob file to inlined data
|
||||
@@ -519,18 +509,18 @@ void CompactionIterator::NextFromInput() {
|
||||
// In the previous iteration we encountered a single delete that we could
|
||||
// not compact out. We will keep this Put, but can drop it's data.
|
||||
// (See Optimization 3, below.)
|
||||
assert(ikey_.type == kTypeValue || ikey_.type == kTypeBlobIndex);
|
||||
if (ikey_.type != kTypeValue && ikey_.type != kTypeBlobIndex) {
|
||||
ROCKS_LOG_FATAL(info_log_, "Unexpected key %s for compaction output",
|
||||
ikey_.DebugString(allow_data_in_errors_, true).c_str());
|
||||
assert(false);
|
||||
ROCKS_LOG_FATAL(info_log_,
|
||||
"Unexpected key type %d for compaction output",
|
||||
ikey_.type);
|
||||
}
|
||||
assert(current_user_key_snapshot_ >= last_snapshot);
|
||||
if (current_user_key_snapshot_ < last_snapshot) {
|
||||
ROCKS_LOG_FATAL(info_log_,
|
||||
"key %s, current_user_key_snapshot_ (%" PRIu64
|
||||
"current_user_key_snapshot_ (%" PRIu64
|
||||
") < last_snapshot (%" PRIu64 ")",
|
||||
ikey_.DebugString(allow_data_in_errors_, true).c_str(),
|
||||
current_user_key_snapshot_, last_snapshot);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (ikey_.type == kTypeBlobIndex) {
|
||||
@@ -635,39 +625,24 @@ void CompactionIterator::NextFromInput() {
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionIterator::NextFromInput:SingleDelete:2", nullptr);
|
||||
if (next_ikey.type == kTypeSingleDeletion) {
|
||||
if (next_ikey.type == kTypeSingleDeletion ||
|
||||
next_ikey.type == kTypeDeletion) {
|
||||
// We encountered two SingleDeletes for same key in a row. This
|
||||
// could be due to unexpected user input. If write-(un)prepared
|
||||
// transaction is used, this could also be due to releasing an old
|
||||
// snapshot between a Put and its matching SingleDelete.
|
||||
// Furthermore, if write-(un)prepared transaction is rolled back
|
||||
// after prepare, we will write a Delete to cancel a prior Put. If
|
||||
// old snapshot is released between a later Put and its matching
|
||||
// SingleDelete, we will end up with a Delete followed by
|
||||
// SingleDelete.
|
||||
// Skip the first SingleDelete and let the next iteration decide
|
||||
// how to handle the second SingleDelete.
|
||||
// how to handle the second SingleDelete or Delete.
|
||||
|
||||
// First SingleDelete has been skipped since we already called
|
||||
// input_.Next().
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
} else if (next_ikey.type == kTypeDeletion) {
|
||||
std::ostringstream oss;
|
||||
oss << "Found SD and type: " << static_cast<int>(next_ikey.type)
|
||||
<< " on the same key, violating the contract "
|
||||
"of SingleDelete. Check your application to make sure the "
|
||||
"application does not mix SingleDelete and Delete for "
|
||||
"the same key. If you are using "
|
||||
"write-prepared/write-unprepared transactions, and use "
|
||||
"SingleDelete to delete certain keys, then make sure "
|
||||
"TransactionDBOptions::rollback_deletion_type_callback is "
|
||||
"configured properly. Mixing SD and DEL can lead to "
|
||||
"undefined behaviors";
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
if (enforce_single_del_contracts_) {
|
||||
ROCKS_LOG_ERROR(info_log_, "%s", oss.str().c_str());
|
||||
valid_ = false;
|
||||
status_ = Status::Corruption(oss.str());
|
||||
return;
|
||||
}
|
||||
ROCKS_LOG_WARN(info_log_, "%s", oss.str().c_str());
|
||||
} else if (!is_timestamp_eligible_for_gc) {
|
||||
// We cannot drop the SingleDelete as timestamp is enabled, and
|
||||
// timestamp of this key is greater than or equal to
|
||||
@@ -769,13 +744,12 @@ void CompactionIterator::NextFromInput() {
|
||||
// Note: Dropping this key will not affect TransactionDB write-conflict
|
||||
// checking since there has already been a record returned for this key
|
||||
// in this snapshot.
|
||||
assert(last_sequence >= current_user_key_sequence_);
|
||||
if (last_sequence < current_user_key_sequence_) {
|
||||
ROCKS_LOG_FATAL(info_log_,
|
||||
"key %s, last_sequence (%" PRIu64
|
||||
"last_sequence (%" PRIu64
|
||||
") < current_user_key_sequence_ (%" PRIu64 ")",
|
||||
ikey_.DebugString(allow_data_in_errors_, true).c_str(),
|
||||
last_sequence, current_user_key_sequence_);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
++iter_stats_.num_record_drop_hidden; // rule (A)
|
||||
@@ -887,12 +861,10 @@ void CompactionIterator::NextFromInput() {
|
||||
pik_status = ParseInternalKey(key_, &ikey_, allow_data_in_errors_);
|
||||
// MergeUntil stops when it encounters a corrupt key and does not
|
||||
// include them in the result, so we expect the keys here to valid.
|
||||
assert(pik_status.ok());
|
||||
if (!pik_status.ok()) {
|
||||
ROCKS_LOG_FATAL(
|
||||
info_log_, "Invalid key %s in compaction. %s",
|
||||
allow_data_in_errors_ ? key_.ToString(true).c_str() : "hidden",
|
||||
pik_status.getState());
|
||||
assert(false);
|
||||
ROCKS_LOG_FATAL(info_log_, "Invalid key in compaction. %s",
|
||||
pik_status.getState());
|
||||
}
|
||||
// Keep current_key_ in sync.
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
@@ -1095,22 +1067,15 @@ void CompactionIterator::PrepareOutput() {
|
||||
!compaction_->allow_ingest_behind() && bottommost_level_ &&
|
||||
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
|
||||
ikey_.type != kTypeMerge && current_key_committed_) {
|
||||
assert(ikey_.type != kTypeDeletion);
|
||||
assert(ikey_.type != kTypeSingleDeletion ||
|
||||
(timestamp_size_ || full_history_ts_low_));
|
||||
if (ikey_.type == kTypeDeletion ||
|
||||
(ikey_.type == kTypeSingleDeletion && timestamp_size_ == 0)) {
|
||||
ROCKS_LOG_FATAL(
|
||||
info_log_,
|
||||
"Unexpected key %s for seq-zero optimization. "
|
||||
"earliest_snapshot %" PRIu64
|
||||
", earliest_write_conflict_snapshot %" PRIu64
|
||||
" job_snapshot %" PRIu64
|
||||
". timestamp_size: %d full_history_ts_low_ %s",
|
||||
ikey_.DebugString(allow_data_in_errors_, true).c_str(),
|
||||
earliest_snapshot_, earliest_write_conflict_snapshot_,
|
||||
job_snapshot_, static_cast<int>(timestamp_size_),
|
||||
full_history_ts_low_ != nullptr
|
||||
? Slice(*full_history_ts_low_).ToString(true).c_str()
|
||||
: "null");
|
||||
assert(false);
|
||||
(ikey_.type == kTypeSingleDeletion &&
|
||||
(!timestamp_size_ || !full_history_ts_low_))) {
|
||||
ROCKS_LOG_FATAL(info_log_,
|
||||
"Unexpected key type %d for seq-zero optimization",
|
||||
ikey_.type);
|
||||
}
|
||||
ikey_.sequence = 0;
|
||||
last_key_seq_zeroed_ = true;
|
||||
@@ -1141,17 +1106,14 @@ inline SequenceNumber CompactionIterator::findEarliestVisibleSnapshot(
|
||||
}
|
||||
auto snapshots_iter = std::lower_bound(
|
||||
snapshots_->begin(), snapshots_->end(), in);
|
||||
assert(prev_snapshot != nullptr);
|
||||
if (snapshots_iter == snapshots_->begin()) {
|
||||
*prev_snapshot = 0;
|
||||
} else {
|
||||
*prev_snapshot = *std::prev(snapshots_iter);
|
||||
assert(*prev_snapshot < in);
|
||||
if (*prev_snapshot >= in) {
|
||||
ROCKS_LOG_FATAL(info_log_,
|
||||
"*prev_snapshot (%" PRIu64 ") >= in (%" PRIu64
|
||||
") in findEarliestVisibleSnapshot",
|
||||
*prev_snapshot, in);
|
||||
assert(false);
|
||||
"*prev_snapshot >= in in findEarliestVisibleSnapshot");
|
||||
}
|
||||
}
|
||||
if (snapshot_checker_ == nullptr) {
|
||||
@@ -1161,12 +1123,9 @@ inline SequenceNumber CompactionIterator::findEarliestVisibleSnapshot(
|
||||
bool has_released_snapshot = !released_snapshots_.empty();
|
||||
for (; snapshots_iter != snapshots_->end(); ++snapshots_iter) {
|
||||
auto cur = *snapshots_iter;
|
||||
assert(in <= cur);
|
||||
if (in > cur) {
|
||||
ROCKS_LOG_FATAL(info_log_,
|
||||
"in (%" PRIu64 ") > cur (%" PRIu64
|
||||
") in findEarliestVisibleSnapshot",
|
||||
in, cur);
|
||||
assert(false);
|
||||
ROCKS_LOG_FATAL(info_log_, "in > cur in findEarliestVisibleSnapshot");
|
||||
}
|
||||
// Skip if cur is in released_snapshots.
|
||||
if (has_released_snapshot && released_snapshots_.count(cur) > 0) {
|
||||
|
||||
@@ -142,11 +142,12 @@ class CompactionIterator {
|
||||
}
|
||||
|
||||
bool enable_blob_garbage_collection() const override {
|
||||
return compaction_->enable_blob_garbage_collection();
|
||||
return compaction_->mutable_cf_options()->enable_blob_garbage_collection;
|
||||
}
|
||||
|
||||
double blob_garbage_collection_age_cutoff() const override {
|
||||
return compaction_->blob_garbage_collection_age_cutoff();
|
||||
return compaction_->mutable_cf_options()
|
||||
->blob_garbage_collection_age_cutoff;
|
||||
}
|
||||
|
||||
uint64_t blob_compaction_readahead_size() const override {
|
||||
@@ -167,42 +168,38 @@ class CompactionIterator {
|
||||
const Compaction* compaction_;
|
||||
};
|
||||
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
MergeHelper* merge_helper, SequenceNumber last_sequence,
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
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,
|
||||
bool enforce_single_del_contracts,
|
||||
const std::atomic<bool>& manual_compaction_canceled,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
const std::string* full_history_ts_low = nullptr);
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
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 std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::atomic<bool>* manual_compaction_canceled = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
const std::string* full_history_ts_low = nullptr);
|
||||
|
||||
// Constructor with custom CompactionProxy, used for tests.
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
MergeHelper* merge_helper, SequenceNumber last_sequence,
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
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,
|
||||
bool enforce_single_del_contracts,
|
||||
const std::atomic<bool>& manual_compaction_canceled,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
const std::string* full_history_ts_low = nullptr);
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
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 std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::atomic<bool>* manual_compaction_canceled = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
const std::string* full_history_ts_low = nullptr);
|
||||
|
||||
~CompactionIterator();
|
||||
|
||||
@@ -323,7 +320,8 @@ class CompactionIterator {
|
||||
std::unique_ptr<CompactionProxy> compaction_;
|
||||
const CompactionFilter* compaction_filter_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const std::atomic<bool>& manual_compaction_canceled_;
|
||||
const std::atomic<int>* manual_compaction_paused_;
|
||||
const std::atomic<bool>* manual_compaction_canceled_;
|
||||
bool bottommost_level_;
|
||||
bool valid_ = false;
|
||||
bool visible_at_tip_;
|
||||
@@ -334,8 +332,6 @@ class CompactionIterator {
|
||||
|
||||
bool allow_data_in_errors_;
|
||||
|
||||
const bool enforce_single_del_contracts_;
|
||||
|
||||
// Comes from comparator.
|
||||
const size_t timestamp_size_;
|
||||
|
||||
@@ -428,7 +424,10 @@ class CompactionIterator {
|
||||
|
||||
bool IsPausingManualCompaction() {
|
||||
// This is a best-effort facility, so memory_order_relaxed is sufficient.
|
||||
return manual_compaction_canceled_.load(std::memory_order_relaxed);
|
||||
return (manual_compaction_paused_ &&
|
||||
manual_compaction_paused_->load(std::memory_order_relaxed) > 0) ||
|
||||
(manual_compaction_canceled_ &&
|
||||
manual_compaction_canceled_->load(std::memory_order_relaxed));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -279,9 +279,9 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
snapshot_checker_.get(), Env::Default(),
|
||||
false /* report_detailed_time */, false, range_del_agg_.get(),
|
||||
nullptr /* blob_file_builder */, true /*allow_data_in_errors*/,
|
||||
true /*enforce_single_del_contracts*/,
|
||||
/*manual_compaction_canceled=*/kManualCompactionCanceledFalse_,
|
||||
std::move(compaction), filter, &shutting_down_, /*info_log=*/nullptr,
|
||||
std::move(compaction), filter, &shutting_down_,
|
||||
/*manual_compaction_paused=*/nullptr,
|
||||
/*manual_compaction_canceled=*/nullptr, /*info_log=*/nullptr,
|
||||
full_history_ts_low));
|
||||
}
|
||||
|
||||
@@ -313,7 +313,7 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
key_not_exists_beyond_output_level, full_history_ts_low);
|
||||
c_iter_->SeekToFirst();
|
||||
for (size_t i = 0; i < expected_keys.size(); i++) {
|
||||
std::string info = "i = " + std::to_string(i);
|
||||
std::string info = "i = " + ToString(i);
|
||||
ASSERT_TRUE(c_iter_->Valid()) << info;
|
||||
ASSERT_OK(c_iter_->status()) << info;
|
||||
ASSERT_EQ(expected_keys[i], c_iter_->key().ToString()) << info;
|
||||
@@ -340,7 +340,6 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg_;
|
||||
std::unique_ptr<SnapshotChecker> snapshot_checker_;
|
||||
std::atomic<bool> shutting_down_{false};
|
||||
const std::atomic<bool> kManualCompactionCanceledFalse_{false};
|
||||
FakeCompaction* compaction_proxy_;
|
||||
};
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/hash.h"
|
||||
@@ -429,7 +428,8 @@ CompactionJob::CompactionJob(
|
||||
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<bool>& manual_compaction_canceled,
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::atomic<bool>* manual_compaction_canceled,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
std::string full_history_ts_low, std::string trim_ts,
|
||||
BlobFileCompletionCallback* blob_callback)
|
||||
@@ -455,6 +455,7 @@ CompactionJob::CompactionJob(
|
||||
fs_->OptimizeForCompactionTableRead(file_options, db_options_)),
|
||||
versions_(versions),
|
||||
shutting_down_(shutting_down),
|
||||
manual_compaction_paused_(manual_compaction_paused),
|
||||
manual_compaction_canceled_(manual_compaction_canceled),
|
||||
db_directory_(db_directory),
|
||||
blob_output_directory_(blob_output_directory),
|
||||
@@ -1046,7 +1047,6 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
const Compaction* compaction = sub_compact->compaction;
|
||||
CompactionServiceInput compaction_input;
|
||||
compaction_input.output_level = compaction->output_level();
|
||||
compaction_input.db_id = db_id_;
|
||||
|
||||
const std::vector<CompactionInputFiles>& inputs =
|
||||
*(compact_->compaction->inputs());
|
||||
@@ -1208,7 +1208,6 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
meta.oldest_ancester_time = file.oldest_ancester_time;
|
||||
meta.file_creation_time = file.file_creation_time;
|
||||
meta.marked_for_compaction = file.marked_for_compaction;
|
||||
meta.unique_id = file.unique_id;
|
||||
|
||||
auto cfd = compaction->column_family_data();
|
||||
sub_compact->outputs.emplace_back(std::move(meta),
|
||||
@@ -1254,8 +1253,8 @@ void CompactionJob::NotifyOnSubcompactionBegin(
|
||||
if (shutting_down_->load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
if (c->is_manual_compaction() &&
|
||||
manual_compaction_canceled_.load(std::memory_order_acquire)) {
|
||||
if (c->is_manual_compaction() && manual_compaction_paused_ &&
|
||||
manual_compaction_paused_->load(std::memory_order_acquire) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1351,7 +1350,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
read_options.fill_cache = false;
|
||||
read_options.rate_limiter_priority = GetRateLimiterPriority();
|
||||
read_options.rate_limiter_priority = Env::IO_LOW;
|
||||
// Compaction iterators shouldn't be confined to a single prefix.
|
||||
// Compactions use Seek() for
|
||||
// (a) concurrent compactions,
|
||||
@@ -1452,9 +1451,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
|
||||
std::unique_ptr<BlobFileBuilder> blob_file_builder(
|
||||
(mutable_cf_options->enable_blob_files &&
|
||||
sub_compact->compaction->output_level() >=
|
||||
mutable_cf_options->blob_file_starting_level)
|
||||
mutable_cf_options->enable_blob_files
|
||||
? new BlobFileBuilder(
|
||||
versions_, fs_.get(),
|
||||
sub_compact->compaction->immutable_options(),
|
||||
@@ -1468,7 +1465,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:1",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<bool>*>(&manual_compaction_canceled_)));
|
||||
const_cast<std::atomic<int>*>(manual_compaction_paused_)));
|
||||
|
||||
Status status;
|
||||
const std::string* const full_history_ts_low =
|
||||
@@ -1482,8 +1479,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
snapshot_checker_, env_, ShouldReportDetailedTime(env_, stats_),
|
||||
/*expect_valid_internal_key=*/true, &range_del_agg,
|
||||
blob_file_builder.get(), db_options_.allow_data_in_errors,
|
||||
db_options_.enforce_single_del_contracts, manual_compaction_canceled_,
|
||||
sub_compact->compaction, compaction_filter, shutting_down_,
|
||||
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();
|
||||
@@ -1566,7 +1563,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:2",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<bool>*>(&manual_compaction_canceled_)));
|
||||
const_cast<std::atomic<int>*>(manual_compaction_paused_)));
|
||||
if (partitioner.get()) {
|
||||
last_key_for_partitioner.assign(c_iter->user_key().data_,
|
||||
c_iter->user_key().size_);
|
||||
@@ -1645,7 +1642,10 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
status = Status::ShutdownInProgress("Database shutdown");
|
||||
}
|
||||
if ((status.ok() || status.IsColumnFamilyDropped()) &&
|
||||
(manual_compaction_canceled_.load(std::memory_order_relaxed))) {
|
||||
((manual_compaction_paused_ &&
|
||||
manual_compaction_paused_->load(std::memory_order_relaxed) > 0) ||
|
||||
(manual_compaction_canceled_ &&
|
||||
manual_compaction_canceled_->load(std::memory_order_relaxed)))) {
|
||||
status = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
if (status.ok()) {
|
||||
@@ -1974,8 +1974,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
refined_oldest_ancester_time =
|
||||
sub_compact->compaction->MinInputFileOldestAncesterTime(
|
||||
&(meta->smallest), &(meta->largest));
|
||||
if (refined_oldest_ancester_time !=
|
||||
std::numeric_limits<uint64_t>::max()) {
|
||||
if (refined_oldest_ancester_time != port::kMaxUint64) {
|
||||
meta->oldest_ancester_time = refined_oldest_ancester_time;
|
||||
}
|
||||
}
|
||||
@@ -2112,11 +2111,11 @@ Status CompactionJob::InstallCompactionResults(
|
||||
|
||||
{
|
||||
Compaction::InputLevelSummaryBuffer inputs_summary;
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] [JOB %d] Compacted %s => %" PRIu64 " bytes",
|
||||
compaction->column_family_data()->GetName().c_str(),
|
||||
job_id_, compaction->InputLevelSummary(&inputs_summary),
|
||||
compact_->total_bytes + compact_->total_blob_bytes);
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Compacted %s => %" PRIu64 " bytes",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
compaction->InputLevelSummary(&inputs_summary),
|
||||
compact_->total_bytes + compact_->total_blob_bytes);
|
||||
}
|
||||
|
||||
VersionEdit* const edit = compaction->edit();
|
||||
@@ -2265,7 +2264,7 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
sub_compact->compaction->MinInputFileOldestAncesterTime(
|
||||
(sub_compact->start != nullptr) ? &tmp_start : nullptr,
|
||||
(sub_compact->end != nullptr) ? &tmp_end : nullptr);
|
||||
if (oldest_ancester_time == std::numeric_limits<uint64_t>::max()) {
|
||||
if (oldest_ancester_time == port::kMaxUint64) {
|
||||
oldest_ancester_time = current_time;
|
||||
}
|
||||
|
||||
@@ -2277,18 +2276,6 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
meta.oldest_ancester_time = oldest_ancester_time;
|
||||
meta.file_creation_time = current_time;
|
||||
meta.temperature = temperature;
|
||||
assert(!db_id_.empty());
|
||||
assert(!db_session_id_.empty());
|
||||
s = GetSstInternalUniqueId(db_id_, db_session_id_, meta.fd.GetNumber(),
|
||||
&meta.unique_id);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(db_options_.info_log,
|
||||
"[%s] [JOB %d] file #%" PRIu64
|
||||
" failed to generate unique id: %s.",
|
||||
cfd->GetName().c_str(), job_id_, meta.fd.GetNumber(),
|
||||
s.ToString().c_str());
|
||||
return s;
|
||||
}
|
||||
sub_compact->outputs.emplace_back(
|
||||
std::move(meta), cfd->internal_comparator(),
|
||||
/*enable_order_check=*/
|
||||
@@ -2297,7 +2284,7 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
/*enable_hash=*/paranoid_file_checks_);
|
||||
}
|
||||
|
||||
writable_file->SetIOPriority(GetRateLimiterPriority());
|
||||
writable_file->SetIOPriority(Env::IOPriority::IO_LOW);
|
||||
writable_file->SetWriteLifeTimeHint(write_hint_);
|
||||
FileTypeSet tmp_set = db_options_.checksum_handoff_file_types;
|
||||
writable_file->SetPreallocationBlockSize(static_cast<size_t>(
|
||||
@@ -2471,7 +2458,7 @@ void CompactionJob::LogCompaction() {
|
||||
<< "compaction_reason"
|
||||
<< GetCompactionReasonString(compaction->compaction_reason());
|
||||
for (size_t i = 0; i < compaction->num_input_levels(); ++i) {
|
||||
stream << ("files_L" + std::to_string(compaction->level(i)));
|
||||
stream << ("files_L" + ToString(compaction->level(i)));
|
||||
stream.StartArray();
|
||||
for (auto f : *compaction->inputs(i)) {
|
||||
stream << f->fd.GetNumber();
|
||||
@@ -2488,19 +2475,6 @@ std::string CompactionJob::GetTableFileName(uint64_t file_number) {
|
||||
file_number, compact_->compaction->output_path_id());
|
||||
}
|
||||
|
||||
Env::IOPriority CompactionJob::GetRateLimiterPriority() {
|
||||
if (versions_ && versions_->GetColumnFamilySet() &&
|
||||
versions_->GetColumnFamilySet()->write_controller()) {
|
||||
WriteController* write_controller =
|
||||
versions_->GetColumnFamilySet()->write_controller();
|
||||
if (write_controller->NeedsDelay() || write_controller->IsStopped()) {
|
||||
return Env::IO_USER;
|
||||
}
|
||||
}
|
||||
|
||||
return Env::IO_LOW;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::string CompactionServiceCompactionJob::GetTableFileName(
|
||||
uint64_t file_number) {
|
||||
@@ -2522,7 +2496,7 @@ 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::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,
|
||||
@@ -2535,7 +2509,7 @@ CompactionServiceCompactionJob::CompactionServiceCompactionJob(
|
||||
compaction->mutable_cf_options()->paranoid_file_checks,
|
||||
compaction->mutable_cf_options()->report_bg_io_stats, dbname,
|
||||
&(compaction_service_result->stats), Env::Priority::USER, io_tracer,
|
||||
manual_compaction_canceled, 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),
|
||||
@@ -2621,7 +2595,7 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
meta.fd.largest_seqno, meta.smallest.Encode().ToString(),
|
||||
meta.largest.Encode().ToString(), meta.oldest_ancester_time,
|
||||
meta.file_creation_time, output_file.validator.GetHash(),
|
||||
meta.marked_for_compaction, meta.unique_id);
|
||||
meta.marked_for_compaction);
|
||||
}
|
||||
compaction_result_->num_output_records = sub_compact->num_output_records;
|
||||
compaction_result_->total_bytes = sub_compact->total_bytes;
|
||||
@@ -2725,9 +2699,6 @@ static std::unordered_map<std::string, OptionTypeInfo> cs_input_type_info = {
|
||||
{"output_level",
|
||||
{offsetof(struct CompactionServiceInput, output_level), OptionType::kInt,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
|
||||
{"db_id",
|
||||
{offsetof(struct CompactionServiceInput, db_id),
|
||||
OptionType::kEncodedString}},
|
||||
{"has_begin",
|
||||
{offsetof(struct CompactionServiceInput, has_begin), OptionType::kBoolean,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
|
||||
@@ -2785,11 +2756,6 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
{offsetof(struct CompactionServiceOutputFile, marked_for_compaction),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"unique_id",
|
||||
OptionTypeInfo::Array<uint64_t, 2>(
|
||||
offsetof(struct CompactionServiceOutputFile, unique_id),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
|
||||
{0, OptionType::kUInt64T})},
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
@@ -2985,7 +2951,6 @@ static std::unordered_map<std::string, OptionTypeInfo> cs_result_type_info = {
|
||||
const void* addr1, const void* addr2, std::string* mismatch) {
|
||||
const auto status1 = static_cast<const Status*>(addr1);
|
||||
const auto status2 = static_cast<const Status*>(addr2);
|
||||
|
||||
StatusSerializationAdapter adatper1(*status1);
|
||||
StatusSerializationAdapter adapter2(*status2);
|
||||
return OptionTypeInfo::TypesAreEqual(opts, status_adapter_type_info,
|
||||
@@ -3043,7 +3008,7 @@ Status CompactionServiceInput::Read(const std::string& data_str,
|
||||
} else {
|
||||
return Status::NotSupported(
|
||||
"Compaction Service Input data version not supported: " +
|
||||
std::to_string(format_version));
|
||||
ToString(format_version));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3072,7 +3037,7 @@ Status CompactionServiceResult::Read(const std::string& data_str,
|
||||
} else {
|
||||
return Status::NotSupported(
|
||||
"Compaction Service Result data version not supported: " +
|
||||
std::to_string(format_version));
|
||||
ToString(format_version));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,8 @@ class CompactionJob {
|
||||
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<bool>& manual_compaction_canceled,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::atomic<bool>* manual_compaction_canceled = nullptr,
|
||||
const std::string& db_id = "", const std::string& db_session_id = "",
|
||||
std::string full_history_ts_low = "", std::string trim_ts = "",
|
||||
BlobFileCompletionCallback* blob_callback = nullptr);
|
||||
@@ -136,8 +137,6 @@ class CompactionJob {
|
||||
IOStatus io_status_;
|
||||
|
||||
private:
|
||||
friend class CompactionJobTestBase;
|
||||
|
||||
// Generates a histogram representing potential divisions of key ranges from
|
||||
// the input. It adds the starting and/or ending keys of certain input files
|
||||
// to the working set and then finds the approximate size of data in between
|
||||
@@ -194,7 +193,8 @@ class CompactionJob {
|
||||
FileOptions file_options_for_read_;
|
||||
VersionSet* versions_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const std::atomic<bool>& manual_compaction_canceled_;
|
||||
const std::atomic<int>* manual_compaction_paused_;
|
||||
const std::atomic<bool>* manual_compaction_canceled_;
|
||||
FSDirectory* db_directory_;
|
||||
FSDirectory* blob_output_directory_;
|
||||
InstrumentedMutex* db_mutex_;
|
||||
@@ -234,10 +234,6 @@ class CompactionJob {
|
||||
// Get table file name in where it's outputting to, which should also be in
|
||||
// `output_directory_`.
|
||||
virtual std::string GetTableFileName(uint64_t file_number);
|
||||
// The rate limiter priority (io_priority) is determined dynamically here.
|
||||
// The Compaction Read and Write priorities are the same for different
|
||||
// scenarios, such as write stalled.
|
||||
Env::IOPriority GetRateLimiterPriority();
|
||||
};
|
||||
|
||||
// CompactionServiceInput is used the pass compaction information between two
|
||||
@@ -257,9 +253,6 @@ struct CompactionServiceInput {
|
||||
std::vector<std::string> input_files;
|
||||
int output_level;
|
||||
|
||||
// db_id is used to generate unique id of sst on the remote compactor
|
||||
std::string db_id;
|
||||
|
||||
// information for subcompaction
|
||||
bool has_begin = false;
|
||||
std::string begin;
|
||||
@@ -291,15 +284,13 @@ struct CompactionServiceOutputFile {
|
||||
uint64_t file_creation_time;
|
||||
uint64_t paranoid_hash;
|
||||
bool marked_for_compaction;
|
||||
UniqueId64x2 unique_id;
|
||||
|
||||
CompactionServiceOutputFile() = default;
|
||||
CompactionServiceOutputFile(
|
||||
const std::string& name, SequenceNumber smallest, SequenceNumber largest,
|
||||
std::string _smallest_internal_key, std::string _largest_internal_key,
|
||||
uint64_t _oldest_ancester_time, uint64_t _file_creation_time,
|
||||
uint64_t _paranoid_hash, bool _marked_for_compaction,
|
||||
UniqueId64x2 _unique_id)
|
||||
uint64_t _paranoid_hash, bool _marked_for_compaction)
|
||||
: file_name(name),
|
||||
smallest_seqno(smallest),
|
||||
largest_seqno(largest),
|
||||
@@ -308,8 +299,7 @@ struct CompactionServiceOutputFile {
|
||||
oldest_ancester_time(_oldest_ancester_time),
|
||||
file_creation_time(_file_creation_time),
|
||||
paranoid_hash(_paranoid_hash),
|
||||
marked_for_compaction(_marked_for_compaction),
|
||||
unique_id(std::move(_unique_id)) {}
|
||||
marked_for_compaction(_marked_for_compaction) {}
|
||||
};
|
||||
|
||||
// CompactionServiceResult contains the compaction result from a different db
|
||||
@@ -355,7 +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::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,
|
||||
|
||||
@@ -268,10 +268,10 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
if (cf == 0) {
|
||||
// default cfd
|
||||
EXPECT_TRUE(db_->GetProperty(
|
||||
"rocksdb.num-files-at-level" + std::to_string(level), &property));
|
||||
"rocksdb.num-files-at-level" + ToString(level), &property));
|
||||
} else {
|
||||
EXPECT_TRUE(db_->GetProperty(
|
||||
handles_[cf], "rocksdb.num-files-at-level" + std::to_string(level),
|
||||
handles_[cf], "rocksdb.num-files-at-level" + ToString(level),
|
||||
&property));
|
||||
}
|
||||
return atoi(property.c_str());
|
||||
@@ -672,7 +672,7 @@ TEST_P(CompactionJobStatsTest, CompactionJobStatsTest) {
|
||||
snprintf(buf, kBufSize, "%d", ++num_L0_files);
|
||||
ASSERT_EQ(std::string(buf), FilesPerLevel(1));
|
||||
}
|
||||
ASSERT_EQ(std::to_string(num_L0_files), FilesPerLevel(1));
|
||||
ASSERT_EQ(ToString(num_L0_files), FilesPerLevel(1));
|
||||
|
||||
// 2nd Phase: perform L0 -> L1 compaction.
|
||||
int L0_compaction_count = 6;
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -69,138 +68,12 @@ void VerifyInitializationOfCompactionJobStats(
|
||||
#endif // !defined(IOS_CROSS_COMPILE)
|
||||
}
|
||||
|
||||
// Mock FSWritableFile for testing io priority.
|
||||
// Only override the essential functions for testing compaction io priority.
|
||||
class MockTestWritableFile : public FSWritableFileOwnerWrapper {
|
||||
public:
|
||||
MockTestWritableFile(std::unique_ptr<FSWritableFile>&& file,
|
||||
Env::IOPriority io_priority)
|
||||
: FSWritableFileOwnerWrapper(std::move(file)),
|
||||
write_io_priority_(io_priority) {}
|
||||
IOStatus Append(const Slice& data, const IOOptions& options,
|
||||
IODebugContext* dbg) override {
|
||||
EXPECT_EQ(options.rate_limiter_priority, write_io_priority_);
|
||||
return target()->Append(data, options, dbg);
|
||||
}
|
||||
IOStatus Append(const Slice& data, const IOOptions& options,
|
||||
const DataVerificationInfo& verification_info,
|
||||
IODebugContext* dbg) override {
|
||||
EXPECT_EQ(options.rate_limiter_priority, write_io_priority_);
|
||||
return target()->Append(data, options, verification_info, dbg);
|
||||
}
|
||||
IOStatus Close(const IOOptions& options, IODebugContext* dbg) override {
|
||||
EXPECT_EQ(options.rate_limiter_priority, write_io_priority_);
|
||||
return target()->Close(options, dbg);
|
||||
}
|
||||
IOStatus Flush(const IOOptions& options, IODebugContext* dbg) override {
|
||||
EXPECT_EQ(options.rate_limiter_priority, write_io_priority_);
|
||||
return target()->Flush(options, dbg);
|
||||
}
|
||||
IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override {
|
||||
EXPECT_EQ(options.rate_limiter_priority, write_io_priority_);
|
||||
return target()->Sync(options, dbg);
|
||||
}
|
||||
IOStatus Fsync(const IOOptions& options, IODebugContext* dbg) override {
|
||||
EXPECT_EQ(options.rate_limiter_priority, write_io_priority_);
|
||||
return target()->Fsync(options, dbg);
|
||||
}
|
||||
uint64_t GetFileSize(const IOOptions& options, IODebugContext* dbg) override {
|
||||
EXPECT_EQ(options.rate_limiter_priority, write_io_priority_);
|
||||
return target()->GetFileSize(options, dbg);
|
||||
}
|
||||
IOStatus RangeSync(uint64_t offset, uint64_t nbytes, const IOOptions& options,
|
||||
IODebugContext* dbg) override {
|
||||
EXPECT_EQ(options.rate_limiter_priority, write_io_priority_);
|
||||
return target()->RangeSync(offset, nbytes, options, dbg);
|
||||
}
|
||||
|
||||
void PrepareWrite(size_t offset, size_t len, const IOOptions& options,
|
||||
IODebugContext* dbg) override {
|
||||
EXPECT_EQ(options.rate_limiter_priority, write_io_priority_);
|
||||
target()->PrepareWrite(offset, len, options, dbg);
|
||||
}
|
||||
|
||||
IOStatus Allocate(uint64_t offset, uint64_t len, const IOOptions& options,
|
||||
IODebugContext* dbg) override {
|
||||
EXPECT_EQ(options.rate_limiter_priority, write_io_priority_);
|
||||
return target()->Allocate(offset, len, options, dbg);
|
||||
}
|
||||
|
||||
private:
|
||||
Env::IOPriority write_io_priority_;
|
||||
};
|
||||
|
||||
// Mock FSRandomAccessFile for testing io priority.
|
||||
// Only override the essential functions for testing compaction io priority.
|
||||
class MockTestRandomAccessFile : public FSRandomAccessFileOwnerWrapper {
|
||||
public:
|
||||
MockTestRandomAccessFile(std::unique_ptr<FSRandomAccessFile>&& file,
|
||||
Env::IOPriority io_priority)
|
||||
: FSRandomAccessFileOwnerWrapper(std::move(file)),
|
||||
read_io_priority_(io_priority) {}
|
||||
|
||||
IOStatus Read(uint64_t offset, size_t n, const IOOptions& options,
|
||||
Slice* result, char* scratch,
|
||||
IODebugContext* dbg) const override {
|
||||
EXPECT_EQ(options.rate_limiter_priority, read_io_priority_);
|
||||
return target()->Read(offset, n, options, result, scratch, dbg);
|
||||
}
|
||||
IOStatus Prefetch(uint64_t offset, size_t n, const IOOptions& options,
|
||||
IODebugContext* dbg) override {
|
||||
EXPECT_EQ(options.rate_limiter_priority, read_io_priority_);
|
||||
return target()->Prefetch(offset, n, options, dbg);
|
||||
}
|
||||
|
||||
private:
|
||||
Env::IOPriority read_io_priority_;
|
||||
};
|
||||
|
||||
// Mock FileSystem for testing io priority.
|
||||
class MockTestFileSystem : public FileSystemWrapper {
|
||||
public:
|
||||
explicit MockTestFileSystem(const std::shared_ptr<FileSystem>& base,
|
||||
Env::IOPriority read_io_priority,
|
||||
Env::IOPriority write_io_priority)
|
||||
: FileSystemWrapper(base),
|
||||
read_io_priority_(read_io_priority),
|
||||
write_io_priority_(write_io_priority) {}
|
||||
|
||||
static const char* kClassName() { return "MockTestFileSystem"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
IOStatus NewRandomAccessFile(const std::string& fname,
|
||||
const FileOptions& file_opts,
|
||||
std::unique_ptr<FSRandomAccessFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
IOStatus s = target()->NewRandomAccessFile(fname, file_opts, result, dbg);
|
||||
EXPECT_OK(s);
|
||||
result->reset(
|
||||
new MockTestRandomAccessFile(std::move(*result), read_io_priority_));
|
||||
return s;
|
||||
}
|
||||
IOStatus NewWritableFile(const std::string& fname,
|
||||
const FileOptions& file_opts,
|
||||
std::unique_ptr<FSWritableFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
IOStatus s = target()->NewWritableFile(fname, file_opts, result, dbg);
|
||||
EXPECT_OK(s);
|
||||
result->reset(
|
||||
new MockTestWritableFile(std::move(*result), write_io_priority_));
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
Env::IOPriority read_io_priority_;
|
||||
Env::IOPriority write_io_priority_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class CompactionJobTestBase : public testing::Test {
|
||||
protected:
|
||||
CompactionJobTestBase(std::string dbname, const Comparator* ucmp,
|
||||
std::function<std::string(uint64_t)> encode_u64_ts,
|
||||
bool test_io_priority)
|
||||
std::function<std::string(uint64_t)> encode_u64_ts)
|
||||
: dbname_(std::move(dbname)),
|
||||
ucmp_(ucmp),
|
||||
db_options_(),
|
||||
@@ -216,8 +89,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
shutting_down_(false),
|
||||
mock_table_factory_(new mock::MockTableFactory()),
|
||||
error_handler_(nullptr, db_options_, &mutex_),
|
||||
encode_u64_ts_(std::move(encode_u64_ts)),
|
||||
test_io_priority_(test_io_priority) {
|
||||
encode_u64_ts_(std::move(encode_u64_ts)) {
|
||||
Env* base_env = Env::Default();
|
||||
EXPECT_OK(
|
||||
test::CreateEnvFromSystem(ConfigOptions(), &base_env, &env_guard_));
|
||||
@@ -232,12 +104,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
db_options_.db_paths.emplace_back(dbname_,
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
cf_options_.comparator = ucmp_;
|
||||
if (test_io_priority_) {
|
||||
BlockBasedTableOptions table_options;
|
||||
cf_options_.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
} else {
|
||||
cf_options_.table_factory = mock_table_factory_;
|
||||
}
|
||||
cf_options_.table_factory = mock_table_factory_;
|
||||
}
|
||||
|
||||
std::string GenerateFileName(uint64_t file_number) {
|
||||
@@ -277,33 +144,6 @@ class CompactionJobTestBase : public testing::Test {
|
||||
return blob_index;
|
||||
}
|
||||
|
||||
// Creates a table with the specificied key value pairs.
|
||||
void CreateTable(const std::string& table_name,
|
||||
const mock::KVVector& contents, uint64_t& file_size) {
|
||||
std::unique_ptr<WritableFileWriter> file_writer;
|
||||
Status s = WritableFileWriter::Create(fs_, table_name, FileOptions(),
|
||||
&file_writer, nullptr);
|
||||
ASSERT_OK(s);
|
||||
std::unique_ptr<TableBuilder> table_builder(
|
||||
cf_options_.table_factory->NewTableBuilder(
|
||||
TableBuilderOptions(*cfd_->ioptions(), mutable_cf_options_,
|
||||
cfd_->internal_comparator(),
|
||||
cfd_->int_tbl_prop_collector_factories(),
|
||||
CompressionType::kNoCompression,
|
||||
CompressionOptions(), 0 /* column_family_id */,
|
||||
kDefaultColumnFamilyName, -1 /* level */),
|
||||
file_writer.get()));
|
||||
// Build table.
|
||||
for (auto kv : contents) {
|
||||
std::string key;
|
||||
std::string value;
|
||||
std::tie(key, value) = kv;
|
||||
table_builder->Add(key, value);
|
||||
}
|
||||
ASSERT_OK(table_builder->Finish());
|
||||
file_size = table_builder->FileSize();
|
||||
}
|
||||
|
||||
void AddMockFile(const mock::KVVector& contents, int level = 0) {
|
||||
assert(contents.size() > 0);
|
||||
|
||||
@@ -357,23 +197,16 @@ class CompactionJobTestBase : public testing::Test {
|
||||
}
|
||||
|
||||
uint64_t file_number = versions_->NewFileNumber();
|
||||
|
||||
uint64_t file_size;
|
||||
if (test_io_priority_) {
|
||||
CreateTable(GenerateFileName(file_number), contents, file_size);
|
||||
} else {
|
||||
file_size = 10;
|
||||
EXPECT_OK(mock_table_factory_->CreateMockTable(
|
||||
env_, GenerateFileName(file_number), std::move(contents)));
|
||||
}
|
||||
EXPECT_OK(mock_table_factory_->CreateMockTable(
|
||||
env_, GenerateFileName(file_number), std::move(contents)));
|
||||
|
||||
VersionEdit edit;
|
||||
edit.AddFile(level, file_number, 0, file_size, smallest_key, largest_key,
|
||||
edit.AddFile(level, file_number, 0, 10, smallest_key, largest_key,
|
||||
smallest_seqno, largest_seqno, false, Temperature::kUnknown,
|
||||
oldest_blob_file_number, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kDisableUserTimestamp,
|
||||
kDisableUserTimestamp, kNullUniqueId64x2);
|
||||
kDisableUserTimestamp);
|
||||
|
||||
mutex_.Lock();
|
||||
EXPECT_OK(
|
||||
@@ -403,8 +236,8 @@ class CompactionJobTestBase : public testing::Test {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
auto contents = mock::MakeMockFile();
|
||||
for (int k = 0; k < kKeysPerFile; ++k) {
|
||||
auto key = std::to_string(i * kMatchingKeys + k);
|
||||
auto value = std::to_string(i * kKeysPerFile + k);
|
||||
auto key = ToString(i * kMatchingKeys + k);
|
||||
auto value = ToString(i * kKeysPerFile + k);
|
||||
InternalKey internal_key(key, ++sequence_number, kTypeValue);
|
||||
|
||||
// This is how the key will look like once it's written in bottommost
|
||||
@@ -488,16 +321,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
const std::vector<SequenceNumber>& snapshots = {},
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber,
|
||||
int output_level = 1, bool verify = true,
|
||||
uint64_t expected_oldest_blob_file_number = kInvalidBlobFileNumber,
|
||||
bool check_get_priority = false,
|
||||
Env::IOPriority read_io_priority = Env::IO_TOTAL,
|
||||
Env::IOPriority write_io_priority = Env::IO_TOTAL) {
|
||||
// For compaction, set fs as MockTestFileSystem to check the io_priority.
|
||||
if (test_io_priority_) {
|
||||
db_options_.fs.reset(
|
||||
new MockTestFileSystem(fs_, read_io_priority, write_io_priority));
|
||||
}
|
||||
|
||||
uint64_t expected_oldest_blob_file_number = kInvalidBlobFileNumber) {
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
|
||||
size_t num_input_files = 0;
|
||||
@@ -527,7 +351,6 @@ class CompactionJobTestBase : public testing::Test {
|
||||
SnapshotChecker* snapshot_checker = nullptr;
|
||||
ASSERT_TRUE(full_history_ts_low_.empty() ||
|
||||
ucmp_->timestamp_size() == full_history_ts_low_.size());
|
||||
const std::atomic<bool> kManualCompactionCanceledFalse{false};
|
||||
CompactionJob compaction_job(
|
||||
0, &compaction, db_options_, mutable_db_options_, env_options_,
|
||||
versions_.get(), &shutting_down_, &log_buffer, nullptr, nullptr,
|
||||
@@ -535,9 +358,9 @@ class CompactionJobTestBase : public testing::Test {
|
||||
earliest_write_conflict_snapshot, snapshot_checker, nullptr,
|
||||
table_cache_, &event_logger, false, false, dbname_,
|
||||
&compaction_job_stats_, Env::Priority::USER, nullptr /* IOTracer */,
|
||||
/*manual_compaction_canceled=*/kManualCompactionCanceledFalse,
|
||||
env_->GenerateUniqueId(), DBImpl::GenerateDbSessionId(nullptr),
|
||||
full_history_ts_low_);
|
||||
/*manual_compaction_paused=*/nullptr,
|
||||
/*manual_compaction_canceled=*/nullptr, /*db_id=*/"",
|
||||
/*db_session_id=*/"", full_history_ts_low_);
|
||||
VerifyInitializationOfCompactionJobStats(compaction_job_stats_);
|
||||
|
||||
compaction_job.Prepare();
|
||||
@@ -567,32 +390,6 @@ class CompactionJobTestBase : public testing::Test {
|
||||
expected_oldest_blob_file_number);
|
||||
}
|
||||
}
|
||||
|
||||
if (check_get_priority) {
|
||||
CheckGetRateLimiterPriority(compaction_job);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckGetRateLimiterPriority(CompactionJob& compaction_job) {
|
||||
// When the state from WriteController is normal.
|
||||
ASSERT_EQ(compaction_job.GetRateLimiterPriority(), Env::IO_LOW);
|
||||
|
||||
WriteController* write_controller =
|
||||
compaction_job.versions_->GetColumnFamilySet()->write_controller();
|
||||
|
||||
{
|
||||
// When the state from WriteController is Delayed.
|
||||
std::unique_ptr<WriteControllerToken> delay_token =
|
||||
write_controller->GetDelayToken(1000000);
|
||||
ASSERT_EQ(compaction_job.GetRateLimiterPriority(), Env::IO_USER);
|
||||
}
|
||||
|
||||
{
|
||||
// When the state from WriteController is Stopped.
|
||||
std::unique_ptr<WriteControllerToken> stop_token =
|
||||
write_controller->GetStopToken();
|
||||
ASSERT_EQ(compaction_job.GetRateLimiterPriority(), Env::IO_USER);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Env> env_guard_;
|
||||
@@ -619,16 +416,15 @@ class CompactionJobTestBase : public testing::Test {
|
||||
ErrorHandler error_handler_;
|
||||
std::string full_history_ts_low_;
|
||||
const std::function<std::string(uint64_t)> encode_u64_ts_;
|
||||
bool test_io_priority_;
|
||||
};
|
||||
|
||||
// TODO(icanadi) Make it simpler once we mock out VersionSet
|
||||
class CompactionJobTest : public CompactionJobTestBase {
|
||||
public:
|
||||
CompactionJobTest()
|
||||
: CompactionJobTestBase(
|
||||
test::PerThreadDBPath("compaction_job_test"), BytewiseComparator(),
|
||||
[](uint64_t /*ts*/) { return ""; }, false) {}
|
||||
: CompactionJobTestBase(test::PerThreadDBPath("compaction_job_test"),
|
||||
BytewiseComparator(),
|
||||
[](uint64_t /*ts*/) { return ""; }) {}
|
||||
};
|
||||
|
||||
TEST_F(CompactionJobTest, Simple) {
|
||||
@@ -1094,10 +890,10 @@ TEST_F(CompactionJobTest, MultiSingleDelete) {
|
||||
// -> Snapshot Put
|
||||
// K: SDel SDel Put SDel Put Put Snapshot SDel Put SDel SDel Put SDel
|
||||
// -> Snapshot Put Snapshot SDel
|
||||
// L: SDel Put SDel Put SDel Snapshot SDel Put SDel SDel Put SDel
|
||||
// -> Snapshot SDel Put SDel
|
||||
// M: (Put) SDel Put SDel Put SDel Snapshot Put SDel SDel Put SDel SDel
|
||||
// -> SDel Snapshot Put SDel
|
||||
// L: SDel Put Del Put SDel Snapshot Del Put Del SDel Put SDel
|
||||
// -> Snapshot SDel
|
||||
// M: (Put) SDel Put Del Put SDel Snapshot Put Del SDel Put SDel Del
|
||||
// -> SDel Snapshot Del
|
||||
NewDB();
|
||||
|
||||
auto file1 = mock::MakeMockFile({
|
||||
@@ -1128,14 +924,14 @@ TEST_F(CompactionJobTest, MultiSingleDelete) {
|
||||
{KeyStr("L", 16U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("L", 15U, kTypeValue), "val"},
|
||||
{KeyStr("L", 14U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("L", 13U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("L", 13U, kTypeDeletion), ""},
|
||||
{KeyStr("L", 12U, kTypeValue), "val"},
|
||||
{KeyStr("L", 11U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("M", 16U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("L", 11U, kTypeDeletion), ""},
|
||||
{KeyStr("M", 16U, kTypeDeletion), ""},
|
||||
{KeyStr("M", 15U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("M", 14U, kTypeValue), "val"},
|
||||
{KeyStr("M", 13U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("M", 12U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("M", 12U, kTypeDeletion), ""},
|
||||
{KeyStr("M", 11U, kTypeValue), "val"},
|
||||
});
|
||||
AddMockFile(file1);
|
||||
@@ -1176,12 +972,12 @@ TEST_F(CompactionJobTest, MultiSingleDelete) {
|
||||
{KeyStr("K", 1U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("L", 5U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("L", 4U, kTypeValue), "val"},
|
||||
{KeyStr("L", 3U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("L", 3U, kTypeDeletion), ""},
|
||||
{KeyStr("L", 2U, kTypeValue), "val"},
|
||||
{KeyStr("L", 1U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("M", 10U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("M", 7U, kTypeValue), "val"},
|
||||
{KeyStr("M", 5U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("M", 5U, kTypeDeletion), ""},
|
||||
{KeyStr("M", 4U, kTypeValue), "val"},
|
||||
{KeyStr("M", 3U, kTypeSingleDeletion), ""},
|
||||
});
|
||||
@@ -1223,9 +1019,7 @@ TEST_F(CompactionJobTest, MultiSingleDelete) {
|
||||
{KeyStr("K", 8U, kTypeValue), "val3"},
|
||||
{KeyStr("L", 16U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("L", 15U, kTypeValue), ""},
|
||||
{KeyStr("L", 11U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("M", 15U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("M", 14U, kTypeValue), ""},
|
||||
{KeyStr("M", 16U, kTypeDeletion), ""},
|
||||
{KeyStr("M", 3U, kTypeSingleDeletion), ""}});
|
||||
|
||||
SetLastSequence(22U);
|
||||
@@ -1311,21 +1105,6 @@ TEST_F(CompactionJobTest, OldestBlobFileNumber) {
|
||||
/* expected_oldest_blob_file_number */ 19);
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, NoEnforceSingleDeleteContract) {
|
||||
db_options_.enforce_single_del_contracts = false;
|
||||
NewDB();
|
||||
|
||||
auto file =
|
||||
mock::MakeMockFile({{KeyStr("a", 4U, kTypeSingleDeletion), ""},
|
||||
{KeyStr("a", 3U, kTypeDeletion), "dontcare"}});
|
||||
AddMockFile(file);
|
||||
SetLastSequence(4U);
|
||||
|
||||
auto expected_results = mock::MakeMockFile();
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
RunCompaction({files}, expected_results);
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, InputSerialization) {
|
||||
// Setup a random CompactionServiceInput
|
||||
CompactionServiceInput input;
|
||||
@@ -1431,14 +1210,13 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
result.status =
|
||||
status_list.at(rnd.Uniform(static_cast<int>(status_list.size())));
|
||||
while (!rnd.OneIn(10)) {
|
||||
UniqueId64x2 id{rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX)};
|
||||
result.output_files.emplace_back(
|
||||
rnd.RandomString(rnd.Uniform(kStrMaxLen)), rnd64.Uniform(UINT64_MAX),
|
||||
rnd64.Uniform(UINT64_MAX),
|
||||
rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen)),
|
||||
rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen)),
|
||||
rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX),
|
||||
rnd64.Uniform(UINT64_MAX), rnd.OneIn(2), id);
|
||||
rnd64.Uniform(UINT64_MAX), rnd.OneIn(2));
|
||||
}
|
||||
result.output_level = rnd.Uniform(10);
|
||||
result.output_path = rnd.RandomString(rnd.Uniform(kStrMaxLen));
|
||||
@@ -1466,16 +1244,6 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
ASSERT_FALSE(deserialized1.TEST_Equals(&result, &mismatch));
|
||||
ASSERT_EQ(mismatch, "stats.num_input_files");
|
||||
|
||||
// Test unique id mismatch
|
||||
if (!result.output_files.empty()) {
|
||||
CompactionServiceResult deserialized_tmp;
|
||||
ASSERT_OK(CompactionServiceResult::Read(output, &deserialized_tmp));
|
||||
deserialized_tmp.output_files[0].unique_id[0] += 1;
|
||||
ASSERT_FALSE(deserialized_tmp.TEST_Equals(&result, &mismatch));
|
||||
ASSERT_EQ(mismatch, "output_files.unique_id");
|
||||
deserialized_tmp.status.PermitUncheckedError();
|
||||
}
|
||||
|
||||
// Test unknown field
|
||||
CompactionServiceResult deserialized2;
|
||||
output.clear();
|
||||
@@ -1518,13 +1286,12 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CompactionJobTimestampTest : public CompactionJobTestBase {
|
||||
public:
|
||||
CompactionJobTimestampTest()
|
||||
: CompactionJobTestBase(test::PerThreadDBPath("compaction_job_ts_test"),
|
||||
test::BytewiseComparatorWithU64TsWrapper(),
|
||||
test::EncodeInt, false) {}
|
||||
test::EncodeInt) {}
|
||||
};
|
||||
|
||||
TEST_F(CompactionJobTimestampTest, GCDisabled) {
|
||||
@@ -1640,69 +1407,6 @@ TEST_F(CompactionJobTimestampTest, SomeKeysExpired) {
|
||||
RunCompaction({files}, expected_results);
|
||||
}
|
||||
|
||||
// The io priority of the compaction reads and writes are different from
|
||||
// other DB reads and writes. To prepare the compaction input files, use the
|
||||
// default filesystem from Env. To test the io priority of the compaction
|
||||
// reads and writes, db_options_.fs is set as MockTestFileSystem.
|
||||
class CompactionJobIOPriorityTest : public CompactionJobTestBase {
|
||||
public:
|
||||
CompactionJobIOPriorityTest()
|
||||
: CompactionJobTestBase(
|
||||
test::PerThreadDBPath("compaction_job_io_priority_test"),
|
||||
BytewiseComparator(), [](uint64_t /*ts*/) { return ""; }, true) {}
|
||||
};
|
||||
|
||||
TEST_F(CompactionJobIOPriorityTest, WriteControllerStateNormal) {
|
||||
// When the state from WriteController is normal.
|
||||
NewDB();
|
||||
mock::KVVector expected_results = CreateTwoFiles(false);
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
auto files = cfd->current()->storage_info()->LevelFiles(0);
|
||||
ASSERT_EQ(2U, files.size());
|
||||
RunCompaction({files}, expected_results, {}, kMaxSequenceNumber, 1, false,
|
||||
kInvalidBlobFileNumber, false, Env::IO_LOW, Env::IO_LOW);
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobIOPriorityTest, WriteControllerStateDelayed) {
|
||||
// When the state from WriteController is Delayed.
|
||||
NewDB();
|
||||
mock::KVVector expected_results = CreateTwoFiles(false);
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
auto files = cfd->current()->storage_info()->LevelFiles(0);
|
||||
ASSERT_EQ(2U, files.size());
|
||||
{
|
||||
std::unique_ptr<WriteControllerToken> delay_token =
|
||||
write_controller_.GetDelayToken(1000000);
|
||||
RunCompaction({files}, expected_results, {}, kMaxSequenceNumber, 1, false,
|
||||
kInvalidBlobFileNumber, false, Env::IO_USER, Env::IO_USER);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobIOPriorityTest, WriteControllerStateStalled) {
|
||||
// When the state from WriteController is Stalled.
|
||||
NewDB();
|
||||
mock::KVVector expected_results = CreateTwoFiles(false);
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
auto files = cfd->current()->storage_info()->LevelFiles(0);
|
||||
ASSERT_EQ(2U, files.size());
|
||||
{
|
||||
std::unique_ptr<WriteControllerToken> stop_token =
|
||||
write_controller_.GetStopToken();
|
||||
RunCompaction({files}, expected_results, {}, kMaxSequenceNumber, 1, false,
|
||||
kInvalidBlobFileNumber, false, Env::IO_USER, Env::IO_USER);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobIOPriorityTest, GetRateLimiterPriority) {
|
||||
NewDB();
|
||||
mock::KVVector expected_results = CreateTwoFiles(false);
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
auto files = cfd->current()->storage_info()->LevelFiles(0);
|
||||
ASSERT_EQ(2U, files.size());
|
||||
RunCompaction({files}, expected_results, {}, kMaxSequenceNumber, 1, false,
|
||||
kInvalidBlobFileNumber, true, Env::IO_LOW, Env::IO_LOW);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -65,7 +65,7 @@ bool FindIntraL0Compaction(const std::vector<FileMetaData*>& level_files,
|
||||
size_t compact_bytes = static_cast<size_t>(level_files[start]->fd.file_size);
|
||||
uint64_t compensated_compact_bytes =
|
||||
level_files[start]->compensated_file_size;
|
||||
size_t compact_bytes_per_del_file = std::numeric_limits<size_t>::max();
|
||||
size_t compact_bytes_per_del_file = port::kMaxSizet;
|
||||
// Compaction range will be [start, limit).
|
||||
size_t limit;
|
||||
// Pull in files until the amount of compaction work per deleted file begins
|
||||
@@ -401,7 +401,7 @@ Status CompactionPicker::GetCompactionInputsFromFileNumbers(
|
||||
"Cannot find matched SST files for the following file numbers:");
|
||||
for (auto fn : *input_set) {
|
||||
message += " ";
|
||||
message += std::to_string(fn);
|
||||
message += ToString(fn);
|
||||
}
|
||||
return Status::InvalidArgument(message);
|
||||
}
|
||||
@@ -640,11 +640,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
GetCompressionType(vstorage, mutable_cf_options, output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
/* grandparents */ {}, /* is manual */ true, trim_ts, /* score */ -1,
|
||||
/* deletion_compaction */ false, CompactionReason::kUnknown,
|
||||
compact_range_options.blob_garbage_collection_policy,
|
||||
compact_range_options.blob_garbage_collection_age_cutoff);
|
||||
|
||||
/* grandparents */ {}, /* is manual */ true, trim_ts);
|
||||
RegisterCompaction(c);
|
||||
vstorage->ComputeCompactionScore(ioptions_, mutable_cf_options);
|
||||
return c;
|
||||
@@ -721,7 +717,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
// files that are created during the current compaction.
|
||||
if (compact_range_options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kForceOptimized &&
|
||||
max_file_num_to_ignore != std::numeric_limits<uint64_t>::max()) {
|
||||
max_file_num_to_ignore != port::kMaxUint64) {
|
||||
assert(input_level == output_level);
|
||||
// inputs_shrunk holds a continuous subset of input files which were all
|
||||
// created before the current manual compaction
|
||||
@@ -822,10 +818,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
vstorage->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
std::move(grandparents), /* is manual */ true, trim_ts, /* score */ -1,
|
||||
/* deletion_compaction */ false, CompactionReason::kUnknown,
|
||||
compact_range_options.blob_garbage_collection_policy,
|
||||
compact_range_options.blob_garbage_collection_age_cutoff);
|
||||
std::move(grandparents),
|
||||
/* is manual compaction */ true, trim_ts);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionPicker::CompactRange:Return", compaction);
|
||||
RegisterCompaction(compaction);
|
||||
@@ -1010,14 +1004,14 @@ Status CompactionPicker::SanitizeCompactionInputFiles(
|
||||
return Status::InvalidArgument(
|
||||
"Output level for column family " + cf_meta.name +
|
||||
" must between [0, " +
|
||||
std::to_string(cf_meta.levels[cf_meta.levels.size() - 1].level) + "].");
|
||||
ToString(cf_meta.levels[cf_meta.levels.size() - 1].level) + "].");
|
||||
}
|
||||
|
||||
if (output_level > MaxOutputLevel()) {
|
||||
return Status::InvalidArgument(
|
||||
"Exceed the maximum output level defined by "
|
||||
"the current compaction algorithm --- " +
|
||||
std::to_string(MaxOutputLevel()));
|
||||
ToString(MaxOutputLevel()));
|
||||
}
|
||||
|
||||
if (output_level < 0) {
|
||||
@@ -1067,8 +1061,8 @@ Status CompactionPicker::SanitizeCompactionInputFiles(
|
||||
return Status::InvalidArgument(
|
||||
"Cannot compact file to up level, input file: " +
|
||||
MakeTableFileName("", file_num) + " level " +
|
||||
std::to_string(input_file_level) + " > output level " +
|
||||
std::to_string(output_level));
|
||||
ToString(input_file_level) + " > output level " +
|
||||
ToString(output_level));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -504,7 +504,7 @@ bool LevelCompactionBuilder::PickIntraL0Compaction() {
|
||||
return false;
|
||||
}
|
||||
return FindIntraL0Compaction(level_files, kMinFilesForIntraL0Compaction,
|
||||
std::numeric_limits<uint64_t>::max(),
|
||||
port::kMaxUint64,
|
||||
mutable_cf_options_.max_compaction_bytes,
|
||||
&start_level_inputs_, earliest_mem_seqno_);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "db/compaction/compaction_picker_level.h"
|
||||
#include "db/compaction/compaction_picker_universal.h"
|
||||
#include "db/compaction/file_pri.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -116,7 +115,7 @@ class CompactionPickerTest : public testing::Test {
|
||||
largest_seq, marked_for_compact, temperature, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName,
|
||||
kDisableUserTimestamp, kDisableUserTimestamp, kNullUniqueId64x2);
|
||||
kDisableUserTimestamp, kDisableUserTimestamp);
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
f->oldest_ancester_time = oldest_ancestor_time;
|
||||
@@ -274,9 +273,9 @@ TEST_F(CompactionPickerTest, NeedsCompactionLevel) {
|
||||
// start a brand new version in each test.
|
||||
NewVersionStorage(kLevels, kCompactionStyleLevel);
|
||||
for (int i = 0; i < file_count; ++i) {
|
||||
Add(level, i, std::to_string((i + 100) * 1000).c_str(),
|
||||
std::to_string((i + 100) * 1000 + 999).c_str(), file_size, 0,
|
||||
i * 100, i * 100 + 99);
|
||||
Add(level, i, ToString((i + 100) * 1000).c_str(),
|
||||
ToString((i + 100) * 1000 + 999).c_str(),
|
||||
file_size, 0, i * 100, i * 100 + 99);
|
||||
}
|
||||
UpdateVersionStorageInfo();
|
||||
ASSERT_EQ(vstorage_->CompactionScoreLevel(0), level);
|
||||
@@ -440,8 +439,8 @@ TEST_F(CompactionPickerTest, NeedsCompactionUniversal) {
|
||||
for (int i = 1;
|
||||
i <= mutable_cf_options_.level0_file_num_compaction_trigger * 2; ++i) {
|
||||
NewVersionStorage(1, kCompactionStyleUniversal);
|
||||
Add(0, i, std::to_string((i + 100) * 1000).c_str(),
|
||||
std::to_string((i + 100) * 1000 + 999).c_str(), 1000000, 0, i * 100,
|
||||
Add(0, i, ToString((i + 100) * 1000).c_str(),
|
||||
ToString((i + 100) * 1000 + 999).c_str(), 1000000, 0, i * 100,
|
||||
i * 100 + 99);
|
||||
UpdateVersionStorageInfo();
|
||||
ASSERT_EQ(level_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
@@ -853,17 +852,17 @@ TEST_F(CompactionPickerTest, UniversalIncrementalSpace4) {
|
||||
// L3: (1101, 1180) (1201, 1280) ... (7901, 7908)
|
||||
// L4: (1130, 1150) (1160, 1210) (1230, 1250) (1260 1310) ... (7960, 8010)
|
||||
for (int i = 11; i < 79; i++) {
|
||||
Add(3, 100 + i * 3, std::to_string(i * 100).c_str(),
|
||||
std::to_string(i * 100 + 80).c_str(), kFileSize, 0, 200, 251);
|
||||
Add(3, 100 + i * 3, ToString(i * 100).c_str(),
|
||||
ToString(i * 100 + 80).c_str(), kFileSize, 0, 200, 251);
|
||||
// Add a tie breaker
|
||||
if (i == 66) {
|
||||
Add(3, 10000U, "6690", "6699", kFileSize, 0, 200, 251);
|
||||
}
|
||||
|
||||
Add(4, 100 + i * 3 + 1, std::to_string(i * 100 + 30).c_str(),
|
||||
std::to_string(i * 100 + 50).c_str(), kFileSize, 0, 200, 251);
|
||||
Add(4, 100 + i * 3 + 2, std::to_string(i * 100 + 60).c_str(),
|
||||
std::to_string(i * 100 + 110).c_str(), kFileSize, 0, 200, 251);
|
||||
Add(4, 100 + i * 3 + 1, ToString(i * 100 + 30).c_str(),
|
||||
ToString(i * 100 + 50).c_str(), kFileSize, 0, 200, 251);
|
||||
Add(4, 100 + i * 3 + 2, ToString(i * 100 + 60).c_str(),
|
||||
ToString(i * 100 + 110).c_str(), kFileSize, 0, 200, 251);
|
||||
}
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
@@ -900,14 +899,14 @@ TEST_F(CompactionPickerTest, UniversalIncrementalSpace5) {
|
||||
// L3: (1101, 1180) (1201, 1280) ... (7901, 7908)
|
||||
// L4: (1130, 1150) (1160, 1210) (1230, 1250) (1260 1310) ... (7960, 8010)
|
||||
for (int i = 11; i < 70; i++) {
|
||||
Add(3, 100 + i * 3, std::to_string(i * 100).c_str(),
|
||||
std::to_string(i * 100 + 80).c_str(),
|
||||
Add(3, 100 + i * 3, ToString(i * 100).c_str(),
|
||||
ToString(i * 100 + 80).c_str(),
|
||||
i % 10 == 9 ? kFileSize * 100 : kFileSize, 0, 200, 251);
|
||||
|
||||
Add(4, 100 + i * 3 + 1, std::to_string(i * 100 + 30).c_str(),
|
||||
std::to_string(i * 100 + 50).c_str(), kFileSize, 0, 200, 251);
|
||||
Add(4, 100 + i * 3 + 2, std::to_string(i * 100 + 60).c_str(),
|
||||
std::to_string(i * 100 + 110).c_str(), kFileSize, 0, 200, 251);
|
||||
Add(4, 100 + i * 3 + 1, ToString(i * 100 + 30).c_str(),
|
||||
ToString(i * 100 + 50).c_str(), kFileSize, 0, 200, 251);
|
||||
Add(4, 100 + i * 3 + 2, ToString(i * 100 + 60).c_str(),
|
||||
ToString(i * 100 + 110).c_str(), kFileSize, 0, 200, 251);
|
||||
}
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
@@ -942,8 +941,8 @@ TEST_F(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
// size of L0 files.
|
||||
for (int i = 1; i <= kFileCount; ++i) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
Add(0, i, std::to_string((i + 100) * 1000).c_str(),
|
||||
std::to_string((i + 100) * 1000 + 999).c_str(), kFileSize, 0, i * 100,
|
||||
Add(0, i, ToString((i + 100) * 1000).c_str(),
|
||||
ToString((i + 100) * 1000 + 999).c_str(), kFileSize, 0, i * 100,
|
||||
i * 100 + 99);
|
||||
UpdateVersionStorageInfo();
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
@@ -2654,8 +2653,8 @@ TEST_F(CompactionPickerTest, UniversalMarkedManualCompaction) {
|
||||
universal_compaction_picker.CompactRange(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
ColumnFamilyData::kCompactAllLevels, 6, CompactRangeOptions(),
|
||||
nullptr, nullptr, &manual_end, &manual_conflict,
|
||||
std::numeric_limits<uint64_t>::max(), ""));
|
||||
nullptr, nullptr, &manual_end, &manual_conflict, port::kMaxUint64,
|
||||
""));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
|
||||
|
||||
@@ -1371,7 +1371,7 @@ Compaction* UniversalCompactionBuilder::PickPeriodicCompaction() {
|
||||
|
||||
uint64_t UniversalCompactionBuilder::GetMaxOverlappingBytes() const {
|
||||
if (!mutable_cf_options_.compaction_options_universal.incremental) {
|
||||
return std::numeric_limits<uint64_t>::max();
|
||||
return port::kMaxUint64;
|
||||
} else {
|
||||
// Try to align cutting boundary with files at the next level if the
|
||||
// file isn't end up with 1/2 of target size, or it would overlap
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -16,17 +15,13 @@ class MyTestCompactionService : public CompactionService {
|
||||
MyTestCompactionService(
|
||||
std::string db_path, Options& options,
|
||||
std::shared_ptr<Statistics>& statistics,
|
||||
std::vector<std::shared_ptr<EventListener>>& listeners,
|
||||
std::vector<std::shared_ptr<TablePropertiesCollectorFactory>>
|
||||
table_properties_collector_factories)
|
||||
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),
|
||||
listeners_(listeners),
|
||||
table_properties_collector_factories_(
|
||||
std::move(table_properties_collector_factories)) {}
|
||||
listeners_(listeners) {}
|
||||
|
||||
static const char* kClassName() { return "MyTestCompactionService"; }
|
||||
|
||||
@@ -83,16 +78,12 @@ class MyTestCompactionService : public CompactionService {
|
||||
options_override.listeners = listeners_;
|
||||
}
|
||||
|
||||
if (!table_properties_collector_factories_.empty()) {
|
||||
options_override.table_properties_collector_factories =
|
||||
table_properties_collector_factories_;
|
||||
}
|
||||
|
||||
OpenAndCompactOptions options;
|
||||
options.canceled = &canceled_;
|
||||
|
||||
Status s = DB::OpenAndCompact(
|
||||
options, db_path_, db_path_ + "/" + std::to_string(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_;
|
||||
@@ -151,8 +142,6 @@ class MyTestCompactionService : public CompactionService {
|
||||
bool is_override_wait_result_ = false;
|
||||
std::string override_wait_result_;
|
||||
std::vector<std::shared_ptr<EventListener>> listeners_;
|
||||
std::vector<std::shared_ptr<TablePropertiesCollectorFactory>>
|
||||
table_properties_collector_factories_;
|
||||
std::atomic_bool canceled_{false};
|
||||
};
|
||||
|
||||
@@ -169,8 +158,7 @@ class CompactionServiceTest : public DBTestBase {
|
||||
compactor_statistics_ = CreateDBStatistics();
|
||||
|
||||
compaction_service_ = std::make_shared<MyTestCompactionService>(
|
||||
dbname_, *options, compactor_statistics_, remote_listeners,
|
||||
remote_table_properties_collector_factories);
|
||||
dbname_, *options, compactor_statistics_, remote_listeners);
|
||||
options->compaction_service = compaction_service_;
|
||||
DestroyAndReopen(*options);
|
||||
}
|
||||
@@ -189,7 +177,7 @@ class CompactionServiceTest : public DBTestBase {
|
||||
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" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -199,7 +187,7 @@ class CompactionServiceTest : public DBTestBase {
|
||||
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" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -211,16 +199,14 @@ class CompactionServiceTest : public DBTestBase {
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
ASSERT_EQ(result, "value" + ToString(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
ASSERT_EQ(result, "value_new" + ToString(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<EventListener>> remote_listeners;
|
||||
std::vector<std::shared_ptr<TablePropertiesCollectorFactory>>
|
||||
remote_table_properties_collector_factories;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Statistics> compactor_statistics_;
|
||||
@@ -238,7 +224,7 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
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" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -246,7 +232,7 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
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" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -256,9 +242,9 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
ASSERT_EQ(result, "value" + ToString(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
ASSERT_EQ(result, "value_new" + ToString(i));
|
||||
}
|
||||
}
|
||||
auto my_cs = GetCompactionService();
|
||||
@@ -289,23 +275,13 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
auto s = static_cast<Status*>(status);
|
||||
*s = Status::Aborted("MyTestCompactionService failed to compact!");
|
||||
});
|
||||
|
||||
// tracking success unique id verification
|
||||
std::atomic_int verify_passed{0};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"Version::VerifySstUniqueIds::Passed", [&](void* arg) {
|
||||
// override job status
|
||||
auto id = static_cast<UniqueId64x2*>(arg);
|
||||
assert(*id != kNullUniqueId64x2);
|
||||
verify_passed++;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Status s;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
s = Put(Key(key_id), "value_new" + std::to_string(key_id));
|
||||
s = Put(Key(key_id), "value_new" + ToString(key_id));
|
||||
if (s.IsAborted()) {
|
||||
break;
|
||||
}
|
||||
@@ -323,12 +299,6 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(s.IsAborted());
|
||||
|
||||
// Test verification
|
||||
ASSERT_EQ(verify_passed, 0);
|
||||
options.verify_sst_unique_id_in_manifest = true;
|
||||
Reopen(options);
|
||||
ASSERT_GT(verify_passed, 0);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, ManualCompaction) {
|
||||
@@ -498,7 +468,7 @@ TEST_F(CompactionServiceTest, CompactionFilter) {
|
||||
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" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -506,7 +476,7 @@ TEST_F(CompactionServiceTest, CompactionFilter) {
|
||||
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" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -520,9 +490,9 @@ TEST_F(CompactionServiceTest, CompactionFilter) {
|
||||
if (i > 5 && i <= 105) {
|
||||
ASSERT_EQ(result, "NOT_FOUND");
|
||||
} else if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
ASSERT_EQ(result, "value" + ToString(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
ASSERT_EQ(result, "value_new" + ToString(i));
|
||||
}
|
||||
}
|
||||
auto my_cs = GetCompactionService();
|
||||
@@ -577,9 +547,9 @@ TEST_F(CompactionServiceTest, ConcurrentCompaction) {
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
ASSERT_EQ(result, "value" + ToString(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
ASSERT_EQ(result, "value_new" + ToString(i));
|
||||
}
|
||||
}
|
||||
auto my_cs = GetCompactionService();
|
||||
@@ -594,7 +564,7 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
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" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -602,7 +572,7 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
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" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -647,7 +617,7 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
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" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -655,7 +625,7 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -683,7 +653,7 @@ TEST_F(CompactionServiceTest, FallbackLocalAuto) {
|
||||
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" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -691,7 +661,7 @@ TEST_F(CompactionServiceTest, FallbackLocalAuto) {
|
||||
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" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -701,9 +671,9 @@ TEST_F(CompactionServiceTest, FallbackLocalAuto) {
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
ASSERT_EQ(result, "value" + ToString(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
ASSERT_EQ(result, "value_new" + ToString(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -826,7 +796,7 @@ TEST_F(CompactionServiceTest, RemoteEventListener) {
|
||||
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" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -834,7 +804,7 @@ TEST_F(CompactionServiceTest, RemoteEventListener) {
|
||||
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" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -851,103 +821,13 @@ TEST_F(CompactionServiceTest, RemoteEventListener) {
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
ASSERT_EQ(result, "value" + ToString(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
ASSERT_EQ(result, "value_new" + ToString(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, TablePropertiesCollector) {
|
||||
const static std::string kUserPropertyName = "TestCount";
|
||||
|
||||
class TablePropertiesCollectorTest : public TablePropertiesCollector {
|
||||
public:
|
||||
Status Finish(UserCollectedProperties* properties) override {
|
||||
*properties = UserCollectedProperties{
|
||||
{kUserPropertyName, std::to_string(count_)},
|
||||
};
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
UserCollectedProperties GetReadableProperties() const override {
|
||||
return UserCollectedProperties();
|
||||
}
|
||||
|
||||
const char* Name() const override { return "TablePropertiesCollectorTest"; }
|
||||
|
||||
Status AddUserKey(const Slice& /*user_key*/, const Slice& /*value*/,
|
||||
EntryType /*type*/, SequenceNumber /*seq*/,
|
||||
uint64_t /*file_size*/) override {
|
||||
count_++;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t count_ = 0;
|
||||
};
|
||||
|
||||
class TablePropertiesCollectorFactoryTest
|
||||
: public TablePropertiesCollectorFactory {
|
||||
public:
|
||||
TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollectorFactory::Context /*context*/) override {
|
||||
return new TablePropertiesCollectorTest();
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "TablePropertiesCollectorFactoryTest";
|
||||
}
|
||||
};
|
||||
|
||||
auto factory = new TablePropertiesCollectorFactoryTest();
|
||||
remote_table_properties_collector_factories.emplace_back(factory);
|
||||
|
||||
const int kNumSst = 3;
|
||||
const int kLevel0Trigger = 4;
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kLevel0Trigger;
|
||||
ReopenWithCompactionService(&options);
|
||||
|
||||
// generate a few SSTs locally which should not have user property
|
||||
for (int i = 0; i < kNumSst; i++) {
|
||||
for (int j = 0; j < 100; j++) {
|
||||
ASSERT_OK(Put(Key(i * 10 + j), "value"));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
TablePropertiesCollection fname_to_props;
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(&fname_to_props));
|
||||
for (const auto& file_props : fname_to_props) {
|
||||
auto properties = file_props.second->user_collected_properties;
|
||||
auto it = properties.find(kUserPropertyName);
|
||||
ASSERT_EQ(it, properties.end());
|
||||
}
|
||||
|
||||
// trigger compaction
|
||||
for (int i = kNumSst; i < kLevel0Trigger; i++) {
|
||||
for (int j = 0; j < 100; j++) {
|
||||
ASSERT_OK(Put(Key(i * 10 + j), "value"));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(&fname_to_props));
|
||||
|
||||
bool has_user_property = false;
|
||||
for (const auto& file_props : fname_to_props) {
|
||||
auto properties = file_props.second->user_collected_properties;
|
||||
auto it = properties.find(kUserPropertyName);
|
||||
if (it != properties.end()) {
|
||||
has_user_property = true;
|
||||
ASSERT_GT(std::stoi(it->second), 0);
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(has_user_property);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+15
-33
@@ -397,7 +397,7 @@ TEST_P(ComparatorDBTest, DoubleComparator) {
|
||||
for (uint32_t j = 0; j < divide_order; j++) {
|
||||
to_divide *= 10.0;
|
||||
}
|
||||
source_strings.push_back(std::to_string(r / to_divide));
|
||||
source_strings.push_back(ToString(r / to_divide));
|
||||
}
|
||||
|
||||
DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 200, 1000, 66);
|
||||
@@ -449,85 +449,67 @@ TEST_P(ComparatorDBTest, TwoStrComparator) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void VerifyNotSuccessor(const Slice& s, const Slice& t) {
|
||||
auto bc = BytewiseComparator();
|
||||
auto rbc = ReverseBytewiseComparator();
|
||||
ASSERT_FALSE(bc->IsSameLengthImmediateSuccessor(s, t));
|
||||
ASSERT_FALSE(rbc->IsSameLengthImmediateSuccessor(s, t));
|
||||
ASSERT_FALSE(bc->IsSameLengthImmediateSuccessor(t, s));
|
||||
ASSERT_FALSE(rbc->IsSameLengthImmediateSuccessor(t, s));
|
||||
}
|
||||
|
||||
void VerifySuccessor(const Slice& s, const Slice& t) {
|
||||
auto bc = BytewiseComparator();
|
||||
auto rbc = ReverseBytewiseComparator();
|
||||
ASSERT_TRUE(bc->IsSameLengthImmediateSuccessor(s, t));
|
||||
ASSERT_FALSE(rbc->IsSameLengthImmediateSuccessor(s, t));
|
||||
ASSERT_FALSE(bc->IsSameLengthImmediateSuccessor(t, s));
|
||||
// Should be true but that increases exposure to a design bug in
|
||||
// auto_prefix_mode, so currently set to FALSE
|
||||
ASSERT_FALSE(rbc->IsSameLengthImmediateSuccessor(t, s));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_P(ComparatorDBTest, IsSameLengthImmediateSuccessor) {
|
||||
{
|
||||
// different length
|
||||
Slice s("abcxy");
|
||||
Slice t("abcxyz");
|
||||
VerifyNotSuccessor(s, t);
|
||||
ASSERT_FALSE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
|
||||
}
|
||||
{
|
||||
Slice s("abcxyz");
|
||||
Slice t("abcxy");
|
||||
VerifyNotSuccessor(s, t);
|
||||
ASSERT_FALSE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
|
||||
}
|
||||
{
|
||||
// not last byte different
|
||||
Slice s("abc1xyz");
|
||||
Slice t("abc2xyz");
|
||||
VerifyNotSuccessor(s, t);
|
||||
ASSERT_FALSE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
|
||||
}
|
||||
{
|
||||
// same string
|
||||
Slice s("abcxyz");
|
||||
Slice t("abcxyz");
|
||||
VerifyNotSuccessor(s, t);
|
||||
ASSERT_FALSE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
|
||||
}
|
||||
{
|
||||
Slice s("abcxy");
|
||||
Slice t("abcxz");
|
||||
VerifySuccessor(s, t);
|
||||
ASSERT_TRUE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
|
||||
}
|
||||
{
|
||||
Slice s("abcxz");
|
||||
Slice t("abcxy");
|
||||
ASSERT_FALSE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
|
||||
}
|
||||
{
|
||||
const char s_array[] = "\x50\x8a\xac";
|
||||
const char t_array[] = "\x50\x8a\xad";
|
||||
Slice s(s_array);
|
||||
Slice t(t_array);
|
||||
VerifySuccessor(s, t);
|
||||
ASSERT_TRUE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
|
||||
}
|
||||
{
|
||||
const char s_array[] = "\x50\x8a\xff";
|
||||
const char t_array[] = "\x50\x8b\x00";
|
||||
Slice s(s_array, 3);
|
||||
Slice t(t_array, 3);
|
||||
VerifySuccessor(s, t);
|
||||
ASSERT_TRUE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
|
||||
}
|
||||
{
|
||||
const char s_array[] = "\x50\x8a\xff\xff";
|
||||
const char t_array[] = "\x50\x8b\x00\x00";
|
||||
Slice s(s_array, 4);
|
||||
Slice t(t_array, 4);
|
||||
VerifySuccessor(s, t);
|
||||
ASSERT_TRUE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
|
||||
}
|
||||
{
|
||||
const char s_array[] = "\x50\x8a\xff\xff";
|
||||
const char t_array[] = "\x50\x8b\x00\x01";
|
||||
Slice s(s_array, 4);
|
||||
Slice t(t_array, 4);
|
||||
VerifyNotSuccessor(s, t);
|
||||
ASSERT_FALSE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-8
@@ -40,8 +40,7 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
Status VerifySstFileChecksum(const Options& options,
|
||||
const EnvOptions& env_options,
|
||||
const ReadOptions& read_options,
|
||||
const std::string& file_path,
|
||||
const SequenceNumber& largest_seqno) {
|
||||
const std::string& file_path) {
|
||||
std::unique_ptr<FSRandomAccessFile> file;
|
||||
uint64_t file_size;
|
||||
InternalKeyComparator internal_comparator(options.comparator);
|
||||
@@ -62,13 +61,12 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
nullptr /* stats */, 0 /* hist_type */, nullptr /* file_read_hist */,
|
||||
ioptions.rate_limiter.get()));
|
||||
const bool kImmortal = true;
|
||||
auto reader_options = TableReaderOptions(
|
||||
ioptions, options.prefix_extractor, env_options, internal_comparator,
|
||||
false /* skip_filters */, !kImmortal, false /* force_direct_prefetch */,
|
||||
-1 /* level */);
|
||||
reader_options.largest_seqno = largest_seqno;
|
||||
s = ioptions.table_factory->NewTableReader(
|
||||
reader_options, std::move(file_reader), file_size, &table_reader,
|
||||
TableReaderOptions(ioptions, options.prefix_extractor, env_options,
|
||||
internal_comparator, false /* skip_filters */,
|
||||
!kImmortal, false /* force_direct_prefetch */,
|
||||
-1 /* level */),
|
||||
std::move(file_reader), file_size, &table_reader,
|
||||
false /* prefetch_index_and_filter_in_cache */);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
|
||||
+39
-267
@@ -308,7 +308,7 @@ class CorruptionTest : public testing::Test {
|
||||
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 = 0;
|
||||
new_size = old_size;
|
||||
}
|
||||
ASSERT_OK(test::TruncateFile(env_, path, new_size));
|
||||
}
|
||||
@@ -354,7 +354,6 @@ TEST_F(CorruptionTest, PostPITRCorruptionWALsRetained) {
|
||||
options_.create_missing_column_families = true;
|
||||
std::vector<ColumnFamilyHandle*> cfhs;
|
||||
ASSERT_OK(DB::Open(options_, dbname_, cf_descs, &cfhs, &db_));
|
||||
assert(db_ != nullptr); // suppress false clang-analyze report
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), cfhs[0], "k", "v"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), cfhs[1], "k", "v"));
|
||||
@@ -376,8 +375,6 @@ TEST_F(CorruptionTest, PostPITRCorruptionWALsRetained) {
|
||||
options_.avoid_flush_during_recovery = true;
|
||||
std::vector<ColumnFamilyHandle*> cfhs;
|
||||
ASSERT_OK(DB::Open(options_, dbname_, cf_descs, &cfhs, &db_));
|
||||
assert(db_ != nullptr); // suppress false clang-analyze report
|
||||
|
||||
// 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"));
|
||||
@@ -394,7 +391,6 @@ TEST_F(CorruptionTest, PostPITRCorruptionWALsRetained) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
std::vector<ColumnFamilyHandle*> cfhs;
|
||||
ASSERT_OK(DB::Open(options_, dbname_, cf_descs, &cfhs, &db_));
|
||||
assert(db_ != nullptr); // suppress false clang-analyze report
|
||||
|
||||
for (auto* cfh : cfhs) {
|
||||
delete cfh;
|
||||
@@ -1049,22 +1045,15 @@ INSTANTIATE_TEST_CASE_P(CorruptionTest, CrashDuringRecoveryWithCorruptionTest,
|
||||
// "Column family inconsistency: SST file contains data beyond the point of
|
||||
// corruption" error will be hit, causing recovery to fail.
|
||||
//
|
||||
// After adding the fix, only after new WAL is synced, RocksDB persist a new
|
||||
// MANIFEST with column families to ensure RocksDB is in consistent state.
|
||||
// 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.
|
||||
// If a future recovery starts from the new MANIFEST, then it means the new WAL
|
||||
// is successfully synced. Due to the sentinel empty write batch at the
|
||||
// beginning, kPointInTimeRecovery of WAL is guaranteed to go after this point.
|
||||
// If future recovery starts from the old MANIFEST, it means the writing the new
|
||||
// MANIFEST failed. It won't have the "SST ahead of WAL" error.
|
||||
//
|
||||
// The combination of corrupting a WAL and injecting an error during subsequent
|
||||
// re-open exposes the bug of prematurely persisting a new MANIFEST with
|
||||
// advanced ColumnFamilyData::log_number.
|
||||
TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
@@ -1075,7 +1064,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
options.env = env_;
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
options.create_if_missing = true;
|
||||
options.max_write_buffer_number = 8;
|
||||
options.max_write_buffer_number = 3;
|
||||
|
||||
Reopen(&options);
|
||||
Status s;
|
||||
@@ -1101,16 +1090,13 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
|
||||
// Write one key to test_cf.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles[1], "old_key", "dontcare"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions(), handles[1]));
|
||||
|
||||
// Write to default_cf and flush this cf several times to advance wal
|
||||
// number. TEST_SwitchMemtable makes sure WALs are not synced and test can
|
||||
// corrupt un-sync WAL.
|
||||
// number.
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key" + std::to_string(i),
|
||||
"value" + std::to_string(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;
|
||||
@@ -1119,13 +1105,12 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
CloseDb();
|
||||
}
|
||||
|
||||
// 2. Corrupt second last un-syned wal file to emulate power reset which
|
||||
// caused the DB to lose the un-synced WAL.
|
||||
// 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();
|
||||
assert(size >= 2);
|
||||
uint64_t log_num = file_nums[size - 2];
|
||||
CorruptFileWithTruncation(FileType::kWalFile, log_num,
|
||||
/*bytes_to_truncate=*/8);
|
||||
@@ -1142,31 +1127,25 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
// 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.
|
||||
|
||||
// 4. Fault is injected to fail the recovery.
|
||||
{
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::GetLogSizeAndMaybeTruncate:0", [&](void* arg) {
|
||||
auto* tmp_s = reinterpret_cast<Status*>(arg);
|
||||
assert(tmp_s);
|
||||
*tmp_s = Status::IOError("Injected");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
handles.clear();
|
||||
options.avoid_flush_during_recovery = true;
|
||||
options.avoid_flush_during_recovery = avoid_flush_during_recovery_;
|
||||
s = DB::Open(options, dbname_, cf_descs, &handles, &db_);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
ASSERT_EQ("IO error: Injected", s.ToString());
|
||||
ASSERT_OK(s);
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
handles.clear();
|
||||
CloseDb();
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
// 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
|
||||
@@ -1189,23 +1168,6 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
{
|
||||
options.avoid_flush_during_recovery = avoid_flush_during_recovery_;
|
||||
ASSERT_OK(DB::Open(options, dbname_, cf_descs, &handles, &db_));
|
||||
|
||||
// Verify that data is not lost.
|
||||
{
|
||||
std::string v;
|
||||
ASSERT_OK(db_->Get(ReadOptions(), handles[1], "old_key", &v));
|
||||
ASSERT_EQ("dontcare", v);
|
||||
|
||||
v.clear();
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "key" + std::to_string(0), &v));
|
||||
ASSERT_EQ("value" + std::to_string(0), v);
|
||||
|
||||
// Since it's corrupting second last wal, below key is not found.
|
||||
v.clear();
|
||||
ASSERT_EQ(db_->Get(ReadOptions(), "key" + std::to_string(1), &v),
|
||||
Status::NotFound());
|
||||
}
|
||||
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
@@ -1221,22 +1183,15 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
// file contains data beyond the point of corruption" error will be hit, causing
|
||||
// recovery to fail.
|
||||
//
|
||||
// After adding the fix, only after new WAL is synced, RocksDB persist a new
|
||||
// MANIFEST with column families to ensure RocksDB is in consistent state.
|
||||
// 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.
|
||||
// If a future recovery starts from the new MANIFEST, then it means the new WAL
|
||||
// is successfully synced. Due to the sentinel empty write batch at the
|
||||
// beginning, kPointInTimeRecovery of WAL is guaranteed to go after this point.
|
||||
// If future recovery starts from the old MANIFEST, it means the writing the new
|
||||
// MANIFEST failed. It won't have the "SST ahead of WAL" error.
|
||||
//
|
||||
// The combination of corrupting a WAL and injecting an error during subsequent
|
||||
// re-open exposes the bug of prematurely persisting a new MANIFEST with
|
||||
// advanced ColumnFamilyData::log_number.
|
||||
TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
@@ -1270,6 +1225,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
// 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));
|
||||
|
||||
@@ -1278,8 +1234,6 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
ASSERT_OK(txn->Put(handles[1], "foo", "value"));
|
||||
ASSERT_OK(txn->SetName("txn0"));
|
||||
ASSERT_OK(txn->Prepare());
|
||||
ASSERT_OK(txn_db->Flush(FlushOptions()));
|
||||
|
||||
delete txn;
|
||||
txn = nullptr;
|
||||
|
||||
@@ -1288,19 +1242,17 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
|
||||
// Put and flush cf0
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
ASSERT_OK(txn_db->Put(WriteOptions(), "key" + std::to_string(i),
|
||||
"value" + std::to_string(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", "value1"));
|
||||
ASSERT_OK(txn->Put(handles[1], "foo1", "value"));
|
||||
ASSERT_OK(txn->Commit());
|
||||
|
||||
delete txn;
|
||||
txn = nullptr;
|
||||
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
@@ -1314,7 +1266,6 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
std::vector<uint64_t> file_nums;
|
||||
GetSortedWalFiles(file_nums);
|
||||
size_t size = file_nums.size();
|
||||
assert(size >= 2);
|
||||
uint64_t log_num = file_nums[size - 2];
|
||||
CorruptFileWithTruncation(FileType::kWalFile, log_num,
|
||||
/*bytes_to_truncate=*/8);
|
||||
@@ -1324,33 +1275,20 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
// 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.
|
||||
|
||||
{
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::Open::BeforeSyncWAL", [&](void* arg) {
|
||||
auto* tmp_s = reinterpret_cast<Status*>(arg);
|
||||
assert(tmp_s);
|
||||
*tmp_s = Status::IOError("Injected");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
options.avoid_flush_during_recovery = avoid_flush_during_recovery_;
|
||||
ASSERT_OK(TransactionDB::Open(options, txn_db_opts, dbname_, cf_descs,
|
||||
&handles, &txn_db));
|
||||
|
||||
handles.clear();
|
||||
s = TransactionDB::Open(options, txn_db_opts, dbname_, cf_descs, &handles,
|
||||
&txn_db);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
ASSERT_EQ("IO error: Injected", s.ToString());
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
CloseDb();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
handles.clear();
|
||||
delete txn_db;
|
||||
}
|
||||
|
||||
// 4. Corrupt max_wal_num.
|
||||
// 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);
|
||||
@@ -1369,29 +1307,10 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
// 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));
|
||||
|
||||
// Verify that data is not lost.
|
||||
{
|
||||
std::string v;
|
||||
// Key not visible since it's not committed.
|
||||
ASSERT_EQ(txn_db->Get(ReadOptions(), handles[1], "foo", &v),
|
||||
Status::NotFound());
|
||||
|
||||
v.clear();
|
||||
ASSERT_OK(txn_db->Get(ReadOptions(), "key" + std::to_string(0), &v));
|
||||
ASSERT_EQ("value" + std::to_string(0), v);
|
||||
|
||||
// Last WAL is corrupted which contains two keys below.
|
||||
v.clear();
|
||||
ASSERT_EQ(txn_db->Get(ReadOptions(), "key" + std::to_string(1), &v),
|
||||
Status::NotFound());
|
||||
v.clear();
|
||||
ASSERT_EQ(txn_db->Get(ReadOptions(), handles[1], "foo1", &v),
|
||||
Status::NotFound());
|
||||
}
|
||||
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
@@ -1399,153 +1318,6 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
}
|
||||
}
|
||||
|
||||
// This test is similar to
|
||||
// CrashDuringRecoveryWithCorruptionTest.CrashDuringRecovery except it calls
|
||||
// flush and corrupts Last WAL. It calls flush to sync some of the WALs and
|
||||
// remaining are unsyned one of which is then corrupted to simulate crash.
|
||||
//
|
||||
// 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, 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.
|
||||
// If a future recovery starts from the new MANIFEST, then it means the new WAL
|
||||
// is successfully synced. Due to the sentinel empty write batch at the
|
||||
// beginning, kPointInTimeRecovery of WAL is guaranteed to go after this point.
|
||||
// If future recovery starts from the old MANIFEST, it means the writing the new
|
||||
// MANIFEST failed. It won't have the "SST ahead of WAL" error.
|
||||
|
||||
// The combination of corrupting a WAL and injecting an error during subsequent
|
||||
// re-open exposes the bug of prematurely persisting a new MANIFEST with
|
||||
// advanced ColumnFamilyData::log_number.
|
||||
TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecoveryWithFlush) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
options.avoid_flush_during_recovery = false;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
Reopen(&options);
|
||||
|
||||
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;
|
||||
|
||||
{
|
||||
ASSERT_OK(DB::Open(options, dbname_, cf_descs, &handles, &db_));
|
||||
|
||||
// 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" + std::to_string(i)));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles[1], "dontcare", "dontcare"));
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
handles.clear();
|
||||
CloseDb();
|
||||
}
|
||||
|
||||
// Corrupt second last un-syned 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 - 1];
|
||||
CorruptFileWithTruncation(FileType::kWalFile, log_num,
|
||||
/*bytes_to_truncate=*/8);
|
||||
}
|
||||
|
||||
// Fault is injected to fail the recovery.
|
||||
{
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::GetLogSizeAndMaybeTruncate:0", [&](void* arg) {
|
||||
auto* tmp_s = reinterpret_cast<Status*>(arg);
|
||||
assert(tmp_s);
|
||||
*tmp_s = Status::IOError("Injected");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
handles.clear();
|
||||
options.avoid_flush_during_recovery = true;
|
||||
s = DB::Open(options, dbname_, cf_descs, &handles, &db_);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
ASSERT_EQ("IO error: Injected", s.ToString());
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
CloseDb();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
// Reopen db again
|
||||
{
|
||||
options.avoid_flush_during_recovery = avoid_flush_during_recovery_;
|
||||
ASSERT_OK(DB::Open(options, dbname_, cf_descs, &handles, &db_));
|
||||
|
||||
// Verify that data is not lost.
|
||||
{
|
||||
std::string v;
|
||||
ASSERT_OK(db_->Get(ReadOptions(), handles[1], "old_key", &v));
|
||||
ASSERT_EQ("dontcare", v);
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
v.clear();
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "key" + std::to_string(i), &v));
|
||||
ASSERT_EQ("value" + std::to_string(i), v);
|
||||
}
|
||||
|
||||
// Since it's corrupting last wal after Flush, below key is not found.
|
||||
v.clear();
|
||||
ASSERT_EQ(db_->Get(ReadOptions(), handles[1], "dontcare", &v),
|
||||
Status::NotFound());
|
||||
}
|
||||
|
||||
for (auto* h : handles) {
|
||||
delete h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -95,8 +95,8 @@ class CuckooTableDBTest : public testing::Test {
|
||||
|
||||
int NumTableFilesAtLevel(int level) {
|
||||
std::string property;
|
||||
EXPECT_TRUE(db_->GetProperty(
|
||||
"rocksdb.num-files-at-level" + std::to_string(level), &property));
|
||||
EXPECT_TRUE(db_->GetProperty("rocksdb.num-files-at-level" + ToString(level),
|
||||
&property));
|
||||
return atoi(property.c_str());
|
||||
}
|
||||
|
||||
|
||||
+79
-570
@@ -12,7 +12,6 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/flush_block_policy.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
@@ -25,7 +24,6 @@
|
||||
#endif
|
||||
#include "util/file_checksum_helper.h"
|
||||
#include "util/random.h"
|
||||
#include "utilities/counted_fs.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
#include "utilities/merge_operators/string_append/stringappend.h"
|
||||
@@ -579,84 +577,55 @@ TEST_F(DBBasicTest, ManifestRollOver) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, IdentityAcrossRestarts) {
|
||||
constexpr size_t kMinIdSize = 10;
|
||||
TEST_F(DBBasicTest, IdentityAcrossRestarts1) {
|
||||
do {
|
||||
for (bool with_manifest : {false, true}) {
|
||||
std::string idfilename = IdentityFileName(dbname_);
|
||||
std::string id1, tmp;
|
||||
ASSERT_OK(db_->GetDbIdentity(id1));
|
||||
ASSERT_GE(id1.size(), kMinIdSize);
|
||||
std::string id1;
|
||||
ASSERT_OK(db_->GetDbIdentity(id1));
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.write_dbid_to_manifest = with_manifest;
|
||||
Reopen(options);
|
||||
std::string id2;
|
||||
ASSERT_OK(db_->GetDbIdentity(id2));
|
||||
// id2 should match id1 because identity was not regenerated
|
||||
ASSERT_EQ(id1, id2);
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id2);
|
||||
Options options = CurrentOptions();
|
||||
Reopen(options);
|
||||
std::string id2;
|
||||
ASSERT_OK(db_->GetDbIdentity(id2));
|
||||
// id1 should match id2 because identity was not regenerated
|
||||
ASSERT_EQ(id1.compare(id2), 0);
|
||||
|
||||
// Recover from deleted/missing IDENTITY
|
||||
ASSERT_OK(env_->DeleteFile(idfilename));
|
||||
Reopen(options);
|
||||
std::string id3;
|
||||
ASSERT_OK(db_->GetDbIdentity(id3));
|
||||
if (with_manifest) {
|
||||
// id3 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id3);
|
||||
} else {
|
||||
// id3 should NOT match id1 because identity was regenerated
|
||||
ASSERT_NE(id1, id3);
|
||||
ASSERT_GE(id3.size(), kMinIdSize);
|
||||
}
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id3);
|
||||
|
||||
// Recover from truncated IDENTITY
|
||||
{
|
||||
std::unique_ptr<WritableFile> w;
|
||||
ASSERT_OK(env_->NewWritableFile(idfilename, &w, EnvOptions()));
|
||||
ASSERT_OK(w->Close());
|
||||
}
|
||||
Reopen(options);
|
||||
std::string id4;
|
||||
ASSERT_OK(db_->GetDbIdentity(id4));
|
||||
if (with_manifest) {
|
||||
// id4 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id4);
|
||||
} else {
|
||||
// id4 should NOT match id1 because identity was regenerated
|
||||
ASSERT_NE(id1, id4);
|
||||
ASSERT_GE(id4.size(), kMinIdSize);
|
||||
}
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id4);
|
||||
|
||||
// Recover from overwritten IDENTITY
|
||||
std::string silly_id = "asdf123456789";
|
||||
{
|
||||
std::unique_ptr<WritableFile> w;
|
||||
ASSERT_OK(env_->NewWritableFile(idfilename, &w, EnvOptions()));
|
||||
ASSERT_OK(w->Append(silly_id));
|
||||
ASSERT_OK(w->Close());
|
||||
}
|
||||
Reopen(options);
|
||||
std::string id5;
|
||||
ASSERT_OK(db_->GetDbIdentity(id5));
|
||||
if (with_manifest) {
|
||||
// id4 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id5);
|
||||
} else {
|
||||
ASSERT_EQ(id5, silly_id);
|
||||
}
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id5);
|
||||
std::string idfilename = IdentityFileName(dbname_);
|
||||
ASSERT_OK(env_->DeleteFile(idfilename));
|
||||
Reopen(options);
|
||||
std::string id3;
|
||||
ASSERT_OK(db_->GetDbIdentity(id3));
|
||||
if (options.write_dbid_to_manifest) {
|
||||
ASSERT_EQ(id1.compare(id3), 0);
|
||||
} else {
|
||||
// id1 should NOT match id3 because identity was regenerated
|
||||
ASSERT_NE(id1.compare(id3), 0);
|
||||
}
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, IdentityAcrossRestarts2) {
|
||||
do {
|
||||
std::string id1;
|
||||
ASSERT_OK(db_->GetDbIdentity(id1));
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.write_dbid_to_manifest = true;
|
||||
Reopen(options);
|
||||
std::string id2;
|
||||
ASSERT_OK(db_->GetDbIdentity(id2));
|
||||
// id1 should match id2 because identity was not regenerated
|
||||
ASSERT_EQ(id1.compare(id2), 0);
|
||||
|
||||
std::string idfilename = IdentityFileName(dbname_);
|
||||
ASSERT_OK(env_->DeleteFile(idfilename));
|
||||
Reopen(options);
|
||||
std::string id3;
|
||||
ASSERT_OK(db_->GetDbIdentity(id3));
|
||||
// id1 should NOT match id3 because identity was regenerated
|
||||
ASSERT_EQ(id1, id3);
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBasicTest, Snapshot) {
|
||||
env_->SetMockSleep();
|
||||
@@ -1183,39 +1152,6 @@ TEST_F(DBBasicTest, DBClose) {
|
||||
ASSERT_EQ(env->GetCloseCount(), 3);
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, DBCloseAllDirectoryFDs) {
|
||||
Options options = GetDefaultOptions();
|
||||
std::string dbname = test::PerThreadDBPath("db_close_all_dir_fds_test");
|
||||
// Configure a specific WAL directory
|
||||
options.wal_dir = dbname + "_wal_dir";
|
||||
// Configure 3 different data directories
|
||||
options.db_paths.emplace_back(dbname + "_1", 512 * 1024);
|
||||
options.db_paths.emplace_back(dbname + "_2", 4 * 1024 * 1024);
|
||||
options.db_paths.emplace_back(dbname + "_3", 1024 * 1024 * 1024);
|
||||
|
||||
ASSERT_OK(DestroyDB(dbname, options));
|
||||
|
||||
DB* db = nullptr;
|
||||
std::unique_ptr<Env> env = NewCompositeEnv(
|
||||
std::make_shared<CountedFileSystem>(FileSystem::Default()));
|
||||
options.create_if_missing = true;
|
||||
options.env = env.get();
|
||||
Status s = DB::Open(options, dbname, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
// Explicitly close the database to ensure the open and close counter for
|
||||
// directories are equivalent
|
||||
s = db->Close();
|
||||
auto* counted_fs =
|
||||
options.env->GetFileSystem()->CheckedCast<CountedFileSystem>();
|
||||
ASSERT_TRUE(counted_fs != nullptr);
|
||||
ASSERT_EQ(counted_fs->counters()->dir_opens,
|
||||
counted_fs->counters()->dir_closes);
|
||||
ASSERT_OK(s);
|
||||
delete db;
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, DBCloseFlushError) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
@@ -1244,17 +1180,10 @@ TEST_F(DBBasicTest, DBCloseFlushError) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
class DBMultiGetTestWithParam
|
||||
: public DBBasicTest,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {};
|
||||
class DBMultiGetTestWithParam : public DBBasicTest,
|
||||
public testing::WithParamInterface<bool> {};
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetMultiCF) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
|
||||
"alyosha", "popovich"},
|
||||
@@ -1311,8 +1240,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCF) {
|
||||
keys.push_back(std::get<1>(cf_kv_vec[i]));
|
||||
}
|
||||
|
||||
values = MultiGet(cfs, keys, nullptr, std::get<0>(GetParam()),
|
||||
std::get<1>(GetParam()));
|
||||
values = MultiGet(cfs, keys, nullptr, GetParam());
|
||||
ASSERT_EQ(values.size(), num_keys);
|
||||
for (unsigned int j = 0; j < values.size(); ++j) {
|
||||
ASSERT_EQ(values[j], std::get<2>(cf_kv_vec[j]) + "_2");
|
||||
@@ -1326,8 +1254,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCF) {
|
||||
keys.push_back(std::get<1>(cf_kv_vec[3]));
|
||||
cfs.push_back(std::get<0>(cf_kv_vec[4]));
|
||||
keys.push_back(std::get<1>(cf_kv_vec[4]));
|
||||
values = MultiGet(cfs, keys, nullptr, std::get<0>(GetParam()),
|
||||
std::get<1>(GetParam()));
|
||||
values = MultiGet(cfs, keys, nullptr, GetParam());
|
||||
ASSERT_EQ(values[0], std::get<2>(cf_kv_vec[0]) + "_2");
|
||||
ASSERT_EQ(values[1], std::get<2>(cf_kv_vec[3]) + "_2");
|
||||
ASSERT_EQ(values[2], std::get<2>(cf_kv_vec[4]) + "_2");
|
||||
@@ -1340,8 +1267,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCF) {
|
||||
keys.push_back(std::get<1>(cf_kv_vec[6]));
|
||||
cfs.push_back(std::get<0>(cf_kv_vec[1]));
|
||||
keys.push_back(std::get<1>(cf_kv_vec[1]));
|
||||
values = MultiGet(cfs, keys, nullptr, std::get<0>(GetParam()),
|
||||
std::get<1>(GetParam()));
|
||||
values = MultiGet(cfs, keys, nullptr, GetParam());
|
||||
ASSERT_EQ(values[0], std::get<2>(cf_kv_vec[7]) + "_2");
|
||||
ASSERT_EQ(values[1], std::get<2>(cf_kv_vec[6]) + "_2");
|
||||
ASSERT_EQ(values[2], std::get<2>(cf_kv_vec[1]) + "_2");
|
||||
@@ -1357,12 +1283,6 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCF) {
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFMutex) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
|
||||
"alyosha", "popovich"},
|
||||
@@ -1408,8 +1328,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFMutex) {
|
||||
keys.push_back("cf" + std::to_string(i) + "_key");
|
||||
}
|
||||
|
||||
values = MultiGet(cfs, keys, nullptr, std::get<0>(GetParam()),
|
||||
std::get<1>(GetParam()));
|
||||
values = MultiGet(cfs, keys, nullptr, GetParam());
|
||||
ASSERT_TRUE(last_try);
|
||||
ASSERT_EQ(values.size(), 8);
|
||||
for (unsigned int j = 0; j < values.size(); ++j) {
|
||||
@@ -1426,12 +1345,6 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFMutex) {
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFSnapshot) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
|
||||
"alyosha", "popovich"},
|
||||
@@ -1476,8 +1389,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFSnapshot) {
|
||||
}
|
||||
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
values = MultiGet(cfs, keys, snapshot, std::get<0>(GetParam()),
|
||||
std::get<1>(GetParam()));
|
||||
values = MultiGet(cfs, keys, snapshot, GetParam());
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
ASSERT_EQ(values.size(), 8);
|
||||
for (unsigned int j = 0; j < values.size(); ++j) {
|
||||
@@ -1493,12 +1405,6 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFSnapshot) {
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFUnsorted) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"one", "two"}, options);
|
||||
|
||||
@@ -1511,9 +1417,8 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFUnsorted) {
|
||||
std::vector<std::string> keys{"foo", "baz", "abc"};
|
||||
std::vector<std::string> values;
|
||||
|
||||
values = MultiGet(cfs, keys, /* snapshot */ nullptr,
|
||||
/* batched */ std::get<0>(GetParam()),
|
||||
/* async */ std::get<1>(GetParam()));
|
||||
values =
|
||||
MultiGet(cfs, keys, /* snapshot */ nullptr, /* batched */ GetParam());
|
||||
|
||||
ASSERT_EQ(values.size(), 3);
|
||||
ASSERT_EQ(values[0], "bar");
|
||||
@@ -1521,18 +1426,10 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFUnsorted) {
|
||||
ASSERT_EQ(values[2], "def");
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetBatchedSimpleUnsorted) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
// Skip for unbatched MultiGet
|
||||
if (!std::get<0>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test is only for batched MultiGet");
|
||||
return;
|
||||
}
|
||||
INSTANTIATE_TEST_CASE_P(DBMultiGetTestWithParam, DBMultiGetTestWithParam,
|
||||
testing::Bool());
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedSimpleUnsorted) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
SetPerfLevel(kEnableCount);
|
||||
@@ -1551,10 +1448,8 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedSimpleUnsorted) {
|
||||
std::vector<ColumnFamilyHandle*> cfs(keys.size(), handles_[1]);
|
||||
std::vector<Status> s(keys.size());
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = std::get<1>(GetParam());
|
||||
db_->MultiGet(ro, handles_[1], keys.size(), keys.data(), values.data(),
|
||||
s.data(), false);
|
||||
db_->MultiGet(ReadOptions(), handles_[1], keys.size(), keys.data(),
|
||||
values.data(), s.data(), false);
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
ASSERT_EQ(std::string(values[5].data(), values[5].size()), "v1");
|
||||
@@ -1575,18 +1470,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedSimpleUnsorted) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetBatchedSortedMultiFile) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
// Skip for unbatched MultiGet
|
||||
if (!std::get<0>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test is only for batched MultiGet");
|
||||
return;
|
||||
}
|
||||
TEST_F(DBBasicTest, MultiGetBatchedSortedMultiFile) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
SetPerfLevel(kEnableCount);
|
||||
@@ -1609,10 +1493,8 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedSortedMultiFile) {
|
||||
std::vector<ColumnFamilyHandle*> cfs(keys.size(), handles_[1]);
|
||||
std::vector<Status> s(keys.size());
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = std::get<1>(GetParam());
|
||||
db_->MultiGet(ro, handles_[1], keys.size(), keys.data(), values.data(),
|
||||
s.data(), true);
|
||||
db_->MultiGet(ReadOptions(), handles_[1], keys.size(), keys.data(),
|
||||
values.data(), s.data(), true);
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
ASSERT_EQ(std::string(values[0].data(), values[0].size()), "v1");
|
||||
@@ -1633,18 +1515,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedSortedMultiFile) {
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetBatchedDuplicateKeys) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
// Skip for unbatched MultiGet
|
||||
if (!std::get<0>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test is only for batched MultiGet");
|
||||
return;
|
||||
}
|
||||
TEST_F(DBBasicTest, MultiGetBatchedDuplicateKeys) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
CreateAndReopenWithCF({"pikachu"}, opts);
|
||||
@@ -1675,10 +1546,8 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedDuplicateKeys) {
|
||||
std::vector<ColumnFamilyHandle*> cfs(keys.size(), handles_[1]);
|
||||
std::vector<Status> s(keys.size());
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = std::get<1>(GetParam());
|
||||
db_->MultiGet(ro, handles_[1], keys.size(), keys.data(), values.data(),
|
||||
s.data(), false);
|
||||
db_->MultiGet(ReadOptions(), handles_[1], keys.size(), keys.data(),
|
||||
values.data(), s.data(), false);
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
ASSERT_EQ(std::string(values[0].data(), values[0].size()), "v8");
|
||||
@@ -1697,18 +1566,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedDuplicateKeys) {
|
||||
SetPerfLevel(kDisable);
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetBatchedMultiLevel) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
// Skip for unbatched MultiGet
|
||||
if (!std::get<0>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test is only for batched MultiGet");
|
||||
return;
|
||||
}
|
||||
TEST_F(DBBasicTest, MultiGetBatchedMultiLevel) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
Reopen(options);
|
||||
@@ -1767,7 +1625,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedMultiLevel) {
|
||||
keys.push_back("key_" + std::to_string(i));
|
||||
}
|
||||
|
||||
values = MultiGet(keys, nullptr, std::get<1>(GetParam()));
|
||||
values = MultiGet(keys, nullptr);
|
||||
ASSERT_EQ(values.size(), 16);
|
||||
for (unsigned int j = 0; j < values.size(); ++j) {
|
||||
int key = j + 64;
|
||||
@@ -1783,18 +1641,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedMultiLevel) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetBatchedMultiLevelMerge) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
// Skip for unbatched MultiGet
|
||||
if (!std::get<0>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test is only for batched MultiGet");
|
||||
return;
|
||||
}
|
||||
TEST_F(DBBasicTest, MultiGetBatchedMultiLevelMerge) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
@@ -1858,7 +1705,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedMultiLevelMerge) {
|
||||
keys.push_back("key_" + std::to_string(i));
|
||||
}
|
||||
|
||||
values = MultiGet(keys, nullptr, std::get<1>(GetParam()));
|
||||
values = MultiGet(keys, nullptr);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (unsigned int j = 0; j < 48; ++j) {
|
||||
int key = j + 32;
|
||||
@@ -1880,18 +1727,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedMultiLevelMerge) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetBatchedValueSizeInMemory) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
// Skip for unbatched MultiGet
|
||||
if (!std::get<0>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test is only for batched MultiGet");
|
||||
return;
|
||||
}
|
||||
TEST_F(DBBasicTest, MultiGetBatchedValueSizeInMemory) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_OK(Put(1, "k1", "v_1"));
|
||||
@@ -1908,7 +1744,6 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedValueSizeInMemory) {
|
||||
get_perf_context()->Reset();
|
||||
ReadOptions ro;
|
||||
ro.value_size_soft_limit = 11;
|
||||
ro.async_io = std::get<1>(GetParam());
|
||||
db_->MultiGet(ro, handles_[1], keys.size(), keys.data(), values.data(),
|
||||
s.data(), false);
|
||||
|
||||
@@ -1926,17 +1761,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedValueSizeInMemory) {
|
||||
SetPerfLevel(kDisable);
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetBatchedValueSize) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
// Skip for unbatched MultiGet
|
||||
if (!std::get<0>(GetParam())) {
|
||||
return;
|
||||
}
|
||||
TEST_F(DBBasicTest, MultiGetBatchedValueSize) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
SetPerfLevel(kEnableCount);
|
||||
@@ -1976,7 +1801,6 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedValueSize) {
|
||||
|
||||
ReadOptions ro;
|
||||
ro.value_size_soft_limit = 20;
|
||||
ro.async_io = std::get<1>(GetParam());
|
||||
db_->MultiGet(ro, handles_[1], keys.size(), keys.data(), values.data(),
|
||||
s.data(), false);
|
||||
|
||||
@@ -2012,18 +1836,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedValueSize) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetBatchedValueSizeMultiLevelMerge) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
// Skip for unbatched MultiGet
|
||||
if (!std::get<0>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test is only for batched MultiGet");
|
||||
return;
|
||||
}
|
||||
TEST_F(DBBasicTest, MultiGetBatchedValueSizeMultiLevelMerge) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
@@ -2095,7 +1908,6 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedValueSizeMultiLevelMerge) {
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
read_options.value_size_soft_limit = 380;
|
||||
read_options.async_io = std::get<1>(GetParam());
|
||||
db_->MultiGet(read_options, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
@@ -2127,219 +1939,6 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedValueSizeMultiLevelMerge) {
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBMultiGetTestWithParam, DBMultiGetTestWithParam,
|
||||
testing::Combine(testing::Bool(), testing::Bool()));
|
||||
|
||||
#if USE_COROUTINES
|
||||
class DBMultiGetAsyncIOTest : public DBBasicTest {
|
||||
public:
|
||||
DBMultiGetAsyncIOTest()
|
||||
: DBBasicTest(), statistics_(ROCKSDB_NAMESPACE::CreateDBStatistics()) {
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.statistics = statistics_;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
int num_keys = 0;
|
||||
|
||||
// Put all keys in the bottommost level, and overwrite some keys
|
||||
// in L0 and L1
|
||||
for (int i = 0; i < 128; ++i) {
|
||||
EXPECT_OK(Put(Key(i), "val_l2_" + std::to_string(i)));
|
||||
num_keys++;
|
||||
if (num_keys == 8) {
|
||||
EXPECT_OK(Flush());
|
||||
num_keys = 0;
|
||||
}
|
||||
}
|
||||
if (num_keys > 0) {
|
||||
EXPECT_OK(Flush());
|
||||
num_keys = 0;
|
||||
}
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
for (int i = 0; i < 128; i += 3) {
|
||||
EXPECT_OK(Put(Key(i), "val_l1_" + std::to_string(i)));
|
||||
num_keys++;
|
||||
if (num_keys == 8) {
|
||||
EXPECT_OK(Flush());
|
||||
num_keys = 0;
|
||||
}
|
||||
}
|
||||
if (num_keys > 0) {
|
||||
EXPECT_OK(Flush());
|
||||
num_keys = 0;
|
||||
}
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
for (int i = 0; i < 128; i += 5) {
|
||||
EXPECT_OK(Put(Key(i), "val_l0_" + std::to_string(i)));
|
||||
num_keys++;
|
||||
if (num_keys == 8) {
|
||||
EXPECT_OK(Flush());
|
||||
num_keys = 0;
|
||||
}
|
||||
}
|
||||
if (num_keys > 0) {
|
||||
EXPECT_OK(Flush());
|
||||
num_keys = 0;
|
||||
}
|
||||
EXPECT_EQ(0, num_keys);
|
||||
}
|
||||
|
||||
const std::shared_ptr<Statistics>& statistics() { return statistics_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Statistics> statistics_;
|
||||
};
|
||||
|
||||
TEST_F(DBMultiGetAsyncIOTest, GetFromL0) {
|
||||
// All 3 keys in L0. The L0 files should be read serially.
|
||||
std::vector<std::string> key_strs{Key(0), Key(40), Key(80)};
|
||||
std::vector<Slice> keys{key_strs[0], key_strs[1], key_strs[2]};
|
||||
std::vector<PinnableSlice> values(key_strs.size());
|
||||
std::vector<Status> statuses(key_strs.size());
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data());
|
||||
ASSERT_EQ(values.size(), 3);
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_OK(statuses[1]);
|
||||
ASSERT_OK(statuses[2]);
|
||||
ASSERT_EQ(values[0], "val_l0_" + std::to_string(0));
|
||||
ASSERT_EQ(values[1], "val_l0_" + std::to_string(40));
|
||||
ASSERT_EQ(values[2], "val_l0_" + std::to_string(80));
|
||||
|
||||
HistogramData multiget_io_batch_size;
|
||||
|
||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||
|
||||
// No async IO in this case since we don't do parallel lookup in L0
|
||||
ASSERT_EQ(multiget_io_batch_size.count, 0);
|
||||
ASSERT_EQ(multiget_io_batch_size.max, 0);
|
||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 0);
|
||||
}
|
||||
|
||||
TEST_F(DBMultiGetAsyncIOTest, GetFromL1) {
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
std::vector<PinnableSlice> values;
|
||||
std::vector<Status> statuses;
|
||||
|
||||
key_strs.push_back(Key(33));
|
||||
key_strs.push_back(Key(54));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data());
|
||||
ASSERT_EQ(values.size(), 3);
|
||||
ASSERT_EQ(statuses[0], Status::OK());
|
||||
ASSERT_EQ(statuses[1], Status::OK());
|
||||
ASSERT_EQ(statuses[2], Status::OK());
|
||||
ASSERT_EQ(values[0], "val_l1_" + std::to_string(33));
|
||||
ASSERT_EQ(values[1], "val_l1_" + std::to_string(54));
|
||||
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
||||
|
||||
HistogramData multiget_io_batch_size;
|
||||
|
||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||
|
||||
// A batch of 3 async IOs is expected, one for each overlapping file in L1
|
||||
ASSERT_EQ(multiget_io_batch_size.count, 1);
|
||||
ASSERT_EQ(multiget_io_batch_size.max, 3);
|
||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 3);
|
||||
}
|
||||
|
||||
TEST_F(DBMultiGetAsyncIOTest, LastKeyInFile) {
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
std::vector<PinnableSlice> values;
|
||||
std::vector<Status> statuses;
|
||||
|
||||
// 21 is the last key in the first L1 file
|
||||
key_strs.push_back(Key(21));
|
||||
key_strs.push_back(Key(54));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data());
|
||||
ASSERT_EQ(values.size(), 3);
|
||||
ASSERT_EQ(statuses[0], Status::OK());
|
||||
ASSERT_EQ(statuses[1], Status::OK());
|
||||
ASSERT_EQ(statuses[2], Status::OK());
|
||||
ASSERT_EQ(values[0], "val_l1_" + std::to_string(21));
|
||||
ASSERT_EQ(values[1], "val_l1_" + std::to_string(54));
|
||||
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
||||
|
||||
HistogramData multiget_io_batch_size;
|
||||
|
||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||
|
||||
// Since the first MultiGet key is the last key in a file, the MultiGet is
|
||||
// expected to lookup in that file first, before moving on to other files.
|
||||
// So the first file lookup will issue one async read, and the next lookup
|
||||
// will lookup 2 files in parallel and issue 2 async reads
|
||||
ASSERT_EQ(multiget_io_batch_size.count, 2);
|
||||
ASSERT_EQ(multiget_io_batch_size.max, 2);
|
||||
}
|
||||
|
||||
TEST_F(DBMultiGetAsyncIOTest, GetFromL1AndL2) {
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
std::vector<PinnableSlice> values;
|
||||
std::vector<Status> statuses;
|
||||
|
||||
// 33 and 102 are in L1, and 56 is in L2
|
||||
key_strs.push_back(Key(33));
|
||||
key_strs.push_back(Key(56));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data());
|
||||
ASSERT_EQ(values.size(), 3);
|
||||
ASSERT_EQ(statuses[0], Status::OK());
|
||||
ASSERT_EQ(statuses[1], Status::OK());
|
||||
ASSERT_EQ(statuses[2], Status::OK());
|
||||
ASSERT_EQ(values[0], "val_l1_" + std::to_string(33));
|
||||
ASSERT_EQ(values[1], "val_l2_" + std::to_string(56));
|
||||
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
||||
|
||||
HistogramData multiget_io_batch_size;
|
||||
|
||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||
|
||||
// There is only one MultiGet key in the bottommost level - 56. Thus
|
||||
// the bottommost level will not use async IO.
|
||||
ASSERT_EQ(multiget_io_batch_size.count, 1);
|
||||
ASSERT_EQ(multiget_io_batch_size.max, 2);
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetStats) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
@@ -2409,37 +2008,27 @@ TEST_F(DBBasicTest, MultiGetStats) {
|
||||
values.data(), s.data(), false);
|
||||
|
||||
ASSERT_EQ(values.size(), kMultiGetBatchSize);
|
||||
HistogramData hist_level;
|
||||
HistogramData hist_data_blocks;
|
||||
HistogramData hist_index_and_filter_blocks;
|
||||
HistogramData hist_sst;
|
||||
|
||||
options.statistics->histogramData(NUM_LEVEL_READ_PER_MULTIGET, &hist_level);
|
||||
options.statistics->histogramData(NUM_DATA_BLOCKS_READ_PER_LEVEL,
|
||||
&hist_data_blocks);
|
||||
options.statistics->histogramData(NUM_INDEX_AND_FILTER_BLOCKS_READ_PER_LEVEL,
|
||||
&hist_index_and_filter_blocks);
|
||||
options.statistics->histogramData(NUM_SST_READ_PER_LEVEL, &hist_sst);
|
||||
|
||||
// Maximum number of blocks read from a file system in a level.
|
||||
ASSERT_EQ(hist_level.max, 1);
|
||||
ASSERT_EQ(hist_data_blocks.max, 32);
|
||||
ASSERT_GT(hist_index_and_filter_blocks.max, 0);
|
||||
// Maximum number of sst files read from file system in a level.
|
||||
ASSERT_EQ(hist_sst.max, 2);
|
||||
|
||||
// Minimun number of blocks read in a level.
|
||||
ASSERT_EQ(hist_level.min, 1);
|
||||
ASSERT_EQ(hist_data_blocks.min, 4);
|
||||
ASSERT_GT(hist_index_and_filter_blocks.min, 0);
|
||||
// Minimun number of sst files read in a level.
|
||||
ASSERT_EQ(hist_sst.min, 1);
|
||||
|
||||
for (PinnableSlice& value : values) {
|
||||
value.Reset();
|
||||
}
|
||||
for (Status& status : s) {
|
||||
status = Status::OK();
|
||||
}
|
||||
db_->MultiGet(read_opts, handles_[1], kMultiGetBatchSize, &keys[950],
|
||||
values.data(), s.data(), false);
|
||||
options.statistics->histogramData(NUM_LEVEL_READ_PER_MULTIGET, &hist_level);
|
||||
ASSERT_EQ(hist_level.max, 2);
|
||||
}
|
||||
|
||||
// Test class for batched MultiGet with prefix extractor
|
||||
@@ -3719,11 +3308,6 @@ class DeadlineRandomAccessFile : public FSRandomAccessFileOwnerWrapper {
|
||||
IOStatus MultiRead(FSReadRequest* reqs, size_t num_reqs,
|
||||
const IOOptions& options, IODebugContext* dbg) override;
|
||||
|
||||
IOStatus ReadAsync(FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(const FSReadRequest&, void*)> cb,
|
||||
void* cb_arg, void** io_handle, IOHandleDeleter* del_fn,
|
||||
IODebugContext* dbg) override;
|
||||
|
||||
private:
|
||||
DeadlineFS& fs_;
|
||||
std::unique_ptr<FSRandomAccessFile> file_;
|
||||
@@ -3864,26 +3448,6 @@ IOStatus DeadlineRandomAccessFile::Read(uint64_t offset, size_t len,
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus DeadlineRandomAccessFile::ReadAsync(
|
||||
FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(const FSReadRequest&, void*)> cb, void* cb_arg,
|
||||
void** io_handle, IOHandleDeleter* del_fn, IODebugContext* dbg) {
|
||||
const std::chrono::microseconds deadline = fs_.GetDeadline();
|
||||
const std::chrono::microseconds io_timeout = fs_.GetIOTimeout();
|
||||
IOStatus s;
|
||||
if (deadline.count() || io_timeout.count()) {
|
||||
fs_.AssertDeadline(deadline, io_timeout, opts);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = FSRandomAccessFileWrapper::ReadAsync(req, opts, cb, cb_arg, io_handle,
|
||||
del_fn, dbg);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = fs_.ShouldDelay(opts);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus DeadlineRandomAccessFile::MultiRead(FSReadRequest* reqs,
|
||||
size_t num_reqs,
|
||||
const IOOptions& options,
|
||||
@@ -3905,8 +3469,7 @@ IOStatus DeadlineRandomAccessFile::MultiRead(FSReadRequest* reqs,
|
||||
|
||||
// A test class for intercepting random reads and injecting artificial
|
||||
// delays. Used for testing the MultiGet deadline feature
|
||||
class DBBasicTestMultiGetDeadline : public DBBasicTestMultiGet,
|
||||
public testing::WithParamInterface<bool> {
|
||||
class DBBasicTestMultiGetDeadline : public DBBasicTestMultiGet {
|
||||
public:
|
||||
DBBasicTestMultiGetDeadline()
|
||||
: DBBasicTestMultiGet(
|
||||
@@ -3929,13 +3492,7 @@ class DBBasicTestMultiGetDeadline : public DBBasicTestMultiGet,
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (GetParam()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
TEST_F(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
std::shared_ptr<DeadlineFS> fs = std::make_shared<DeadlineFS>(env_, false);
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fs));
|
||||
Options options = CurrentOptions();
|
||||
@@ -3966,7 +3523,6 @@ TEST_P(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
|
||||
ReadOptions ro;
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
ro.async_io = GetParam();
|
||||
// Delay the first IO
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, 0);
|
||||
|
||||
@@ -4069,9 +3625,6 @@ TEST_P(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DeadlineIO, DBBasicTestMultiGetDeadline,
|
||||
::testing::Bool());
|
||||
|
||||
TEST_F(DBBasicTest, ManifestWriteFailure) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -4128,27 +3681,6 @@ TEST_F(DBBasicTest, DestroyDefaultCfHandle) {
|
||||
ASSERT_TRUE(db_->DestroyColumnFamilyHandle(default_cf).IsInvalidArgument());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, FailOpenIfLoggerCreationFail) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"rocksdb::CreateLoggerFromOptions:AfterGetPath", [&](void* arg) {
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
assert(s);
|
||||
*s = Status::IOError("Injected");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_EQ(nullptr, options.info_log);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBasicTest, VerifyFileChecksums) {
|
||||
Options options = GetDefaultOptions();
|
||||
@@ -4192,29 +3724,6 @@ TEST_F(DBBasicTest, VerifyFileChecksums) {
|
||||
Reopen(options);
|
||||
ASSERT_TRUE(db_->VerifyFileChecksums(ReadOptions()).IsInvalidArgument());
|
||||
}
|
||||
|
||||
// TODO: re-enable after we provide finer-grained control for WAL tracking to
|
||||
// meet the needs of different use cases, durability levels and recovery modes.
|
||||
TEST_F(DBBasicTest, DISABLED_ManualWalSync) {
|
||||
Options options = CurrentOptions();
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kAbsoluteConsistency;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("x", "y"));
|
||||
// This does not create a new WAL.
|
||||
ASSERT_OK(db_->SyncWAL());
|
||||
EXPECT_FALSE(dbfull()->GetVersionSet()->GetWalSet().GetWals().empty());
|
||||
|
||||
std::unique_ptr<LogFile> wal;
|
||||
Status s = db_->GetCurrentWalFile(&wal);
|
||||
ASSERT_OK(s);
|
||||
Close();
|
||||
|
||||
EXPECT_OK(env_->DeleteFile(LogFileName(dbname_, wal->LogNumber())));
|
||||
|
||||
ASSERT_TRUE(TryReopen(options).IsCorruption());
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// A test class for intercepting random reads and injecting artificial
|
||||
@@ -4274,7 +3783,7 @@ TEST_P(DBBasicTestDeadline, PointLookupDeadline) {
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 400; ++i) {
|
||||
std::string key = "k" + std::to_string(i);
|
||||
std::string key = "k" + ToString(i);
|
||||
ASSERT_OK(Put(key, rnd.RandomString(100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
@@ -4357,7 +3866,7 @@ TEST_P(DBBasicTestDeadline, IteratorDeadline) {
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 400; ++i) {
|
||||
std::string key = "k" + std::to_string(i);
|
||||
std::string key = "k" + ToString(i);
|
||||
ASSERT_OK(Put(key, rnd.RandomString(100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
+31
-36
@@ -13,7 +13,6 @@
|
||||
|
||||
#include "cache/cache_entry_roles.h"
|
||||
#include "cache/cache_key.h"
|
||||
#include "cache/fast_lru_cache.h"
|
||||
#include "cache/lru_cache.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
@@ -76,7 +75,7 @@ class DBBlockCacheTest : public DBTestBase {
|
||||
void InitTable(const Options& /*options*/) {
|
||||
std::string value(kValueSize, 'a');
|
||||
for (size_t i = 0; i < kNumBlocks; i++) {
|
||||
ASSERT_OK(Put(std::to_string(i), value.c_str()));
|
||||
ASSERT_OK(Put(ToString(i), value.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +204,7 @@ TEST_F(DBBlockCacheTest, IteratorBlockCacheUsage) {
|
||||
|
||||
ASSERT_EQ(0, cache->GetUsage());
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(std::to_string(0));
|
||||
iter->Seek(ToString(0));
|
||||
ASSERT_LT(0, cache->GetUsage());
|
||||
delete iter;
|
||||
iter = nullptr;
|
||||
@@ -236,7 +235,7 @@ TEST_F(DBBlockCacheTest, TestWithoutCompressedBlockCache) {
|
||||
// Load blocks into cache.
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(std::to_string(i));
|
||||
iter->Seek(ToString(i));
|
||||
ASSERT_OK(iter->status());
|
||||
CheckCacheCounters(options, 1, 0, 1, 0);
|
||||
iterators[i].reset(iter);
|
||||
@@ -249,7 +248,7 @@ TEST_F(DBBlockCacheTest, TestWithoutCompressedBlockCache) {
|
||||
// Test with strict capacity limit.
|
||||
cache->SetStrictCapacityLimit(true);
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(std::to_string(kNumBlocks - 1));
|
||||
iter->Seek(ToString(kNumBlocks - 1));
|
||||
ASSERT_TRUE(iter->status().IsIncomplete());
|
||||
CheckCacheCounters(options, 1, 0, 0, 1);
|
||||
delete iter;
|
||||
@@ -263,7 +262,7 @@ TEST_F(DBBlockCacheTest, TestWithoutCompressedBlockCache) {
|
||||
ASSERT_EQ(0, cache->GetPinnedUsage());
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(std::to_string(i));
|
||||
iter->Seek(ToString(i));
|
||||
ASSERT_OK(iter->status());
|
||||
CheckCacheCounters(options, 0, 1, 0, 0);
|
||||
iterators[i].reset(iter);
|
||||
@@ -289,7 +288,7 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
|
||||
std::string value(kValueSize, 'a');
|
||||
for (size_t i = 0; i < kNumBlocks; i++) {
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Put(ToString(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
@@ -313,7 +312,7 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
|
||||
// Load blocks into cache.
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(value, Get(ToString(i)));
|
||||
CheckCacheCounters(options, 1, 0, 1, 0);
|
||||
CheckCompressedCacheCounters(options, 1, 0, 1, 0);
|
||||
}
|
||||
@@ -334,7 +333,7 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
|
||||
// Load last key block.
|
||||
ASSERT_EQ("Result incomplete: Insert failed due to LRU cache being full.",
|
||||
Get(std::to_string(kNumBlocks - 1)));
|
||||
Get(ToString(kNumBlocks - 1)));
|
||||
// Failure will also record the miss counter.
|
||||
CheckCacheCounters(options, 1, 0, 0, 1);
|
||||
CheckCompressedCacheCounters(options, 1, 0, 1, 0);
|
||||
@@ -343,7 +342,7 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
// cache and load into block cache.
|
||||
cache->SetStrictCapacityLimit(false);
|
||||
// Load last key block.
|
||||
ASSERT_EQ(value, Get(std::to_string(kNumBlocks - 1)));
|
||||
ASSERT_EQ(value, Get(ToString(kNumBlocks - 1)));
|
||||
CheckCacheCounters(options, 1, 0, 1, 0);
|
||||
CheckCompressedCacheCounters(options, 0, 1, 0, 0);
|
||||
}
|
||||
@@ -568,7 +567,7 @@ TEST_F(DBBlockCacheTest, FillCacheAndIterateDB) {
|
||||
Iterator* iter = nullptr;
|
||||
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(std::to_string(0));
|
||||
iter->Seek(ToString(0));
|
||||
while (iter->Valid()) {
|
||||
iter->Next();
|
||||
}
|
||||
@@ -646,10 +645,10 @@ TEST_F(DBBlockCacheTest, WarmCacheWithDataBlocksDuringFlush) {
|
||||
|
||||
std::string value(kValueSize, 'a');
|
||||
for (size_t i = 1; i <= kNumBlocks; i++) {
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Put(ToString(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(value, Get(ToString(i)));
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
}
|
||||
@@ -670,7 +669,7 @@ class DBBlockCacheTest1 : public DBTestBase,
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBBlockCacheTest1, DBBlockCacheTest1,
|
||||
::testing::Values(1, 2));
|
||||
::testing::Values(1, 2, 3));
|
||||
|
||||
TEST_P(DBBlockCacheTest1, WarmCacheWithBlocksDuringFlush) {
|
||||
Options options = CurrentOptions();
|
||||
@@ -686,10 +685,13 @@ TEST_P(DBBlockCacheTest1, WarmCacheWithBlocksDuringFlush) {
|
||||
table_options.partition_filters = true;
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
break;
|
||||
case 2: // full filter
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
case 2: // block-based filter
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||
break;
|
||||
case 3: // full filter
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
@@ -703,7 +705,7 @@ TEST_P(DBBlockCacheTest1, WarmCacheWithBlocksDuringFlush) {
|
||||
|
||||
std::string value(kValueSize, 'a');
|
||||
for (size_t i = 1; i <= kNumBlocks; i++) {
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Put(ToString(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
if (filter_type == 1) {
|
||||
@@ -715,7 +717,7 @@ TEST_P(DBBlockCacheTest1, WarmCacheWithBlocksDuringFlush) {
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_FILTER_ADD));
|
||||
}
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(value, Get(ToString(i)));
|
||||
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
@@ -770,12 +772,12 @@ TEST_F(DBBlockCacheTest, DynamicallyWarmCacheDuringFlush) {
|
||||
std::string value(kValueSize, 'a');
|
||||
|
||||
for (size_t i = 1; i <= 5; i++) {
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Put(ToString(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(value, Get(ToString(i)));
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(
|
||||
@@ -788,12 +790,12 @@ TEST_F(DBBlockCacheTest, DynamicallyWarmCacheDuringFlush) {
|
||||
{{"block_based_table_factory", "{prepopulate_block_cache=kDisable;}"}}));
|
||||
|
||||
for (size_t i = 6; i <= kNumBlocks; i++) {
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Put(ToString(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(value, Get(ToString(i)));
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(
|
||||
@@ -932,10 +934,7 @@ TEST_F(DBBlockCacheTest, AddRedundantStats) {
|
||||
int iterations_tested = 0;
|
||||
for (std::shared_ptr<Cache> base_cache :
|
||||
{NewLRUCache(capacity, num_shard_bits),
|
||||
NewClockCache(capacity, num_shard_bits),
|
||||
NewFastLRUCache(capacity, 1 /*estimated_value_size*/, num_shard_bits,
|
||||
false /*strict_capacity_limit*/,
|
||||
kDefaultCacheMetadataChargePolicy)}) {
|
||||
NewClockCache(capacity, num_shard_bits)}) {
|
||||
if (!base_cache) {
|
||||
// Skip clock cache when not supported
|
||||
continue;
|
||||
@@ -1290,14 +1289,10 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
|
||||
for (bool partition : {false, true}) {
|
||||
for (std::shared_ptr<Cache> cache :
|
||||
{NewLRUCache(capacity), NewClockCache(capacity)}) {
|
||||
// This test doesn't support FastLRUCache because the
|
||||
// keys used are not 16B long.
|
||||
// TODO(guido) Add support for FastLRUCache.
|
||||
if (!cache) {
|
||||
// Skip clock cache when not supported
|
||||
continue;
|
||||
}
|
||||
|
||||
++iterations_tested;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -1411,7 +1406,7 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
|
||||
|
||||
for (size_t i = 0; i < kNumCacheEntryRoles; ++i) {
|
||||
auto role = static_cast<CacheEntryRole>(i);
|
||||
EXPECT_EQ(std::to_string(expected[i]),
|
||||
EXPECT_EQ(ToString(expected[i]),
|
||||
values[BlockCacheEntryStatsMapKeys::EntryCount(role)]);
|
||||
}
|
||||
|
||||
@@ -1424,7 +1419,7 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
|
||||
// re-scanning stats, but not totally aggressive.
|
||||
// Within some time window, we will get cached entry stats
|
||||
env_->MockSleepForSeconds(1);
|
||||
EXPECT_EQ(std::to_string(prev_expected[static_cast<size_t>(
|
||||
EXPECT_EQ(ToString(prev_expected[static_cast<size_t>(
|
||||
CacheEntryRole::kWriteBuffer)]),
|
||||
values[BlockCacheEntryStatsMapKeys::EntryCount(
|
||||
CacheEntryRole::kWriteBuffer)]);
|
||||
@@ -1434,7 +1429,7 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
|
||||
ASSERT_TRUE(db_->GetMapProperty(DB::Properties::kBlockCacheEntryStats,
|
||||
&values));
|
||||
EXPECT_EQ(
|
||||
std::to_string(
|
||||
ToString(
|
||||
expected[static_cast<size_t>(CacheEntryRole::kWriteBuffer)]),
|
||||
values[BlockCacheEntryStatsMapKeys::EntryCount(
|
||||
CacheEntryRole::kWriteBuffer)]);
|
||||
@@ -1642,7 +1637,7 @@ TEST_P(DBBlockCacheKeyTest, StableCacheKeys) {
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
std::vector<std::string> external;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
std::string f = dbname_ + "/external" + std::to_string(i) + ".sst";
|
||||
std::string f = dbname_ + "/external" + ToString(i) + ".sst";
|
||||
external.push_back(f);
|
||||
ASSERT_OK(sst_file_writer.Open(f));
|
||||
ASSERT_OK(sst_file_writer.Put(Key(key_count), "abc"));
|
||||
@@ -1726,7 +1721,7 @@ class CacheKeyTest : public testing::Test {
|
||||
// Like SemiStructuredUniqueIdGen::GenerateNext
|
||||
tp_.db_session_id = EncodeSessionId(base_session_upper_,
|
||||
base_session_lower_ ^ session_counter_);
|
||||
tp_.db_id = std::to_string(db_id_);
|
||||
tp_.db_id = ToString(db_id_);
|
||||
tp_.orig_file_number = file_number_;
|
||||
bool is_stable;
|
||||
std::string cur_session_id = ""; // ignored
|
||||
|
||||
+202
-152
@@ -37,6 +37,8 @@ std::shared_ptr<const FilterPolicy> Create(double bits_per_key,
|
||||
return BloomLikeFilterPolicy::Create(name, bits_per_key);
|
||||
}
|
||||
const std::string kLegacyBloom = test::LegacyBloomFilterPolicy::kClassName();
|
||||
const std::string kDeprecatedBlock =
|
||||
DeprecatedBlockBasedBloomFilterPolicy::kClassName();
|
||||
const std::string kFastLocalBloom =
|
||||
test::FastLocalBloomFilterPolicy::kClassName();
|
||||
const std::string kStandard128Ribbon =
|
||||
@@ -109,7 +111,6 @@ TEST_P(DBBloomFilterTestDefFormatVersion, KeyMayExist) {
|
||||
options_override.filter_policy = Create(20, bfp_impl_);
|
||||
options_override.partition_filters = partition_filters_;
|
||||
options_override.metadata_block_size = 32;
|
||||
options_override.full_block_cache = true;
|
||||
Options options = CurrentOptions(options_override);
|
||||
if (partition_filters_) {
|
||||
auto* table_options =
|
||||
@@ -187,7 +188,7 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloomCustomPrefixExtractor) {
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
get_perf_context()->EnablePerLevelPerfContext();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
if (partition_filters) {
|
||||
bbto.partition_filters = true;
|
||||
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
@@ -254,15 +255,6 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloomCustomPrefixExtractor) {
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// No bloom on extractor changed, after re-open
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(10));
|
||||
Reopen(options);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foobarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 3);
|
||||
ASSERT_EQ(
|
||||
3,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
|
||||
get_perf_context()->Reset();
|
||||
}
|
||||
}
|
||||
@@ -274,7 +266,7 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloom) {
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
get_perf_context()->EnablePerLevelPerfContext();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
if (partition_filters) {
|
||||
bbto.partition_filters = true;
|
||||
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
@@ -338,7 +330,7 @@ TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
|
||||
get_perf_context()->EnablePerLevelPerfContext();
|
||||
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.whole_key_filtering = false;
|
||||
if (partition_filters) {
|
||||
bbto.partition_filters = true;
|
||||
@@ -754,6 +746,7 @@ TEST_P(DBBloomFilterTestWithParam, SkipFilterOnEssentiallyZeroBpk) {
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatDef, DBBloomFilterTestDefFormatVersion,
|
||||
::testing::Values(
|
||||
std::make_tuple(kDeprecatedBlock, false, test::kDefaultFormatVersion),
|
||||
std::make_tuple(kAutoBloom, true, test::kDefaultFormatVersion),
|
||||
std::make_tuple(kAutoBloom, false, test::kDefaultFormatVersion),
|
||||
std::make_tuple(kAutoRibbon, false, test::kDefaultFormatVersion)));
|
||||
@@ -761,6 +754,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatDef, DBBloomFilterTestWithParam,
|
||||
::testing::Values(
|
||||
std::make_tuple(kDeprecatedBlock, false, test::kDefaultFormatVersion),
|
||||
std::make_tuple(kAutoBloom, true, test::kDefaultFormatVersion),
|
||||
std::make_tuple(kAutoBloom, false, test::kDefaultFormatVersion),
|
||||
std::make_tuple(kAutoRibbon, false, test::kDefaultFormatVersion)));
|
||||
@@ -768,6 +762,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatLatest, DBBloomFilterTestWithParam,
|
||||
::testing::Values(
|
||||
std::make_tuple(kDeprecatedBlock, false, kLatestFormatVersion),
|
||||
std::make_tuple(kAutoBloom, true, kLatestFormatVersion),
|
||||
std::make_tuple(kAutoBloom, false, kLatestFormatVersion),
|
||||
std::make_tuple(kAutoRibbon, false, kLatestFormatVersion)));
|
||||
@@ -833,6 +828,8 @@ 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},
|
||||
@@ -860,7 +857,7 @@ TEST_F(DBBloomFilterTest, BloomFilterCompatibility) {
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
|
||||
std::string prefix = std::to_string(i) + "_";
|
||||
std::string prefix = ToString(i) + "_";
|
||||
ASSERT_OK(Put(prefix + "A", "val"));
|
||||
ASSERT_OK(Put(prefix + "Z", "val"));
|
||||
ASSERT_OK(Flush());
|
||||
@@ -875,39 +872,116 @@ TEST_F(DBBloomFilterTest, BloomFilterCompatibility) {
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
for (size_t j = 0; j < kCompatibilityConfigs.size(); ++j) {
|
||||
std::string prefix = std::to_string(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),
|
||||
2);
|
||||
j == 0 ? 0 : 2);
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A cache wrapper that tracks peaks and increments of filter
|
||||
* construction cache reservation.
|
||||
* p0
|
||||
* / \ p1
|
||||
* / \ /\
|
||||
* / \/ \
|
||||
* a / b \
|
||||
* peaks = {p0, p1}
|
||||
* increments = {p1-a, p2-b}
|
||||
*/
|
||||
class FilterConstructResPeakTrackingCache : public CacheWrapper {
|
||||
public:
|
||||
explicit FilterConstructResPeakTrackingCache(std::shared_ptr<Cache> target)
|
||||
: CacheWrapper(std::move(target)),
|
||||
cur_cache_res_(0),
|
||||
cache_res_peak_(0),
|
||||
cache_res_increment_(0),
|
||||
last_peak_tracked_(false),
|
||||
cache_res_increments_sum_(0) {}
|
||||
|
||||
using Cache::Insert;
|
||||
Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override {
|
||||
Status s = target_->Insert(key, value, charge, deleter, handle, priority);
|
||||
if (deleter == kNoopDeleterForFilterConstruction) {
|
||||
if (last_peak_tracked_) {
|
||||
cache_res_peak_ = 0;
|
||||
cache_res_increment_ = 0;
|
||||
last_peak_tracked_ = false;
|
||||
}
|
||||
cur_cache_res_ += charge;
|
||||
cache_res_peak_ = std::max(cache_res_peak_, cur_cache_res_);
|
||||
cache_res_increment_ += charge;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
using Cache::Release;
|
||||
bool Release(Handle* handle, bool erase_if_last_ref = false) override {
|
||||
auto deleter = GetDeleter(handle);
|
||||
if (deleter == kNoopDeleterForFilterConstruction) {
|
||||
if (!last_peak_tracked_) {
|
||||
cache_res_peaks_.push_back(cache_res_peak_);
|
||||
cache_res_increments_sum_ += cache_res_increment_;
|
||||
last_peak_tracked_ = true;
|
||||
}
|
||||
cur_cache_res_ -= GetCharge(handle);
|
||||
}
|
||||
bool is_successful = target_->Release(handle, erase_if_last_ref);
|
||||
return is_successful;
|
||||
}
|
||||
|
||||
std::deque<std::size_t> GetReservedCachePeaks() { return cache_res_peaks_; }
|
||||
|
||||
std::size_t GetReservedCacheIncrementSum() {
|
||||
return cache_res_increments_sum_;
|
||||
}
|
||||
|
||||
private:
|
||||
static const Cache::DeleterFn kNoopDeleterForFilterConstruction;
|
||||
|
||||
std::size_t cur_cache_res_;
|
||||
std::size_t cache_res_peak_;
|
||||
std::size_t cache_res_increment_;
|
||||
bool last_peak_tracked_;
|
||||
std::deque<std::size_t> cache_res_peaks_;
|
||||
std::size_t cache_res_increments_sum_;
|
||||
};
|
||||
|
||||
const Cache::DeleterFn
|
||||
FilterConstructResPeakTrackingCache::kNoopDeleterForFilterConstruction =
|
||||
CacheReservationManagerImpl<
|
||||
CacheEntryRole::kFilterConstruction>::TEST_GetNoopDeleterForRole();
|
||||
|
||||
// To align with the type of hash entry being reserved in implementation.
|
||||
using FilterConstructionReserveMemoryHash = uint64_t;
|
||||
|
||||
class ChargeFilterConstructionTestWithParam
|
||||
class DBFilterConstructionReserveMemoryTestWithParam
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<
|
||||
CacheEntryRoleOptions::Decision, std::string, bool, bool>> {
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<bool, std::string, bool, bool>> {
|
||||
public:
|
||||
ChargeFilterConstructionTestWithParam()
|
||||
DBFilterConstructionReserveMemoryTestWithParam()
|
||||
: DBTestBase("db_bloom_filter_tests",
|
||||
/*env_do_fsync=*/true),
|
||||
num_key_(0),
|
||||
charge_filter_construction_(std::get<0>(GetParam())),
|
||||
reserve_table_builder_memory_(std::get<0>(GetParam())),
|
||||
policy_(std::get<1>(GetParam())),
|
||||
partition_filters_(std::get<2>(GetParam())),
|
||||
detect_filter_construct_corruption_(std::get<3>(GetParam())) {
|
||||
if (charge_filter_construction_ ==
|
||||
CacheEntryRoleOptions::Decision::kDisabled ||
|
||||
if (!reserve_table_builder_memory_ || policy_ == kDeprecatedBlock ||
|
||||
policy_ == kLegacyBloom) {
|
||||
// For these cases, we only interested in whether filter construction
|
||||
// cache charging happens instead of its accuracy. Therefore we don't
|
||||
// cache resevation happens instead of its accuracy. Therefore we don't
|
||||
// need many keys.
|
||||
num_key_ = 5;
|
||||
} else if (partition_filters_) {
|
||||
@@ -922,11 +996,11 @@ class ChargeFilterConstructionTestWithParam
|
||||
sizeof(FilterConstructionReserveMemoryHash);
|
||||
} else if (policy_ == kFastLocalBloom) {
|
||||
// For Bloom Filter + FullFilter case, since we design the num_key_ to
|
||||
// make hash entry cache charging be a multiple of dummy entries, the
|
||||
// make hash entry cache reservation be a multiple of dummy entries, the
|
||||
// correct behavior of charging final filter on top of it will trigger at
|
||||
// least another dummy entry insertion. Therefore we can assert that
|
||||
// behavior and we don't need a large number of keys to verify we
|
||||
// indeed charge the final filter for in cache, even though final
|
||||
// indeed charge the final filter for cache reservation, even though final
|
||||
// filter is a lot smaller than hash entries.
|
||||
num_key_ = 1 *
|
||||
CacheReservationManagerImpl<
|
||||
@@ -936,7 +1010,7 @@ class ChargeFilterConstructionTestWithParam
|
||||
// For Ribbon Filter + FullFilter case, we need a large enough number of
|
||||
// keys so that charging final filter after releasing the hash entries
|
||||
// reservation will trigger at least another dummy entry (or equivalently
|
||||
// to saying, causing another peak in cache charging) as banding
|
||||
// to saying, causing another peak in cache reservation) as banding
|
||||
// reservation might not be a multiple of dummy entry.
|
||||
num_key_ = 12 *
|
||||
CacheReservationManagerImpl<
|
||||
@@ -952,9 +1026,7 @@ class ChargeFilterConstructionTestWithParam
|
||||
// calculation.
|
||||
constexpr std::size_t kCacheCapacity = 100 * 1024 * 1024;
|
||||
|
||||
table_options.cache_usage_options.options_overrides.insert(
|
||||
{CacheEntryRole::kFilterConstruction,
|
||||
{/*.charged = */ charge_filter_construction_}});
|
||||
table_options.reserve_table_builder_memory = reserve_table_builder_memory_;
|
||||
table_options.filter_policy = Create(10, policy_);
|
||||
table_options.partition_filters = partition_filters_;
|
||||
if (table_options.partition_filters) {
|
||||
@@ -972,8 +1044,7 @@ class ChargeFilterConstructionTestWithParam
|
||||
lo.capacity = kCacheCapacity;
|
||||
lo.num_shard_bits = 0; // 2^0 shard
|
||||
lo.strict_capacity_limit = true;
|
||||
cache_ = std::make_shared<
|
||||
TargetCacheChargeTrackingCache<CacheEntryRole::kFilterConstruction>>(
|
||||
cache_ = std::make_shared<FilterConstructResPeakTrackingCache>(
|
||||
(NewLRUCache(lo)));
|
||||
table_options.block_cache = cache_;
|
||||
|
||||
@@ -982,71 +1053,56 @@ class ChargeFilterConstructionTestWithParam
|
||||
|
||||
std::size_t GetNumKey() { return num_key_; }
|
||||
|
||||
CacheEntryRoleOptions::Decision ChargeFilterConstructMemory() {
|
||||
return charge_filter_construction_;
|
||||
}
|
||||
bool ReserveTableBuilderMemory() { return reserve_table_builder_memory_; }
|
||||
|
||||
std::string GetFilterPolicy() { return policy_; }
|
||||
|
||||
bool PartitionFilters() { return partition_filters_; }
|
||||
|
||||
std::shared_ptr<
|
||||
TargetCacheChargeTrackingCache<CacheEntryRole::kFilterConstruction>>
|
||||
GetCache() {
|
||||
std::shared_ptr<FilterConstructResPeakTrackingCache>
|
||||
GetFilterConstructResPeakTrackingCache() {
|
||||
return cache_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t num_key_;
|
||||
CacheEntryRoleOptions::Decision charge_filter_construction_;
|
||||
bool reserve_table_builder_memory_;
|
||||
std::string policy_;
|
||||
bool partition_filters_;
|
||||
std::shared_ptr<
|
||||
TargetCacheChargeTrackingCache<CacheEntryRole::kFilterConstruction>>
|
||||
cache_;
|
||||
std::shared_ptr<FilterConstructResPeakTrackingCache> cache_;
|
||||
bool detect_filter_construct_corruption_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ChargeFilterConstructionTestWithParam,
|
||||
ChargeFilterConstructionTestWithParam,
|
||||
::testing::Values(
|
||||
std::make_tuple(CacheEntryRoleOptions::Decision::kDisabled,
|
||||
kFastLocalBloom, false, false),
|
||||
DBFilterConstructionReserveMemoryTestWithParam,
|
||||
DBFilterConstructionReserveMemoryTestWithParam,
|
||||
::testing::Values(std::make_tuple(false, kFastLocalBloom, false, false),
|
||||
|
||||
std::make_tuple(CacheEntryRoleOptions::Decision::kEnabled,
|
||||
kFastLocalBloom, false, false),
|
||||
std::make_tuple(CacheEntryRoleOptions::Decision::kEnabled,
|
||||
kFastLocalBloom, false, true),
|
||||
std::make_tuple(CacheEntryRoleOptions::Decision::kEnabled,
|
||||
kFastLocalBloom, true, false),
|
||||
std::make_tuple(CacheEntryRoleOptions::Decision::kEnabled,
|
||||
kFastLocalBloom, true, true),
|
||||
std::make_tuple(true, kFastLocalBloom, false, false),
|
||||
std::make_tuple(true, kFastLocalBloom, false, true),
|
||||
std::make_tuple(true, kFastLocalBloom, true, false),
|
||||
std::make_tuple(true, kFastLocalBloom, true, true),
|
||||
|
||||
std::make_tuple(CacheEntryRoleOptions::Decision::kEnabled,
|
||||
kStandard128Ribbon, false, false),
|
||||
std::make_tuple(CacheEntryRoleOptions::Decision::kEnabled,
|
||||
kStandard128Ribbon, false, true),
|
||||
std::make_tuple(CacheEntryRoleOptions::Decision::kEnabled,
|
||||
kStandard128Ribbon, true, false),
|
||||
std::make_tuple(CacheEntryRoleOptions::Decision::kEnabled,
|
||||
kStandard128Ribbon, true, true),
|
||||
std::make_tuple(true, kStandard128Ribbon, false, false),
|
||||
std::make_tuple(true, kStandard128Ribbon, false, true),
|
||||
std::make_tuple(true, kStandard128Ribbon, true, false),
|
||||
std::make_tuple(true, kStandard128Ribbon, true, true),
|
||||
|
||||
std::make_tuple(CacheEntryRoleOptions::Decision::kEnabled, kLegacyBloom,
|
||||
false, false)));
|
||||
std::make_tuple(true, kDeprecatedBlock, false, false),
|
||||
std::make_tuple(true, kLegacyBloom, false, false)));
|
||||
|
||||
// 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
|
||||
// keys, big enough so that its cache charging triggers dummy entry insertion
|
||||
// keys, big enough so that its cache reservation triggers dummy entry insertion
|
||||
// and becomes observable in the test.
|
||||
//
|
||||
// However, inserting that many keys slows down this test and leaves future
|
||||
// developers an opportunity to speed it up.
|
||||
//
|
||||
// Possible approaches & challenges:
|
||||
// 1. Use sync point during cache charging of filter construction
|
||||
// 1. Use sync point during cache reservation of filter construction
|
||||
//
|
||||
// Benefit: It does not rely on triggering dummy entry insertion
|
||||
// but the sync point to verify small memory user is charged correctly.
|
||||
@@ -1055,7 +1111,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
//
|
||||
// 2. Make dummy entry size configurable and set it small in the test
|
||||
//
|
||||
// Benefit: It increases the precision of cache charging and therefore
|
||||
// Benefit: It increases the precision of cache reservation and therefore
|
||||
// small memory usage can still trigger insertion of dummy entry.
|
||||
//
|
||||
// Challenge: change CacheReservationManager related APIs and a hack
|
||||
@@ -1063,17 +1119,16 @@ INSTANTIATE_TEST_CASE_P(
|
||||
// CacheReservationManager used in filter construction for testing
|
||||
// since CacheReservationManager is not exposed at the high level.
|
||||
//
|
||||
TEST_P(ChargeFilterConstructionTestWithParam, Basic) {
|
||||
TEST_P(DBFilterConstructionReserveMemoryTestWithParam, ReserveMemory) {
|
||||
Options options = CurrentOptions();
|
||||
// We set write_buffer_size big enough so that in the case where there is
|
||||
// filter construction cache charging, flush won't be triggered before we
|
||||
// filter construction cache reservation, flush won't be triggered before we
|
||||
// manually trigger it for clean testing
|
||||
options.write_buffer_size = 640 << 20;
|
||||
BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
std::shared_ptr<
|
||||
TargetCacheChargeTrackingCache<CacheEntryRole::kFilterConstruction>>
|
||||
cache = GetCache();
|
||||
std::shared_ptr<FilterConstructResPeakTrackingCache> cache =
|
||||
GetFilterConstructResPeakTrackingCache();
|
||||
options.create_if_missing = true;
|
||||
// Disable auto compaction to prevent its unexpected side effect
|
||||
// to the number of keys per partition designed by us in the test
|
||||
@@ -1084,34 +1139,34 @@ TEST_P(ChargeFilterConstructionTestWithParam, Basic) {
|
||||
ASSERT_OK(Put(Key(i), Key(i)));
|
||||
}
|
||||
|
||||
ASSERT_EQ(cache->GetChargedCacheIncrementSum(), 0)
|
||||
ASSERT_EQ(cache->GetReservedCacheIncrementSum(), 0)
|
||||
<< "Flush was triggered too early in the test case with filter "
|
||||
"construction cache charging - please make sure no flush triggered "
|
||||
"construction cache reservation - please make sure no flush triggered "
|
||||
"during the key insertions above";
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
bool charge_filter_construction = (ChargeFilterConstructMemory() ==
|
||||
CacheEntryRoleOptions::Decision::kEnabled);
|
||||
bool reserve_table_builder_memory = ReserveTableBuilderMemory();
|
||||
std::string policy = GetFilterPolicy();
|
||||
bool partition_filters = PartitionFilters();
|
||||
bool detect_filter_construct_corruption =
|
||||
table_options.detect_filter_construct_corruption;
|
||||
|
||||
std::deque<std::size_t> filter_construction_cache_res_peaks =
|
||||
cache->GetChargedCachePeaks();
|
||||
cache->GetReservedCachePeaks();
|
||||
std::size_t filter_construction_cache_res_increments_sum =
|
||||
cache->GetChargedCacheIncrementSum();
|
||||
cache->GetReservedCacheIncrementSum();
|
||||
|
||||
if (!charge_filter_construction) {
|
||||
if (!reserve_table_builder_memory) {
|
||||
EXPECT_EQ(filter_construction_cache_res_peaks.size(), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (policy == kLegacyBloom) {
|
||||
if (policy == kDeprecatedBlock || policy == kLegacyBloom) {
|
||||
EXPECT_EQ(filter_construction_cache_res_peaks.size(), 0)
|
||||
<< "There shouldn't be filter construction cache charging as this "
|
||||
"feature does not support kLegacyBloom";
|
||||
<< "There shouldn't be filter construction cache reservation as this "
|
||||
"feature does not support kDeprecatedBlock "
|
||||
"nor kLegacyBloom";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1183,14 +1238,14 @@ TEST_P(ChargeFilterConstructionTestWithParam, Basic) {
|
||||
*/
|
||||
if (!partition_filters) {
|
||||
EXPECT_EQ(filter_construction_cache_res_peaks.size(), 1)
|
||||
<< "Filter construction cache charging should have only 1 peak in "
|
||||
<< "Filter construction cache reservation should have only 1 peak in "
|
||||
"case: kFastLocalBloom + FullFilter";
|
||||
std::size_t filter_construction_cache_res_peak =
|
||||
filter_construction_cache_res_peaks[0];
|
||||
EXPECT_GT(filter_construction_cache_res_peak,
|
||||
predicted_hash_entries_cache_res)
|
||||
<< "The testing number of hash entries is designed to make hash "
|
||||
"entries cache charging be multiples of dummy entries"
|
||||
"entries cache reservation be multiples of dummy entries"
|
||||
" so the correct behavior of charging final filter on top of it"
|
||||
" should've triggered at least another dummy entry insertion";
|
||||
|
||||
@@ -1203,7 +1258,7 @@ TEST_P(ChargeFilterConstructionTestWithParam, Basic) {
|
||||
return;
|
||||
} else {
|
||||
EXPECT_GE(filter_construction_cache_res_peaks.size(), 2)
|
||||
<< "Filter construction cache charging should have multiple peaks "
|
||||
<< "Filter construction cache reservation should have multiple peaks "
|
||||
"in case: kFastLocalBloom + "
|
||||
"PartitionedFilter";
|
||||
std::size_t predicted_filter_construction_cache_res_increments_sum =
|
||||
@@ -1310,11 +1365,11 @@ TEST_P(ChargeFilterConstructionTestWithParam, Basic) {
|
||||
CacheReservationManagerImpl<
|
||||
CacheEntryRole::kFilterConstruction>::GetDummyEntrySize()),
|
||||
1)
|
||||
<< "Final filter cache charging too small for this test - please "
|
||||
<< "Final filter cache reservation too small for this test - please "
|
||||
"increase the number of keys";
|
||||
if (!detect_filter_construct_corruption) {
|
||||
EXPECT_EQ(filter_construction_cache_res_peaks.size(), 2)
|
||||
<< "Filter construction cache charging should have 2 peaks in "
|
||||
<< "Filter construction cache reservation should have 2 peaks in "
|
||||
"case: kStandard128Ribbon + "
|
||||
"FullFilter. "
|
||||
"The second peak is resulted from charging the final filter "
|
||||
@@ -1333,7 +1388,7 @@ TEST_P(ChargeFilterConstructionTestWithParam, Basic) {
|
||||
predicted_filter_construction_cache_res_peak * 1.1);
|
||||
} else {
|
||||
EXPECT_EQ(filter_construction_cache_res_peaks.size(), 1)
|
||||
<< "Filter construction cache charging should have 1 peaks in "
|
||||
<< "Filter construction cache reservation should have 1 peaks in "
|
||||
"case: kStandard128Ribbon + FullFilter "
|
||||
"+ detect_filter_construct_corruption. "
|
||||
"The previous second peak now disappears since we don't "
|
||||
@@ -1354,13 +1409,13 @@ TEST_P(ChargeFilterConstructionTestWithParam, Basic) {
|
||||
} else {
|
||||
if (!detect_filter_construct_corruption) {
|
||||
EXPECT_GE(filter_construction_cache_res_peaks.size(), 3)
|
||||
<< "Filter construction cache charging should have more than 3 "
|
||||
<< "Filter construction cache reservation should have more than 3 "
|
||||
"peaks "
|
||||
"in case: kStandard128Ribbon + "
|
||||
"PartitionedFilter";
|
||||
} else {
|
||||
EXPECT_GE(filter_construction_cache_res_peaks.size(), 2)
|
||||
<< "Filter construction cache charging should have more than 2 "
|
||||
<< "Filter construction cache reservation should have more than 2 "
|
||||
"peaks "
|
||||
"in case: kStandard128Ribbon + "
|
||||
"PartitionedFilter + detect_filter_construct_corruption";
|
||||
@@ -1657,11 +1712,11 @@ class TestingContextCustomFilterPolicy
|
||||
test_report_ +=
|
||||
OptionsHelper::compaction_style_to_string[context.compaction_style];
|
||||
test_report_ += ",n=";
|
||||
test_report_ += std::to_string(context.num_levels);
|
||||
test_report_ += ROCKSDB_NAMESPACE::ToString(context.num_levels);
|
||||
test_report_ += ",l=";
|
||||
test_report_ += std::to_string(context.level_at_creation);
|
||||
test_report_ += ROCKSDB_NAMESPACE::ToString(context.level_at_creation);
|
||||
test_report_ += ",b=";
|
||||
test_report_ += std::to_string(int{context.is_bottommost});
|
||||
test_report_ += ROCKSDB_NAMESPACE::ToString(int{context.is_bottommost});
|
||||
test_report_ += ",r=";
|
||||
test_report_ += table_file_creation_reason_to_string[context.reason];
|
||||
test_report_ += "\n";
|
||||
@@ -1796,9 +1851,10 @@ class SliceTransformLimitedDomain : public SliceTransform {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBBloomFilterTest, PrefixExtractorWithFilter1) {
|
||||
TEST_F(DBBloomFilterTest, PrefixExtractorFullFilter) {
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(ROCKSDB_NAMESPACE::NewBloomFilterPolicy(10));
|
||||
// Full Filter Block
|
||||
bbto.filter_policy.reset(ROCKSDB_NAMESPACE::NewBloomFilterPolicy(10, false));
|
||||
bbto.whole_key_filtering = false;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -1824,9 +1880,10 @@ TEST_F(DBBloomFilterTest, PrefixExtractorWithFilter1) {
|
||||
ASSERT_EQ(Get("zzzzz_AAAA"), "val5");
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, PrefixExtractorWithFilter2) {
|
||||
TEST_F(DBBloomFilterTest, PrefixExtractorBlockFilter) {
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(ROCKSDB_NAMESPACE::NewBloomFilterPolicy(10));
|
||||
// Block Filter Block
|
||||
bbto.filter_policy.reset(ROCKSDB_NAMESPACE::NewBloomFilterPolicy(10, true));
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.prefix_extractor = std::make_shared<SliceTransformLimitedDomain>();
|
||||
@@ -2217,6 +2274,7 @@ class BloomStatsTestWithParam
|
||||
} else {
|
||||
BlockBasedTableOptions table_options;
|
||||
if (partition_filters_) {
|
||||
assert(bfp_impl_ != kDeprecatedBlock);
|
||||
table_options.partition_filters = partition_filters_;
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
@@ -2341,7 +2399,9 @@ TEST_P(BloomStatsTestWithParam, BloomStatsTestWithIter) {
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(value3, iter->value().ToString());
|
||||
uint64_t expected_hits = 2;
|
||||
// The seek doesn't check block-based bloom filter because last index key
|
||||
// starts with the same prefix we're seeking to.
|
||||
uint64_t expected_hits = bfp_impl_ == kDeprecatedBlock ? 1 : 2;
|
||||
ASSERT_EQ(expected_hits, get_perf_context()->bloom_sst_hit_count);
|
||||
|
||||
iter->Seek(key2);
|
||||
@@ -2353,7 +2413,8 @@ TEST_P(BloomStatsTestWithParam, BloomStatsTestWithIter) {
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
BloomStatsTestWithParam, BloomStatsTestWithParam,
|
||||
::testing::Values(std::make_tuple(kLegacyBloom, false),
|
||||
::testing::Values(std::make_tuple(kDeprecatedBlock, false),
|
||||
std::make_tuple(kLegacyBloom, false),
|
||||
std::make_tuple(kLegacyBloom, true),
|
||||
std::make_tuple(kFastLocalBloom, false),
|
||||
std::make_tuple(kFastLocalBloom, true),
|
||||
@@ -2478,7 +2539,7 @@ TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.optimize_filters_for_hits = true;
|
||||
@@ -2666,6 +2727,7 @@ int CountIter(std::unique_ptr<Iterator>& iter, const Slice& key) {
|
||||
// as the upper bound and two keys are adjacent according to the comparator.
|
||||
TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
for (const auto& bfp_impl : BloomLikeFilterPolicy::GetAllFixedImpls()) {
|
||||
int using_full_builder = bfp_impl != kDeprecatedBlock;
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = CurrentOptions().env;
|
||||
@@ -2717,7 +2779,8 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abcdxx00"), 4);
|
||||
// should check bloom filter since upper bound meets requirement
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 3);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
2 + using_full_builder);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
}
|
||||
{
|
||||
@@ -2730,7 +2793,8 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abcdxx01"), 4);
|
||||
// should skip bloom filter since upper bound is too long
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 3);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
2 + using_full_builder);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
}
|
||||
{
|
||||
@@ -2743,7 +2807,8 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
ASSERT_EQ(CountIter(iter, "abcdxx02"), 4);
|
||||
// should check bloom filter since upper bound matches transformed seek
|
||||
// key
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
2 + using_full_builder * 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
}
|
||||
{
|
||||
@@ -2756,7 +2821,8 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "aaaaaaaa"), 0);
|
||||
// should skip bloom filter since mismatch is found
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
2 + using_full_builder * 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
}
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "fixed:3"}}));
|
||||
@@ -2769,52 +2835,23 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abc"), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
2 + using_full_builder * 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
}
|
||||
// Same with re-open
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
Reopen(options);
|
||||
{
|
||||
Slice upper_bound("abd");
|
||||
ReadOptions read_options;
|
||||
read_options.prefix_same_as_start = true;
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abc"), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
}
|
||||
// Set back to capped:4 and verify BF is always read
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(4));
|
||||
Reopen(options);
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "capped:4"}}));
|
||||
{
|
||||
// set back to capped:4 and verify BF is always read
|
||||
Slice upper_bound("abd");
|
||||
ReadOptions read_options;
|
||||
read_options.prefix_same_as_start = true;
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abc"), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 5);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
3 + using_full_builder * 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
}
|
||||
// Same if there's a problem initally loading prefix transform
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTable::Open::ForceNullTablePrefixExtractor",
|
||||
[&](void* arg) { *static_cast<bool*>(arg) = true; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Reopen(options);
|
||||
{
|
||||
Slice upper_bound("abd");
|
||||
ReadOptions read_options;
|
||||
read_options.prefix_same_as_start = true;
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abc"), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 6);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 2);
|
||||
}
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2822,6 +2859,7 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
// verify iterators can read all SST files using the latest config.
|
||||
TEST_F(DBBloomFilterTest, DynamicBloomFilterMultipleSST) {
|
||||
for (const auto& bfp_impl : BloomLikeFilterPolicy::GetAllFixedImpls()) {
|
||||
int using_full_builder = bfp_impl != kDeprecatedBlock;
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.create_if_missing = true;
|
||||
@@ -2855,9 +2893,11 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterMultipleSST) {
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "foo"), 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
1 + using_full_builder);
|
||||
ASSERT_EQ(CountIter(iter, "gpk"), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
1 + using_full_builder);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
|
||||
// second SST with capped:3 BF
|
||||
@@ -2870,12 +2910,14 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterMultipleSST) {
|
||||
// BF is cappped:3 now
|
||||
std::unique_ptr<Iterator> iter_tmp(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter_tmp, "foo"), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
2 + using_full_builder * 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
ASSERT_EQ(CountIter(iter_tmp, "gpk"), 0);
|
||||
// both counters are incremented because BF is "not changed" for 1 of the
|
||||
// 2 SST files, so filter is checked once and found no match.
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 5);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
3 + using_full_builder * 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
}
|
||||
|
||||
@@ -2893,21 +2935,25 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterMultipleSST) {
|
||||
std::unique_ptr<Iterator> iter_tmp(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter_tmp, "foo"), 9);
|
||||
// the first and last BF are checked
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 7);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
4 + using_full_builder * 3);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
ASSERT_EQ(CountIter(iter_tmp, "gpk"), 0);
|
||||
// only last BF is checked and not found
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 8);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
5 + using_full_builder * 3);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 2);
|
||||
}
|
||||
|
||||
// iter_old can only see the first SST, so checked plus 1
|
||||
ASSERT_EQ(CountIter(iter_old, "foo"), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 9);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
6 + using_full_builder * 3);
|
||||
// iter was created after the first setoptions call so only full filter
|
||||
// will check the filter
|
||||
ASSERT_EQ(CountIter(iter, "foo"), 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 10);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
6 + using_full_builder * 4);
|
||||
|
||||
{
|
||||
// keys in all three SSTs are visible to iterator
|
||||
@@ -2915,10 +2961,12 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterMultipleSST) {
|
||||
// so +2 for checked counter
|
||||
std::unique_ptr<Iterator> iter_all(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter_all, "foo"), 9);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 12);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
7 + using_full_builder * 5);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 2);
|
||||
ASSERT_EQ(CountIter(iter_all, "gpk"), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 13);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
8 + using_full_builder * 5);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 3);
|
||||
}
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "capped:3"}}));
|
||||
@@ -2929,10 +2977,12 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterMultipleSST) {
|
||||
ASSERT_EQ(CountIter(iter_all, "foo"), 6);
|
||||
// all three SST are checked because the current options has the same as
|
||||
// the remaining SST (capped:3)
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 16);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
9 + using_full_builder * 7);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 3);
|
||||
ASSERT_EQ(CountIter(iter_all, "gpk"), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 17);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED),
|
||||
10 + using_full_builder * 7);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 4);
|
||||
}
|
||||
// TODO(Zhongyi): Maybe also need to add Get calls to test point look up?
|
||||
|
||||
@@ -454,7 +454,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterDeletesAll) {
|
||||
// put some data
|
||||
for (int table = 0; table < 4; ++table) {
|
||||
for (int i = 0; i < 10 + table; ++i) {
|
||||
ASSERT_OK(Put(std::to_string(table * 100 + i), "val"));
|
||||
ASSERT_OK(Put(ToString(table * 100 + i), "val"));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -755,7 +755,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextCfId) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Compaction filters aplies to all records, regardless snapshots.
|
||||
TEST_F(DBTestCompactionFilter, CompactionFilterIgnoreSnapshot) {
|
||||
std::string five = std::to_string(5);
|
||||
std::string five = ToString(5);
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_filter_factory = std::make_shared<DeleteISFilterFactory>();
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -766,7 +766,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterIgnoreSnapshot) {
|
||||
const Snapshot* snapshot = nullptr;
|
||||
for (int table = 0; table < 4; ++table) {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
ASSERT_OK(Put(std::to_string(table * 100 + i), "val"));
|
||||
ASSERT_OK(Put(ToString(table * 100 + i), "val"));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
@@ -968,71 +968,6 @@ TEST_F(DBTestCompactionFilter, IgnoreSnapshotsFalseRecovery) {
|
||||
ASSERT_TRUE(TryReopen(options).IsNotSupported());
|
||||
}
|
||||
|
||||
TEST_F(DBTestCompactionFilter, DropKeyWithSingleDelete) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("a", "v0"));
|
||||
ASSERT_OK(Put("b", "v0"));
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
|
||||
ASSERT_OK(SingleDelete("b"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
{
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = options.num_levels - 1;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
}
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
Close();
|
||||
|
||||
class DeleteFilterV2 : public CompactionFilter {
|
||||
public:
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType /*value_type*/,
|
||||
const Slice& /*existing_value*/,
|
||||
std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
if (key.starts_with("b")) {
|
||||
return Decision::kPurge;
|
||||
}
|
||||
return Decision::kRemove;
|
||||
}
|
||||
|
||||
const char* Name() const override { return "DeleteFilterV2"; }
|
||||
} delete_filter_v2;
|
||||
|
||||
options.compaction_filter = &delete_filter_v2;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("b", "v1"));
|
||||
ASSERT_OK(Put("x", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put("r", "v1"));
|
||||
ASSERT_OK(Put("z", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
Close();
|
||||
|
||||
options.compaction_filter = nullptr;
|
||||
Reopen(options);
|
||||
ASSERT_OK(SingleDelete("b"));
|
||||
ASSERT_OK(Flush());
|
||||
{
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+13
-106
@@ -2409,30 +2409,6 @@ TEST_P(DBCompactionTestWithParam, LevelCompactionCFPathUse) {
|
||||
|
||||
check_getvalues();
|
||||
|
||||
{ // Also verify GetLiveFilesStorageInfo with db_paths / cf_paths
|
||||
std::vector<LiveFileStorageInfo> new_infos;
|
||||
LiveFilesStorageInfoOptions lfsio;
|
||||
lfsio.wal_size_for_flush = UINT64_MAX; // no flush
|
||||
ASSERT_OK(db_->GetLiveFilesStorageInfo(lfsio, &new_infos));
|
||||
std::unordered_map<std::string, int> live_sst_by_dir;
|
||||
for (auto& info : new_infos) {
|
||||
if (info.file_type == kTableFile) {
|
||||
live_sst_by_dir[info.directory]++;
|
||||
// Verify file on disk (no directory confusion)
|
||||
uint64_t size;
|
||||
ASSERT_OK(env_->GetFileSize(
|
||||
info.directory + "/" + info.relative_filename, &size));
|
||||
ASSERT_EQ(info.size, size);
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(3U * 3U, live_sst_by_dir.size());
|
||||
for (auto& paths : {options.db_paths, cf_opt1.cf_paths, cf_opt2.cf_paths}) {
|
||||
ASSERT_EQ(1, live_sst_by_dir[paths[0].path]);
|
||||
ASSERT_EQ(4, live_sst_by_dir[paths[1].path]);
|
||||
ASSERT_EQ(2, live_sst_by_dir[paths[2].path]);
|
||||
}
|
||||
}
|
||||
|
||||
ReopenWithColumnFamilies({"default", "one", "two"}, option_vector);
|
||||
|
||||
check_getvalues();
|
||||
@@ -2817,7 +2793,7 @@ TEST_P(DBCompactionTestWithParam, DISABLED_CompactFilesOnLevelCompaction) {
|
||||
|
||||
Random rnd(301);
|
||||
for (int key = 64 * kEntriesPerBuffer; key >= 0; --key) {
|
||||
ASSERT_OK(Put(1, std::to_string(key), rnd.RandomString(kTestValueSize)));
|
||||
ASSERT_OK(Put(1, ToString(key), rnd.RandomString(kTestValueSize)));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[1]));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -2849,7 +2825,7 @@ TEST_P(DBCompactionTestWithParam, DISABLED_CompactFilesOnLevelCompaction) {
|
||||
|
||||
// make sure all key-values are still there.
|
||||
for (int key = 64 * kEntriesPerBuffer; key >= 0; --key) {
|
||||
ASSERT_NE(Get(1, std::to_string(key)), "NOT_FOUND");
|
||||
ASSERT_NE(Get(1, ToString(key)), "NOT_FOUND");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4404,8 +4380,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithCompactionFilters) {
|
||||
for (CompactionFilterType comp_filter_type :
|
||||
{kUseCompactionFilter, kUseCompactionFilterFactory}) {
|
||||
// Assert that periodic compactions are not enabled.
|
||||
ASSERT_EQ(std::numeric_limits<uint64_t>::max() - 1,
|
||||
options.periodic_compaction_seconds);
|
||||
ASSERT_EQ(port::kMaxUint64 - 1, options.periodic_compaction_seconds);
|
||||
|
||||
if (comp_filter_type == kUseCompactionFilter) {
|
||||
options.compaction_filter = &test_compaction_filter;
|
||||
@@ -4668,9 +4643,9 @@ TEST_F(DBCompactionTest, CompactRangeSkipFlushAfterDelay) {
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT("DBCompactionTest::CompactRangeSkipFlushAfterDelay:PreFlush");
|
||||
ASSERT_OK(Put(std::to_string(0), rnd.RandomString(1024)));
|
||||
ASSERT_OK(Put(ToString(0), rnd.RandomString(1024)));
|
||||
ASSERT_OK(dbfull()->Flush(flush_opts));
|
||||
ASSERT_OK(Put(std::to_string(0), rnd.RandomString(1024)));
|
||||
ASSERT_OK(Put(ToString(0), rnd.RandomString(1024)));
|
||||
TEST_SYNC_POINT("DBCompactionTest::CompactRangeSkipFlushAfterDelay:PostFlush");
|
||||
manual_compaction_thread.join();
|
||||
|
||||
@@ -4679,7 +4654,7 @@ TEST_F(DBCompactionTest, CompactRangeSkipFlushAfterDelay) {
|
||||
std::string num_keys_in_memtable;
|
||||
ASSERT_TRUE(db_->GetProperty(DB::Properties::kNumEntriesActiveMemTable,
|
||||
&num_keys_in_memtable));
|
||||
ASSERT_EQ(std::to_string(1), num_keys_in_memtable);
|
||||
ASSERT_EQ(ToString(1), num_keys_in_memtable);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
@@ -4828,7 +4803,7 @@ TEST_F(DBCompactionTest, SubcompactionEvent) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 10 + j;
|
||||
ASSERT_OK(Put(Key(key_id), "value" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -4838,7 +4813,7 @@ TEST_F(DBCompactionTest, SubcompactionEvent) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(Put(Key(key_id), "value" + std::to_string(key_id)));
|
||||
ASSERT_OK(Put(Key(key_id), "value" + ToString(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -5830,7 +5805,7 @@ TEST_P(DBCompactionTestWithBottommostParam, SequenceKeysManualCompaction) {
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
|
||||
ASSERT_EQ(std::to_string(kSstNum), FilesPerLevel(0));
|
||||
ASSERT_EQ(ToString(kSstNum), FilesPerLevel(0));
|
||||
|
||||
auto cro = CompactRangeOptions();
|
||||
cro.bottommost_level_compaction = bottommost_level_compaction_;
|
||||
@@ -5843,7 +5818,7 @@ TEST_P(DBCompactionTestWithBottommostParam, SequenceKeysManualCompaction) {
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
} else {
|
||||
// Just trivial move from level 0 -> 1
|
||||
ASSERT_EQ("0," + std::to_string(kSstNum), FilesPerLevel(0));
|
||||
ASSERT_EQ("0," + ToString(kSstNum), FilesPerLevel(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6387,74 +6362,6 @@ INSTANTIATE_TEST_CASE_P(DBCompactionTestBlobGC, DBCompactionTestBlobGC,
|
||||
::testing::Combine(::testing::Values(0.0, 0.5, 1.0),
|
||||
::testing::Bool()));
|
||||
|
||||
TEST_P(DBCompactionTestBlobGC, CompactionWithBlobGCOverrides) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.enable_blob_files = true;
|
||||
options.blob_file_size = 32; // one blob per file
|
||||
options.enable_blob_garbage_collection = true;
|
||||
options.blob_garbage_collection_age_cutoff = 0;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < 128; i += 2) {
|
||||
ASSERT_OK(Put("key" + std::to_string(i), "value" + std::to_string(i)));
|
||||
ASSERT_OK(
|
||||
Put("key" + std::to_string(i + 1), "value" + std::to_string(i + 1)));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
std::vector<uint64_t> original_blob_files = GetBlobFileNumbers();
|
||||
ASSERT_EQ(original_blob_files.size(), 128);
|
||||
|
||||
// Note: turning off enable_blob_files before the compaction results in
|
||||
// garbage collected values getting inlined.
|
||||
ASSERT_OK(db_->SetOptions({{"enable_blob_files", "false"}}));
|
||||
|
||||
CompactRangeOptions cro;
|
||||
cro.blob_garbage_collection_policy = BlobGarbageCollectionPolicy::kForce;
|
||||
cro.blob_garbage_collection_age_cutoff = blob_gc_age_cutoff_;
|
||||
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
|
||||
// Check that the GC stats are correct
|
||||
{
|
||||
VersionSet* const versions = dbfull()->GetVersionSet();
|
||||
assert(versions);
|
||||
assert(versions->GetColumnFamilySet());
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
const InternalStats* const internal_stats = cfd->internal_stats();
|
||||
assert(internal_stats);
|
||||
|
||||
const auto& compaction_stats = internal_stats->TEST_GetCompactionStats();
|
||||
ASSERT_GE(compaction_stats.size(), 2);
|
||||
|
||||
ASSERT_GE(compaction_stats[1].bytes_read_blob, 0);
|
||||
ASSERT_EQ(compaction_stats[1].bytes_written_blob, 0);
|
||||
}
|
||||
|
||||
const size_t cutoff_index = static_cast<size_t>(
|
||||
cro.blob_garbage_collection_age_cutoff * original_blob_files.size());
|
||||
const size_t expected_num_files = original_blob_files.size() - cutoff_index;
|
||||
|
||||
const std::vector<uint64_t> new_blob_files = GetBlobFileNumbers();
|
||||
|
||||
ASSERT_EQ(new_blob_files.size(), expected_num_files);
|
||||
|
||||
// Original blob files below the cutoff should be gone, original blob files
|
||||
// at or above the cutoff should be still there
|
||||
for (size_t i = cutoff_index; i < original_blob_files.size(); ++i) {
|
||||
ASSERT_EQ(new_blob_files[i - cutoff_index], original_blob_files[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 128; ++i) {
|
||||
ASSERT_EQ(Get("key" + std::to_string(i)), "value" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestBlobGC, CompactionWithBlobGC) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
@@ -7242,7 +7149,7 @@ TEST_F(DBCompactionTest, DisableManualCompactionThreadQueueFull) {
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ(std::to_string(kNumL0Files + (kNumL0Files / 2)), FilesPerLevel(0));
|
||||
ASSERT_EQ(ToString(kNumL0Files + (kNumL0Files / 2)), FilesPerLevel(0));
|
||||
|
||||
db_->DisableManualCompaction();
|
||||
|
||||
@@ -7299,7 +7206,7 @@ TEST_F(DBCompactionTest, DisableManualCompactionThreadQueueFullDBClose) {
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ(std::to_string(kNumL0Files + (kNumL0Files / 2)), FilesPerLevel(0));
|
||||
ASSERT_EQ(ToString(kNumL0Files + (kNumL0Files / 2)), FilesPerLevel(0));
|
||||
|
||||
db_->DisableManualCompaction();
|
||||
|
||||
@@ -7359,7 +7266,7 @@ TEST_F(DBCompactionTest, DBCloseWithManualCompaction) {
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ(std::to_string(kNumL0Files + (kNumL0Files / 2)), FilesPerLevel(0));
|
||||
ASSERT_EQ(ToString(kNumL0Files + (kNumL0Files / 2)), FilesPerLevel(0));
|
||||
|
||||
// Close DB with manual compaction and auto triggered compaction in the queue.
|
||||
auto s = db_->Close();
|
||||
|
||||
+2
-41
@@ -124,9 +124,6 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
}
|
||||
|
||||
Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
// Record tracked WALs as a (minimum) cross-check for directory scan
|
||||
std::vector<uint64_t> required_by_manifest;
|
||||
|
||||
// If caller disabled deletions, this function should return files that are
|
||||
// guaranteed not to be deleted until deletions are re-enabled. We need to
|
||||
// wait for pending purges to finish since WalManager doesn't know which
|
||||
@@ -140,13 +137,6 @@ Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
while (pending_purge_obsolete_files_ > 0 || bg_purge_scheduled_ > 0) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
|
||||
// Record tracked WALs as a (minimum) cross-check for directory scan
|
||||
const auto& manifest_wals = versions_->GetWalSet().GetWals();
|
||||
required_by_manifest.reserve(manifest_wals.size());
|
||||
for (const auto& wal : manifest_wals) {
|
||||
required_by_manifest.push_back(wal.first);
|
||||
}
|
||||
}
|
||||
|
||||
Status s = wal_manager_.GetSortedWalFiles(files);
|
||||
@@ -160,29 +150,6 @@ Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
assert(deletions_disabled.IsNotSupported());
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
// Verify includes those required by manifest (one sorted list is superset
|
||||
// of the other)
|
||||
auto required = required_by_manifest.begin();
|
||||
auto included = files.begin();
|
||||
|
||||
while (required != required_by_manifest.end()) {
|
||||
if (included == files.end() || *required < (*included)->LogNumber()) {
|
||||
// FAIL - did not find
|
||||
return Status::Corruption(
|
||||
"WAL file " + std::to_string(*required) +
|
||||
" required by manifest but not in directory list");
|
||||
}
|
||||
if (*required == (*included)->LogNumber()) {
|
||||
++required;
|
||||
++included;
|
||||
} else {
|
||||
assert(*required > (*included)->LogNumber());
|
||||
++included;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -210,7 +177,7 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
VectorLogPtr live_wal_files;
|
||||
bool flush_memtable = true;
|
||||
if (!immutable_db_options_.allow_2pc) {
|
||||
if (opts.wal_size_for_flush == std::numeric_limits<uint64_t>::max()) {
|
||||
if (opts.wal_size_for_flush == port::kMaxUint64) {
|
||||
flush_memtable = false;
|
||||
} else if (opts.wal_size_for_flush > 0) {
|
||||
// If the outstanding log files are small, we skip the flush.
|
||||
@@ -382,13 +349,7 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
TEST_SYNC_POINT("CheckpointImpl::CreateCheckpoint:SavedLiveFiles2");
|
||||
|
||||
if (s.ok()) {
|
||||
// To maximize the effectiveness of track_and_verify_wals_in_manifest,
|
||||
// sync WAL when it is enabled.
|
||||
s = FlushWAL(
|
||||
immutable_db_options_.track_and_verify_wals_in_manifest /* sync */);
|
||||
if (s.IsNotSupported()) { // read-only DB or similar
|
||||
s = Status::OK();
|
||||
}
|
||||
s = FlushWAL(false /* sync */);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("CheckpointImpl::CreateCustomCheckpoint:AfterGetLive1");
|
||||
|
||||
+1
-1
@@ -2356,7 +2356,7 @@ TEST_P(DBAtomicFlushTest, PrecomputeMinLogNumberToKeepNon2PC) {
|
||||
ASSERT_OK(Flush(cf_ids));
|
||||
uint64_t log_num_after_flush = dbfull()->TEST_GetCurrentLogNumber();
|
||||
|
||||
uint64_t min_log_number_to_keep = std::numeric_limits<uint64_t>::max();
|
||||
uint64_t min_log_number_to_keep = port::kMaxUint64;
|
||||
autovector<ColumnFamilyData*> flushed_cfds;
|
||||
autovector<autovector<VersionEdit*>> flush_edits;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
|
||||
@@ -40,52 +40,17 @@ size_t CompactedDBImpl::FindFile(const Slice& key) {
|
||||
|
||||
Status CompactedDBImpl::Get(const ReadOptions& options, ColumnFamilyHandle*,
|
||||
const Slice& key, PinnableSlice* value) {
|
||||
return Get(options, /*column_family*/ nullptr, key, value,
|
||||
/*timestamp*/ nullptr);
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Get(const ReadOptions& options, ColumnFamilyHandle*,
|
||||
const Slice& key, PinnableSlice* value,
|
||||
std::string* timestamp) {
|
||||
assert(user_comparator_);
|
||||
if (options.timestamp) {
|
||||
const Status s = FailIfTsMismatchCf(
|
||||
DefaultColumnFamily(), *(options.timestamp), /*ts_for_read=*/true);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
const Status s = FailIfCfHasTs(DefaultColumnFamily());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (options.timestamp || user_comparator_->timestamp_size()) {
|
||||
// TODO: support timestamp
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
// Clear the timestamps for returning results so that we can distinguish
|
||||
// between tombstone or key that has never been written
|
||||
if (timestamp) {
|
||||
timestamp->clear();
|
||||
}
|
||||
|
||||
GetWithTimestampReadCallback read_cb(kMaxSequenceNumber);
|
||||
std::string* ts =
|
||||
user_comparator_->timestamp_size() > 0 ? timestamp : nullptr;
|
||||
LookupKey lkey(key, kMaxSequenceNumber, options.timestamp);
|
||||
GetContext get_context(user_comparator_, nullptr, nullptr, nullptr,
|
||||
GetContext::kNotFound, lkey.user_key(), value, ts,
|
||||
nullptr, nullptr, true, nullptr, nullptr, nullptr,
|
||||
nullptr, &read_cb);
|
||||
|
||||
const FdWithKeyRange& f = files_.files[FindFile(lkey.user_key())];
|
||||
if (user_comparator_->CompareWithoutTimestamp(
|
||||
key, /*a_has_ts=*/false,
|
||||
ExtractUserKeyAndStripTimestamp(f.smallest_key,
|
||||
user_comparator_->timestamp_size()),
|
||||
/*b_has_ts=*/false) < 0) {
|
||||
return Status::NotFound();
|
||||
}
|
||||
Status s = f.fd.table_reader->Get(options, lkey.internal_key(), &get_context,
|
||||
nullptr);
|
||||
GetContext::kNotFound, key, value, nullptr, nullptr,
|
||||
nullptr, true, nullptr, nullptr);
|
||||
LookupKey lkey(key, kMaxSequenceNumber);
|
||||
Status s = files_.files[FindFile(key)].fd.table_reader->Get(
|
||||
options, lkey.internal_key(), &get_context, nullptr);
|
||||
if (!s.ok() && !s.IsNotFound()) {
|
||||
return s;
|
||||
}
|
||||
@@ -95,73 +60,37 @@ Status CompactedDBImpl::Get(const ReadOptions& options, ColumnFamilyHandle*,
|
||||
return Status::NotFound();
|
||||
}
|
||||
|
||||
std::vector<Status> CompactedDBImpl::MultiGet(
|
||||
const ReadOptions& options, const std::vector<ColumnFamilyHandle*>&,
|
||||
std::vector<Status> CompactedDBImpl::MultiGet(const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>&,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values) {
|
||||
return MultiGet(options, keys, values, /*timestamps*/ nullptr);
|
||||
}
|
||||
|
||||
std::vector<Status> CompactedDBImpl::MultiGet(
|
||||
const ReadOptions& options, const std::vector<ColumnFamilyHandle*>&,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values,
|
||||
std::vector<std::string>* timestamps) {
|
||||
assert(user_comparator_);
|
||||
size_t num_keys = keys.size();
|
||||
|
||||
if (options.timestamp) {
|
||||
Status s = FailIfTsMismatchCf(DefaultColumnFamily(), *(options.timestamp),
|
||||
/*ts_for_read=*/true);
|
||||
if (!s.ok()) {
|
||||
return std::vector<Status>(num_keys, s);
|
||||
}
|
||||
} else {
|
||||
Status s = FailIfCfHasTs(DefaultColumnFamily());
|
||||
if (!s.ok()) {
|
||||
return std::vector<Status>(num_keys, s);
|
||||
}
|
||||
if (user_comparator_->timestamp_size() || options.timestamp) {
|
||||
// TODO: support timestamp
|
||||
return std::vector<Status>(keys.size(), Status::NotSupported());
|
||||
}
|
||||
|
||||
// Clear the timestamps for returning results so that we can distinguish
|
||||
// between tombstone or key that has never been written
|
||||
if (timestamps) {
|
||||
for (auto& ts : *timestamps) {
|
||||
ts.clear();
|
||||
}
|
||||
}
|
||||
|
||||
GetWithTimestampReadCallback read_cb(kMaxSequenceNumber);
|
||||
autovector<TableReader*, 16> reader_list;
|
||||
for (const auto& key : keys) {
|
||||
LookupKey lkey(key, kMaxSequenceNumber, options.timestamp);
|
||||
const FdWithKeyRange& f = files_.files[FindFile(lkey.user_key())];
|
||||
if (user_comparator_->CompareWithoutTimestamp(
|
||||
key, /*a_has_ts=*/false,
|
||||
ExtractUserKeyAndStripTimestamp(f.smallest_key,
|
||||
user_comparator_->timestamp_size()),
|
||||
/*b_has_ts=*/false) < 0) {
|
||||
const FdWithKeyRange& f = files_.files[FindFile(key)];
|
||||
if (user_comparator_->Compare(key, ExtractUserKey(f.smallest_key)) < 0) {
|
||||
reader_list.push_back(nullptr);
|
||||
} else {
|
||||
LookupKey lkey(key, kMaxSequenceNumber);
|
||||
f.fd.table_reader->Prepare(lkey.internal_key());
|
||||
reader_list.push_back(f.fd.table_reader);
|
||||
}
|
||||
}
|
||||
std::vector<Status> statuses(num_keys, Status::NotFound());
|
||||
values->resize(num_keys);
|
||||
if (timestamps) {
|
||||
timestamps->resize(num_keys);
|
||||
}
|
||||
|
||||
std::vector<Status> statuses(keys.size(), Status::NotFound());
|
||||
values->resize(keys.size());
|
||||
int idx = 0;
|
||||
for (auto* r : reader_list) {
|
||||
if (r != nullptr) {
|
||||
PinnableSlice pinnable_val;
|
||||
std::string& value = (*values)[idx];
|
||||
LookupKey lkey(keys[idx], kMaxSequenceNumber, options.timestamp);
|
||||
std::string* timestamp = timestamps ? &(*timestamps)[idx] : nullptr;
|
||||
GetContext get_context(
|
||||
user_comparator_, nullptr, nullptr, nullptr, GetContext::kNotFound,
|
||||
lkey.user_key(), &pinnable_val,
|
||||
user_comparator_->timestamp_size() > 0 ? timestamp : nullptr, nullptr,
|
||||
nullptr, true, nullptr, nullptr, nullptr, nullptr, &read_cb);
|
||||
GetContext get_context(user_comparator_, nullptr, nullptr, nullptr,
|
||||
GetContext::kNotFound, keys[idx], &pinnable_val,
|
||||
nullptr, nullptr, nullptr, true, nullptr, nullptr);
|
||||
LookupKey lkey(keys[idx], kMaxSequenceNumber);
|
||||
Status s = r->Get(options, lkey.internal_key(), &get_context, nullptr);
|
||||
assert(static_cast<size_t>(idx) < statuses.size());
|
||||
if (!s.ok() && !s.IsNotFound()) {
|
||||
|
||||
@@ -28,25 +28,12 @@ class CompactedDBImpl : public DBImpl {
|
||||
virtual Status Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) override;
|
||||
|
||||
Status Get(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* value,
|
||||
std::string* timestamp) override;
|
||||
|
||||
using DB::MultiGet;
|
||||
// Note that CompactedDBImpl::MultiGet is not the optimized version of
|
||||
// MultiGet to use.
|
||||
// TODO: optimize CompactedDBImpl::MultiGet, see DBImpl::MultiGet for details.
|
||||
virtual std::vector<Status> MultiGet(
|
||||
const ReadOptions& options, const std::vector<ColumnFamilyHandle*>&,
|
||||
const std::vector<Slice>& keys,
|
||||
std::vector<std::string>* values) override;
|
||||
|
||||
std::vector<Status> MultiGet(const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>&,
|
||||
const std::vector<Slice>& keys,
|
||||
std::vector<std::string>* values,
|
||||
std::vector<std::string>* timestamps) override;
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>&,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values)
|
||||
override;
|
||||
|
||||
using DBImpl::Put;
|
||||
virtual Status Put(const WriteOptions& /*options*/,
|
||||
|
||||
+86
-293
@@ -101,7 +101,6 @@
|
||||
#include "util/compression.h"
|
||||
#include "util/crc32c.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/distributed_mutex.h"
|
||||
#include "util/hash_containers.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/stop_watch.h"
|
||||
@@ -146,8 +145,6 @@ void DumpSupportInfo(Logger* logger) {
|
||||
}
|
||||
ROCKS_LOG_HEADER(logger, "Fast CRC32 supported: %s",
|
||||
crc32c::IsFastCrc32Supported().c_str());
|
||||
|
||||
ROCKS_LOG_HEADER(logger, "DMutex implementation: %s", DMutex::kName());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -156,9 +153,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
bool read_only)
|
||||
: dbname_(dbname),
|
||||
own_info_log_(options.info_log == nullptr),
|
||||
init_logger_creation_s_(),
|
||||
initial_db_options_(SanitizeOptions(dbname, options, read_only,
|
||||
&init_logger_creation_s_)),
|
||||
initial_db_options_(SanitizeOptions(dbname, options, read_only)),
|
||||
env_(initial_db_options_.env),
|
||||
io_tracer_(std::make_shared<IOTracer>()),
|
||||
immutable_db_options_(initial_db_options_),
|
||||
@@ -516,19 +511,6 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
WaitForBackgroundWork();
|
||||
}
|
||||
|
||||
Status DBImpl::MaybeReleaseTimestampedSnapshotsAndCheck() {
|
||||
size_t num_snapshots = 0;
|
||||
ReleaseTimestampedSnapshotsOlderThan(std::numeric_limits<uint64_t>::max(),
|
||||
&num_snapshots);
|
||||
|
||||
// If there is unreleased snapshot, fail the close call
|
||||
if (num_snapshots > 0) {
|
||||
return Status::Aborted("Cannot close DB with unreleased snapshot.");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::CloseHelper() {
|
||||
// Guarantee that there is no background error recovery in progress before
|
||||
// continuing with the shutdown
|
||||
@@ -585,7 +567,7 @@ Status DBImpl::CloseHelper() {
|
||||
// flushing by first checking if there is a need for
|
||||
// flushing (but need to implement something
|
||||
// else than imm()->IsFlushPending() because the output
|
||||
// memtables added to imm() don't trigger flushes).
|
||||
// memtables added to imm() dont trigger flushes).
|
||||
if (immutable_db_options_.experimental_mempurge_threshold > 0.0) {
|
||||
Status flush_ret;
|
||||
mutex_.Unlock();
|
||||
@@ -732,40 +714,24 @@ Status DBImpl::CloseHelper() {
|
||||
write_buffer_manager_->RemoveDBFromQueue(wbm_stall_.get());
|
||||
}
|
||||
|
||||
IOStatus io_s = directories_.Close(IOOptions(), nullptr /* dbg */);
|
||||
if (!io_s.ok()) {
|
||||
ret = io_s;
|
||||
}
|
||||
if (ret.IsAborted()) {
|
||||
// Reserve IsAborted() error for those where users didn't release
|
||||
// certain resource and they can release them and come back and
|
||||
// retry. In this case, we wrap this exception to something else.
|
||||
return Status::Incomplete(ret.ToString());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Status DBImpl::CloseImpl() { return CloseHelper(); }
|
||||
|
||||
DBImpl::~DBImpl() {
|
||||
// TODO: remove this.
|
||||
init_logger_creation_s_.PermitUncheckedError();
|
||||
|
||||
InstrumentedMutexLock closing_lock_guard(&closing_mutex_);
|
||||
if (closed_) {
|
||||
return;
|
||||
if (!closed_) {
|
||||
closed_ = true;
|
||||
closing_status_ = CloseHelper();
|
||||
closing_status_.PermitUncheckedError();
|
||||
}
|
||||
|
||||
closed_ = true;
|
||||
|
||||
{
|
||||
const Status s = MaybeReleaseTimestampedSnapshotsAndCheck();
|
||||
s.PermitUncheckedError();
|
||||
}
|
||||
|
||||
closing_status_ = CloseImpl();
|
||||
closing_status_.PermitUncheckedError();
|
||||
}
|
||||
|
||||
void DBImpl::MaybeIgnoreError(Status* s) const {
|
||||
@@ -820,9 +786,8 @@ Status DBImpl::StartPeriodicWorkScheduler() {
|
||||
return periodic_work_scheduler_->Register(
|
||||
this, mutable_db_options_.stats_dump_period_sec,
|
||||
mutable_db_options_.stats_persist_period_sec);
|
||||
#else
|
||||
return Status::OK();
|
||||
#endif // !ROCKSDB_LITE
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// esitmate the total size of stats_history_
|
||||
@@ -884,8 +849,7 @@ void DBImpl::PersistStats() {
|
||||
if (stats_slice_.find(stat.first) != stats_slice_.end()) {
|
||||
uint64_t delta = stat.second - stats_slice_[stat.first];
|
||||
s = batch.Put(persist_stats_cf_handle_,
|
||||
Slice(key, std::min(100, length)),
|
||||
std::to_string(delta));
|
||||
Slice(key, std::min(100, length)), ToString(delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1372,7 +1336,6 @@ Status DBImpl::FlushWAL(bool sync) {
|
||||
}
|
||||
|
||||
Status DBImpl::SyncWAL() {
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:Begin");
|
||||
autovector<log::Writer*, 1> logs_to_sync;
|
||||
bool need_log_dir_sync;
|
||||
uint64_t current_log_number;
|
||||
@@ -1385,7 +1348,7 @@ Status DBImpl::SyncWAL() {
|
||||
current_log_number = logfile_number_;
|
||||
|
||||
while (logs_.front().number <= current_log_number &&
|
||||
logs_.front().IsSyncing()) {
|
||||
logs_.front().getting_synced) {
|
||||
log_sync_cv_.Wait();
|
||||
}
|
||||
// First check that logs are safe to sync in background.
|
||||
@@ -1402,7 +1365,8 @@ Status DBImpl::SyncWAL() {
|
||||
for (auto it = logs_.begin();
|
||||
it != logs_.end() && it->number <= current_log_number; ++it) {
|
||||
auto& log = *it;
|
||||
log.PrepareForSync();
|
||||
assert(!log.getting_synced);
|
||||
log.getting_synced = true;
|
||||
logs_to_sync.push_back(log.writer);
|
||||
}
|
||||
|
||||
@@ -1475,24 +1439,24 @@ Status DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir) {
|
||||
VersionEdit synced_wals;
|
||||
for (auto it = logs_.begin(); it != logs_.end() && it->number <= up_to;) {
|
||||
auto& wal = *it;
|
||||
assert(wal.IsSyncing());
|
||||
|
||||
assert(wal.getting_synced);
|
||||
if (logs_.size() > 1) {
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest &&
|
||||
wal.GetPreSyncSize() > 0) {
|
||||
synced_wals.AddWal(wal.number, WalMetadata(wal.GetPreSyncSize()));
|
||||
wal.writer->file()->GetFileSize() > 0) {
|
||||
synced_wals.AddWal(wal.number,
|
||||
WalMetadata(wal.writer->file()->GetFileSize()));
|
||||
}
|
||||
logs_to_free_.push_back(wal.ReleaseWriter());
|
||||
// To modify logs_ both mutex_ and log_write_mutex_ must be held
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
it = logs_.erase(it);
|
||||
} else {
|
||||
wal.FinishSync();
|
||||
wal.getting_synced = false;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
assert(logs_.empty() || logs_[0].number > up_to ||
|
||||
(logs_.size() == 1 && !logs_[0].IsSyncing()));
|
||||
(logs_.size() == 1 && !logs_[0].getting_synced));
|
||||
|
||||
Status s;
|
||||
if (synced_wals.IsWalAddition()) {
|
||||
@@ -1512,7 +1476,8 @@ void DBImpl::MarkLogsNotSynced(uint64_t up_to) {
|
||||
for (auto it = logs_.begin(); it != logs_.end() && it->number <= up_to;
|
||||
++it) {
|
||||
auto& wal = *it;
|
||||
wal.FinishSync();
|
||||
assert(wal.getting_synced);
|
||||
wal.getting_synced = false;
|
||||
}
|
||||
log_sync_cv_.SignalAll();
|
||||
}
|
||||
@@ -1749,8 +1714,6 @@ Status DBImpl::Get(const ReadOptions& read_options,
|
||||
Status DBImpl::Get(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value, std::string* timestamp) {
|
||||
assert(value != nullptr);
|
||||
value->Reset();
|
||||
GetImplOptions get_impl_options;
|
||||
get_impl_options.column_family = column_family;
|
||||
get_impl_options.value = value;
|
||||
@@ -1759,6 +1722,17 @@ Status DBImpl::Get(const ReadOptions& read_options,
|
||||
return s;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class GetWithTimestampReadCallback : public ReadCallback {
|
||||
public:
|
||||
explicit GetWithTimestampReadCallback(SequenceNumber seq)
|
||||
: ReadCallback(seq) {}
|
||||
bool IsVisibleFullCheck(SequenceNumber seq) override {
|
||||
return seq <= max_visible_seq_;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
GetImplOptions& get_impl_options) {
|
||||
assert(get_impl_options.value != nullptr ||
|
||||
@@ -1767,9 +1741,8 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
assert(get_impl_options.column_family);
|
||||
|
||||
if (read_options.timestamp) {
|
||||
const Status s = FailIfTsMismatchCf(get_impl_options.column_family,
|
||||
*(read_options.timestamp),
|
||||
/*ts_for_read=*/true);
|
||||
const Status s = FailIfTsSizesMismatch(get_impl_options.column_family,
|
||||
*(read_options.timestamp));
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -1780,12 +1753,6 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the timestamps for returning results so that we can distinguish
|
||||
// between tombstone or key that has never been written
|
||||
if (get_impl_options.timestamp) {
|
||||
get_impl_options.timestamp->clear();
|
||||
}
|
||||
|
||||
GetWithTimestampReadCallback read_cb(0); // Will call Refresh
|
||||
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, immutable_db_options_.clock);
|
||||
@@ -1827,7 +1794,11 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
// data for the snapshot, so the reader would see neither data that was be
|
||||
// visible to the snapshot before compaction nor the newer data inserted
|
||||
// afterwards.
|
||||
snapshot = GetLastPublishedSequence();
|
||||
if (last_seq_same_as_publish_seq_) {
|
||||
snapshot = versions_->LastSequence();
|
||||
} else {
|
||||
snapshot = versions_->LastPublishedSequence();
|
||||
}
|
||||
if (get_impl_options.callback) {
|
||||
// The unprep_seqs are not published for write unprepared, so it could be
|
||||
// that max_visible_seq is larger. Seek to the std::max of the two.
|
||||
@@ -1995,8 +1966,8 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
assert(column_family[i]);
|
||||
if (read_options.timestamp) {
|
||||
stat_list[i] = FailIfTsMismatchCf(
|
||||
column_family[i], *(read_options.timestamp), /*ts_for_read=*/true);
|
||||
stat_list[i] =
|
||||
FailIfTsSizesMismatch(column_family[i], *(read_options.timestamp));
|
||||
if (!stat_list[i].ok()) {
|
||||
should_fail = true;
|
||||
}
|
||||
@@ -2220,7 +2191,11 @@ bool DBImpl::MultiCFSnapshot(
|
||||
// version because a flush happening in between may compact away data for
|
||||
// the snapshot, but the snapshot is earlier than the data overwriting it,
|
||||
// so users may see wrong results.
|
||||
*snapshot = GetLastPublishedSequence();
|
||||
if (last_seq_same_as_publish_seq_) {
|
||||
*snapshot = versions_->LastSequence();
|
||||
} else {
|
||||
*snapshot = versions_->LastPublishedSequence();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If we end up with the same issue of memtable geting sealed during 2
|
||||
@@ -2251,7 +2226,11 @@ bool DBImpl::MultiCFSnapshot(
|
||||
// acquire the lock so we're sure to succeed
|
||||
mutex_.Lock();
|
||||
}
|
||||
*snapshot = GetLastPublishedSequence();
|
||||
if (last_seq_same_as_publish_seq_) {
|
||||
*snapshot = versions_->LastSequence();
|
||||
} else {
|
||||
*snapshot = versions_->LastPublishedSequence();
|
||||
}
|
||||
} else {
|
||||
*snapshot =
|
||||
static_cast_with_check<const SnapshotImpl>(read_options.snapshot)
|
||||
@@ -2322,8 +2301,7 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
ColumnFamilyHandle* cfh = column_families[i];
|
||||
assert(cfh);
|
||||
if (read_options.timestamp) {
|
||||
statuses[i] = FailIfTsMismatchCf(cfh, *(read_options.timestamp),
|
||||
/*ts_for_read=*/true);
|
||||
statuses[i] = FailIfTsSizesMismatch(cfh, *(read_options.timestamp));
|
||||
if (!statuses[i].ok()) {
|
||||
should_fail = true;
|
||||
}
|
||||
@@ -2358,7 +2336,6 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE> sorted_keys;
|
||||
sorted_keys.resize(num_keys);
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
values[i].Reset();
|
||||
key_context.emplace_back(column_families[i], keys[i], &values[i],
|
||||
timestamps ? ×tamps[i] : nullptr,
|
||||
&statuses[i]);
|
||||
@@ -2505,7 +2482,6 @@ void DBImpl::MultiGet(const ReadOptions& read_options,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE> sorted_keys;
|
||||
sorted_keys.resize(num_keys);
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
values[i].Reset();
|
||||
key_context.emplace_back(column_family, keys[i], &values[i],
|
||||
timestamps ? ×tamps[i] : nullptr,
|
||||
&statuses[i]);
|
||||
@@ -2594,16 +2570,6 @@ Status DBImpl::MultiGetImpl(
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, immutable_db_options_.clock);
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, DB_MULTIGET);
|
||||
|
||||
assert(sorted_keys);
|
||||
// Clear the timestamps for returning results so that we can distinguish
|
||||
// between tombstone or key that has never been written
|
||||
for (auto* kctx : *sorted_keys) {
|
||||
assert(kctx);
|
||||
if (kctx->timestamp) {
|
||||
kctx->timestamp->clear();
|
||||
}
|
||||
}
|
||||
|
||||
// For each of the given keys, apply the entire "get" process as follows:
|
||||
// First look in the memtable, then in the immutable memtable (if any).
|
||||
// s is both in/out. When in, s could either be OK or MergeInProgress.
|
||||
@@ -2623,8 +2589,7 @@ Status DBImpl::MultiGetImpl(
|
||||
? MultiGetContext::MAX_BATCH_SIZE
|
||||
: keys_left;
|
||||
MultiGetContext ctx(sorted_keys, start_key + num_keys - keys_left,
|
||||
batch_size, snapshot, read_options, GetFileSystem(),
|
||||
stats_);
|
||||
batch_size, snapshot, read_options);
|
||||
MultiGetRange range = ctx.GetMultiGetRange();
|
||||
range.AddValueSize(curr_value_size);
|
||||
bool lookup_current = false;
|
||||
@@ -2816,6 +2781,7 @@ Status DBImpl::CreateColumnFamilyImpl(const ColumnFamilyOptions& cf_options,
|
||||
s = cfd->AddDirectories(&dummy_created_dirs);
|
||||
}
|
||||
if (s.ok()) {
|
||||
single_column_family_mode_ = false;
|
||||
auto* cfd =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(column_family_name);
|
||||
assert(cfd != nullptr);
|
||||
@@ -2985,8 +2951,8 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
assert(column_family);
|
||||
|
||||
if (read_options.timestamp) {
|
||||
const Status s = FailIfTsMismatchCf(
|
||||
column_family, *(read_options.timestamp), /*ts_for_read=*/true);
|
||||
const Status s =
|
||||
FailIfTsSizesMismatch(column_family, *(read_options.timestamp));
|
||||
if (!s.ok()) {
|
||||
return NewErrorIterator(s);
|
||||
}
|
||||
@@ -3127,8 +3093,7 @@ Status DBImpl::NewIterators(
|
||||
if (read_options.timestamp) {
|
||||
for (auto* cf : column_families) {
|
||||
assert(cf);
|
||||
const Status s = FailIfTsMismatchCf(cf, *(read_options.timestamp),
|
||||
/*ts_for_read=*/true);
|
||||
const Status s = FailIfTsSizesMismatch(cf, *(read_options.timestamp));
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -3190,48 +3155,6 @@ const Snapshot* DBImpl::GetSnapshotForWriteConflictBoundary() {
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
std::pair<Status, std::shared_ptr<const Snapshot>>
|
||||
DBImpl::CreateTimestampedSnapshot(SequenceNumber snapshot_seq, uint64_t ts) {
|
||||
assert(ts != std::numeric_limits<uint64_t>::max());
|
||||
|
||||
auto ret = CreateTimestampedSnapshotImpl(snapshot_seq, ts, /*lock=*/true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<const SnapshotImpl> DBImpl::GetTimestampedSnapshot(
|
||||
uint64_t ts) const {
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
return timestamped_snapshots_.GetSnapshot(ts);
|
||||
}
|
||||
|
||||
void DBImpl::ReleaseTimestampedSnapshotsOlderThan(uint64_t ts,
|
||||
size_t* remaining_total_ss) {
|
||||
autovector<std::shared_ptr<const SnapshotImpl>> snapshots_to_release;
|
||||
{
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
timestamped_snapshots_.ReleaseSnapshotsOlderThan(ts, snapshots_to_release);
|
||||
}
|
||||
snapshots_to_release.clear();
|
||||
|
||||
if (remaining_total_ss) {
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
*remaining_total_ss = static_cast<size_t>(snapshots_.count());
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::GetTimestampedSnapshots(
|
||||
uint64_t ts_lb, uint64_t ts_ub,
|
||||
std::vector<std::shared_ptr<const Snapshot>>& timestamped_snapshots) const {
|
||||
if (ts_lb >= ts_ub) {
|
||||
return Status::InvalidArgument(
|
||||
"timestamp lower bound must be smaller than upper bound");
|
||||
}
|
||||
timestamped_snapshots.clear();
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
timestamped_snapshots_.GetSnapshots(ts_lb, ts_ub, timestamped_snapshots);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary,
|
||||
bool lock) {
|
||||
int64_t unix_time = 0;
|
||||
@@ -3241,8 +3164,6 @@ SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary,
|
||||
|
||||
if (lock) {
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
mutex_.AssertHeld();
|
||||
}
|
||||
// returns null if the underlying memtable does not support snapshot.
|
||||
if (!is_snapshot_supported_) {
|
||||
@@ -3252,7 +3173,9 @@ SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary,
|
||||
delete s;
|
||||
return nullptr;
|
||||
}
|
||||
auto snapshot_seq = GetLastPublishedSequence();
|
||||
auto snapshot_seq = last_seq_same_as_publish_seq_
|
||||
? versions_->LastSequence()
|
||||
: versions_->LastPublishedSequence();
|
||||
SnapshotImpl* snapshot =
|
||||
snapshots_.New(s, snapshot_seq, unix_time, is_write_conflict_boundary);
|
||||
if (lock) {
|
||||
@@ -3261,115 +3184,6 @@ SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary,
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
std::pair<Status, std::shared_ptr<const SnapshotImpl>>
|
||||
DBImpl::CreateTimestampedSnapshotImpl(SequenceNumber snapshot_seq, uint64_t ts,
|
||||
bool lock) {
|
||||
int64_t unix_time = 0;
|
||||
immutable_db_options_.clock->GetCurrentTime(&unix_time)
|
||||
.PermitUncheckedError(); // Ignore error
|
||||
SnapshotImpl* s = new SnapshotImpl;
|
||||
|
||||
const bool need_update_seq = (snapshot_seq != kMaxSequenceNumber);
|
||||
|
||||
if (lock) {
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
mutex_.AssertHeld();
|
||||
}
|
||||
// returns null if the underlying memtable does not support snapshot.
|
||||
if (!is_snapshot_supported_) {
|
||||
if (lock) {
|
||||
mutex_.Unlock();
|
||||
}
|
||||
delete s;
|
||||
return std::make_pair(
|
||||
Status::NotSupported("Memtable does not support snapshot"), nullptr);
|
||||
}
|
||||
|
||||
// Caller is not write thread, thus didn't provide a valid snapshot_seq.
|
||||
// Obtain seq from db.
|
||||
if (!need_update_seq) {
|
||||
snapshot_seq = GetLastPublishedSequence();
|
||||
}
|
||||
|
||||
std::shared_ptr<const SnapshotImpl> latest =
|
||||
timestamped_snapshots_.GetSnapshot(std::numeric_limits<uint64_t>::max());
|
||||
|
||||
// If there is already a latest timestamped snapshot, then we need to do some
|
||||
// checks.
|
||||
if (latest) {
|
||||
uint64_t latest_snap_ts = latest->GetTimestamp();
|
||||
SequenceNumber latest_snap_seq = latest->GetSequenceNumber();
|
||||
assert(latest_snap_seq <= snapshot_seq);
|
||||
bool needs_create_snap = true;
|
||||
Status status;
|
||||
std::shared_ptr<const SnapshotImpl> ret;
|
||||
if (latest_snap_ts > ts) {
|
||||
// A snapshot created later cannot have smaller timestamp than a previous
|
||||
// timestamped snapshot.
|
||||
needs_create_snap = false;
|
||||
std::ostringstream oss;
|
||||
oss << "snapshot exists with larger timestamp " << latest_snap_ts << " > "
|
||||
<< ts;
|
||||
status = Status::InvalidArgument(oss.str());
|
||||
} else if (latest_snap_ts == ts) {
|
||||
if (latest_snap_seq == snapshot_seq) {
|
||||
// We are requesting the same sequence number and timestamp, thus can
|
||||
// safely reuse (share) the current latest timestamped snapshot.
|
||||
needs_create_snap = false;
|
||||
ret = latest;
|
||||
} else if (latest_snap_seq < snapshot_seq) {
|
||||
// There may have been writes to the database since the latest
|
||||
// timestamped snapshot, yet we are still requesting the same
|
||||
// timestamp. In this case, we cannot create the new timestamped
|
||||
// snapshot.
|
||||
needs_create_snap = false;
|
||||
std::ostringstream oss;
|
||||
oss << "Allocated seq is " << snapshot_seq
|
||||
<< ", while snapshot exists with smaller seq " << latest_snap_seq
|
||||
<< " but same timestamp " << ts;
|
||||
status = Status::InvalidArgument(oss.str());
|
||||
}
|
||||
}
|
||||
if (!needs_create_snap) {
|
||||
if (lock) {
|
||||
mutex_.Unlock();
|
||||
}
|
||||
delete s;
|
||||
return std::make_pair(status, ret);
|
||||
} else {
|
||||
status.PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
|
||||
SnapshotImpl* snapshot =
|
||||
snapshots_.New(s, snapshot_seq, unix_time,
|
||||
/*is_write_conflict_boundary=*/true, ts);
|
||||
|
||||
std::shared_ptr<const SnapshotImpl> ret(
|
||||
snapshot,
|
||||
std::bind(&DBImpl::ReleaseSnapshot, this, std::placeholders::_1));
|
||||
timestamped_snapshots_.AddSnapshot(ret);
|
||||
|
||||
// Caller is from write thread, and we need to update database's sequence
|
||||
// number.
|
||||
if (need_update_seq) {
|
||||
assert(versions_);
|
||||
if (last_seq_same_as_publish_seq_) {
|
||||
versions_->SetLastSequence(snapshot_seq);
|
||||
} else {
|
||||
// TODO: support write-prepared/write-unprepared transactions with two
|
||||
// write queues.
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (lock) {
|
||||
mutex_.Unlock();
|
||||
}
|
||||
return std::make_pair(Status::OK(), ret);
|
||||
}
|
||||
|
||||
namespace {
|
||||
using CfdList = autovector<ColumnFamilyData*, 2>;
|
||||
bool CfdListContains(const CfdList& list, ColumnFamilyData* cfd) {
|
||||
@@ -3395,7 +3209,11 @@ void DBImpl::ReleaseSnapshot(const Snapshot* s) {
|
||||
snapshots_.Delete(casted_s);
|
||||
uint64_t oldest_snapshot;
|
||||
if (snapshots_.empty()) {
|
||||
oldest_snapshot = GetLastPublishedSequence();
|
||||
if (last_seq_same_as_publish_seq_) {
|
||||
oldest_snapshot = versions_->LastSequence();
|
||||
} else {
|
||||
oldest_snapshot = versions_->LastPublishedSequence();
|
||||
}
|
||||
} else {
|
||||
oldest_snapshot = snapshots_.oldest()->number_;
|
||||
}
|
||||
@@ -3537,7 +3355,7 @@ bool DBImpl::GetProperty(ColumnFamilyHandle* column_family,
|
||||
bool ret_value =
|
||||
GetIntPropertyInternal(cfd, *property_info, false, &int_value);
|
||||
if (ret_value) {
|
||||
*value = std::to_string(int_value);
|
||||
*value = ToString(int_value);
|
||||
}
|
||||
return ret_value;
|
||||
} else if (property_info->handle_string) {
|
||||
@@ -4172,8 +3990,8 @@ Status DBImpl::CheckConsistency() {
|
||||
} else if (fsize != md.size) {
|
||||
corruption_messages += "Sst file size mismatch: " + file_path +
|
||||
". Size recorded in manifest " +
|
||||
std::to_string(md.size) + ", actual size " +
|
||||
std::to_string(fsize) + "\n";
|
||||
ToString(md.size) + ", actual size " +
|
||||
ToString(fsize) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4286,14 +4104,13 @@ Status DBImpl::Close() {
|
||||
if (closed_) {
|
||||
return closing_status_;
|
||||
}
|
||||
|
||||
{
|
||||
const Status s = MaybeReleaseTimestampedSnapshotsAndCheck();
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// If there is unreleased snapshot, fail the close call
|
||||
if (!snapshots_.empty()) {
|
||||
return Status::Aborted("Cannot close DB with unreleased snapshot.");
|
||||
}
|
||||
}
|
||||
|
||||
closing_status_ = CloseImpl();
|
||||
closed_ = true;
|
||||
return closing_status_;
|
||||
@@ -4575,19 +4392,6 @@ Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name) {
|
||||
s = dir_obj->FsyncWithDirOptions(IOOptions(), nullptr,
|
||||
DirFsyncOptions(options_file_name));
|
||||
}
|
||||
if (s.ok()) {
|
||||
Status temp_s = dir_obj->Close(IOOptions(), nullptr);
|
||||
// The default Close() could return "NotSupproted" and we bypass it
|
||||
// if it is not impelmented. Detailed explanations can be found in
|
||||
// db/db_impl/db_impl.h
|
||||
if (!temp_s.ok()) {
|
||||
if (temp_s.IsNotSupported()) {
|
||||
temp_s.PermitUncheckedError();
|
||||
} else {
|
||||
s = temp_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -4714,8 +4518,6 @@ Status DBImpl::GetLatestSequenceForKey(
|
||||
*timestamp != std::string(ts_sz, '\xff')) ||
|
||||
(*seq == kMaxSequenceNumber && timestamp->empty()));
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::GetLatestSequenceForKey:mem", timestamp);
|
||||
|
||||
if (*seq != kMaxSequenceNumber) {
|
||||
// Found a sequence number, no need to check immutable memtables
|
||||
*found_record_for_key = true;
|
||||
@@ -4780,7 +4582,6 @@ Status DBImpl::GetLatestSequenceForKey(
|
||||
(*seq != kMaxSequenceNumber &&
|
||||
*timestamp != std::string(ts_sz, '\xff')) ||
|
||||
(*seq == kMaxSequenceNumber && timestamp->empty()));
|
||||
|
||||
if (*seq != kMaxSequenceNumber) {
|
||||
// Found a sequence number, no need to check SST files
|
||||
assert(0 == ts_sz || *timestamp != std::string(ts_sz, '\xff'));
|
||||
@@ -5017,6 +4818,19 @@ Status DBImpl::IngestExternalFiles(
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
int consumed_seqno_count =
|
||||
ingestion_jobs[0].ConsumedSequenceNumbersCount();
|
||||
for (size_t i = 1; i != num_cfs; ++i) {
|
||||
consumed_seqno_count =
|
||||
std::max(consumed_seqno_count,
|
||||
ingestion_jobs[i].ConsumedSequenceNumbersCount());
|
||||
}
|
||||
if (consumed_seqno_count > 0) {
|
||||
const SequenceNumber last_seqno = versions_->LastSequence();
|
||||
versions_->SetLastAllocatedSequence(last_seqno + consumed_seqno_count);
|
||||
versions_->SetLastPublishedSequence(last_seqno + consumed_seqno_count);
|
||||
versions_->SetLastSequence(last_seqno + consumed_seqno_count);
|
||||
}
|
||||
autovector<ColumnFamilyData*> cfds_to_commit;
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
autovector<autovector<VersionEdit*>> edit_lists;
|
||||
@@ -5046,27 +4860,6 @@ Status DBImpl::IngestExternalFiles(
|
||||
status =
|
||||
versions_->LogAndApply(cfds_to_commit, mutable_cf_options_list,
|
||||
edit_lists, &mutex_, directories_.GetDbDir());
|
||||
// It is safe to update VersionSet last seqno here after LogAndApply since
|
||||
// LogAndApply persists last sequence number from VersionEdits,
|
||||
// which are from file's largest seqno and not from VersionSet.
|
||||
//
|
||||
// It is necessary to update last seqno here since LogAndApply releases
|
||||
// mutex when persisting MANIFEST file, and the snapshots taken during
|
||||
// that period will not be stable if VersionSet last seqno is updated
|
||||
// before LogAndApply.
|
||||
int consumed_seqno_count =
|
||||
ingestion_jobs[0].ConsumedSequenceNumbersCount();
|
||||
for (size_t i = 1; i != num_cfs; ++i) {
|
||||
consumed_seqno_count =
|
||||
std::max(consumed_seqno_count,
|
||||
ingestion_jobs[i].ConsumedSequenceNumbersCount());
|
||||
}
|
||||
if (consumed_seqno_count > 0) {
|
||||
const SequenceNumber last_seqno = versions_->LastSequence();
|
||||
versions_->SetLastAllocatedSequence(last_seqno + consumed_seqno_count);
|
||||
versions_->SetLastPublishedSequence(last_seqno + consumed_seqno_count);
|
||||
versions_->SetLastSequence(last_seqno + consumed_seqno_count);
|
||||
}
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
@@ -5330,8 +5123,8 @@ Status DBImpl::VerifyChecksumInternal(const ReadOptions& read_options,
|
||||
fmeta->file_checksum_func_name, fname,
|
||||
read_options);
|
||||
} else {
|
||||
s = ROCKSDB_NAMESPACE::VerifySstFileChecksum(
|
||||
opts, file_options_, read_options, fname, fd.largest_seqno);
|
||||
s = ROCKSDB_NAMESPACE::VerifySstFileChecksum(opts, file_options_,
|
||||
read_options, fname);
|
||||
}
|
||||
RecordTick(stats_, VERIFY_CHECKSUM_READ_BYTES,
|
||||
IOSTATS(bytes_read) - prev_bytes_read);
|
||||
@@ -5545,7 +5338,7 @@ Status DBImpl::ReserveFileNumbersBeforeIngestion(
|
||||
|
||||
Status DBImpl::GetCreationTimeOfOldestFile(uint64_t* creation_time) {
|
||||
if (mutable_db_options_.max_open_files == -1) {
|
||||
uint64_t oldest_time = std::numeric_limits<uint64_t>::max();
|
||||
uint64_t oldest_time = port::kMaxUint64;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (!cfd->IsDropped()) {
|
||||
uint64_t ctime;
|
||||
|
||||
+30
-222
@@ -32,7 +32,6 @@
|
||||
#include "db/log_writer.h"
|
||||
#include "db/logs_with_prep_tracker.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/post_memtable_callback.h"
|
||||
#include "db/pre_release_callback.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/read_callback.h"
|
||||
@@ -114,49 +113,6 @@ class Directories {
|
||||
|
||||
FSDirectory* GetDbDir() { return db_dir_.get(); }
|
||||
|
||||
IOStatus Close(const IOOptions& options, IODebugContext* dbg) {
|
||||
// close all directories for all database paths
|
||||
IOStatus s = IOStatus::OK();
|
||||
|
||||
// The default implementation for Close() in Directory/FSDirectory class
|
||||
// "NotSupported" status, the upper level interface should be able to
|
||||
// handle this error so that Close() does not fail after upgrading when
|
||||
// run on FileSystems that have not implemented `Directory::Close()` or
|
||||
// `FSDirectory::Close()` yet
|
||||
|
||||
if (db_dir_) {
|
||||
IOStatus temp_s = db_dir_->Close(options, dbg);
|
||||
if (!temp_s.ok() && !temp_s.IsNotSupported() && s.ok()) {
|
||||
s = std::move(temp_s);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to close everything even if one fails
|
||||
s.PermitUncheckedError();
|
||||
|
||||
if (wal_dir_) {
|
||||
IOStatus temp_s = wal_dir_->Close(options, dbg);
|
||||
if (!temp_s.ok() && !temp_s.IsNotSupported() && s.ok()) {
|
||||
s = std::move(temp_s);
|
||||
}
|
||||
}
|
||||
|
||||
s.PermitUncheckedError();
|
||||
|
||||
for (auto& data_dir_ptr : data_dirs_) {
|
||||
if (data_dir_ptr) {
|
||||
IOStatus temp_s = data_dir_ptr->Close(options, dbg);
|
||||
if (!temp_s.ok() && !temp_s.IsNotSupported() && s.ok()) {
|
||||
s = std::move(temp_s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ready for caller
|
||||
s.MustCheck();
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<FSDirectory> db_dir_;
|
||||
std::vector<std::unique_ptr<FSDirectory>> data_dirs_;
|
||||
@@ -327,19 +283,6 @@ class DBImpl : public DB {
|
||||
|
||||
virtual const Snapshot* GetSnapshot() override;
|
||||
virtual void ReleaseSnapshot(const Snapshot* snapshot) override;
|
||||
// Create a timestamped snapshot. This snapshot can be shared by multiple
|
||||
// readers. If any of them uses it for write conflict checking, then
|
||||
// is_write_conflict_boundary is true. For simplicity, set it to true by
|
||||
// default.
|
||||
std::pair<Status, std::shared_ptr<const Snapshot>> CreateTimestampedSnapshot(
|
||||
SequenceNumber snapshot_seq, uint64_t ts);
|
||||
std::shared_ptr<const SnapshotImpl> GetTimestampedSnapshot(uint64_t ts) const;
|
||||
void ReleaseTimestampedSnapshotsOlderThan(
|
||||
uint64_t ts, size_t* remaining_total_ss = nullptr);
|
||||
Status GetTimestampedSnapshots(uint64_t ts_lb, uint64_t ts_ub,
|
||||
std::vector<std::shared_ptr<const Snapshot>>&
|
||||
timestamped_snapshots) const;
|
||||
|
||||
using DB::GetProperty;
|
||||
virtual bool GetProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, std::string* value) override;
|
||||
@@ -1217,8 +1160,6 @@ class DBImpl : public DB {
|
||||
static void TEST_ResetDbSessionIdGen();
|
||||
static std::string GenerateDbSessionId(Env* env);
|
||||
|
||||
bool seq_per_batch() const { return seq_per_batch_; }
|
||||
|
||||
protected:
|
||||
const std::string dbname_;
|
||||
// TODO(peterd): unify with VersionSet::db_id_
|
||||
@@ -1229,7 +1170,6 @@ class DBImpl : public DB {
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
// Flag to check whether we allocated and own the info log file
|
||||
bool own_info_log_;
|
||||
Status init_logger_creation_s_;
|
||||
const DBOptions initial_db_options_;
|
||||
Env* const env_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
@@ -1243,9 +1183,6 @@ class DBImpl : public DB {
|
||||
InstrumentedMutex trace_mutex_;
|
||||
BlockCacheTracer block_cache_tracer_;
|
||||
|
||||
// constant false canceled flag, used when the compaction is not manual
|
||||
const std::atomic<bool> kManualCompactionCanceledFalse_{false};
|
||||
|
||||
// State below is protected by mutex_
|
||||
// With two_write_queues enabled, some of the variables that accessed during
|
||||
// WriteToWAL need different synchronization: log_empty_, alive_log_files_,
|
||||
@@ -1270,6 +1207,9 @@ class DBImpl : public DB {
|
||||
// only used for dynamically adjusting max_total_wal_size. it is a sum of
|
||||
// [write_buffer_size * max_write_buffer_number] over all column families
|
||||
uint64_t max_total_in_memory_state_;
|
||||
// If true, we have only one (default) column family. We use this to optimize
|
||||
// some code-paths
|
||||
bool single_column_family_mode_;
|
||||
|
||||
// The options to access storage files
|
||||
const FileOptions file_options_;
|
||||
@@ -1300,39 +1240,6 @@ class DBImpl : public DB {
|
||||
|
||||
std::atomic<bool> shutting_down_;
|
||||
|
||||
// RecoveryContext struct stores the context about version edits along
|
||||
// with corresponding column_family_data and column_family_options.
|
||||
class RecoveryContext {
|
||||
public:
|
||||
~RecoveryContext() {
|
||||
for (auto& edit_list : edit_lists_) {
|
||||
for (auto* edit : edit_list) {
|
||||
delete edit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateVersionEdits(ColumnFamilyData* cfd, const VersionEdit& edit) {
|
||||
assert(cfd != nullptr);
|
||||
if (map_.find(cfd->GetID()) == map_.end()) {
|
||||
uint32_t size = static_cast<uint32_t>(map_.size());
|
||||
map_.emplace(cfd->GetID(), size);
|
||||
cfds_.emplace_back(cfd);
|
||||
mutable_cf_opts_.emplace_back(cfd->GetLatestMutableCFOptions());
|
||||
edit_lists_.emplace_back(autovector<VersionEdit*>());
|
||||
}
|
||||
uint32_t i = map_[cfd->GetID()];
|
||||
edit_lists_[i].emplace_back(new VersionEdit(edit));
|
||||
}
|
||||
|
||||
std::unordered_map<uint32_t, uint32_t> map_; // cf_id to index;
|
||||
autovector<ColumnFamilyData*> cfds_;
|
||||
autovector<const MutableCFOptions*> mutable_cf_opts_;
|
||||
autovector<autovector<VersionEdit*>> edit_lists_;
|
||||
// files_to_delete_ contains sst files
|
||||
std::unordered_set<std::string> files_to_delete_;
|
||||
};
|
||||
|
||||
// Except in DB::Open(), WriteOptionsFile can only be called when:
|
||||
// Persist options to options file.
|
||||
// If need_mutex_lock = false, the method will lock DB mutex.
|
||||
@@ -1402,8 +1309,7 @@ class DBImpl : public DB {
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
|
||||
bool disable_memtable = false, uint64_t* seq_used = nullptr,
|
||||
size_t batch_cnt = 0,
|
||||
PreReleaseCallback* pre_release_callback = nullptr,
|
||||
PostMemTableCallback* post_memtable_callback = nullptr);
|
||||
PreReleaseCallback* pre_release_callback = nullptr);
|
||||
|
||||
Status PipelinedWriteImpl(const WriteOptions& options, WriteBatch* updates,
|
||||
WriteCallback* callback = nullptr,
|
||||
@@ -1450,21 +1356,16 @@ class DBImpl : public DB {
|
||||
// be made to the descriptor are added to *edit.
|
||||
// recovered_seq is set to less than kMaxSequenceNumber if the log's tail is
|
||||
// skipped.
|
||||
// recovery_ctx stores the context about version edits and all those
|
||||
// edits are persisted to new Manifest after successfully syncing the new WAL.
|
||||
virtual Status Recover(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool read_only = false, bool error_if_wal_file_exists = false,
|
||||
bool error_if_data_exists_in_wals = false,
|
||||
uint64_t* recovered_seq = nullptr,
|
||||
RecoveryContext* recovery_ctx = nullptr);
|
||||
uint64_t* recovered_seq = nullptr);
|
||||
|
||||
virtual bool OwnTablesAndLogs() const { return true; }
|
||||
|
||||
// Setup DB identity file, and write DB ID to manifest if necessary.
|
||||
Status SetupDBId(bool read_only, RecoveryContext* recovery_ctx);
|
||||
// Assign db_id_ and write DB ID to manifest if necessary.
|
||||
void SetDBId(std::string&& id, bool read_only, RecoveryContext* recovery_ctx);
|
||||
// Set DB identity file, and write DB ID to manifest if necessary.
|
||||
Status SetDBId(bool read_only);
|
||||
|
||||
// REQUIRES: db mutex held when calling this function, but the db mutex can
|
||||
// be released and re-acquired. Db mutex will be held when the function
|
||||
@@ -1473,31 +1374,20 @@ class DBImpl : public DB {
|
||||
// not referenced in the MANIFEST (e.g.
|
||||
// 1. It's best effort recovery;
|
||||
// 2. The VersionEdits referencing the SST files are appended to
|
||||
// RecoveryContext, DB crashes when syncing the MANIFEST, the VersionEdits are
|
||||
// MANIFEST, DB crashes when syncing the MANIFEST, the VersionEdits are
|
||||
// still not synced to MANIFEST during recovery.)
|
||||
// It stores the SST files to be deleted in RecoveryContext. In the
|
||||
// We delete these SST files. In the
|
||||
// meantime, we find out the largest file number present in the paths, and
|
||||
// bump up the version set's next_file_number_ to be 1 + largest_file_number.
|
||||
// recovery_ctx stores the context about version edits and files to be
|
||||
// deleted. All those edits are persisted to new Manifest after successfully
|
||||
// syncing the new WAL.
|
||||
Status DeleteUnreferencedSstFiles(RecoveryContext* recovery_ctx);
|
||||
Status DeleteUnreferencedSstFiles();
|
||||
|
||||
// SetDbSessionId() should be called in the constuctor DBImpl()
|
||||
// to ensure that db_session_id_ gets updated every time the DB is opened
|
||||
void SetDbSessionId();
|
||||
|
||||
Status FailIfCfHasTs(const ColumnFamilyHandle* column_family) const;
|
||||
Status FailIfTsMismatchCf(ColumnFamilyHandle* column_family, const Slice& ts,
|
||||
bool ts_for_read) const;
|
||||
|
||||
// recovery_ctx stores the context about version edits and
|
||||
// LogAndApplyForRecovery persist all those edits to new Manifest after
|
||||
// successfully syncing new WAL.
|
||||
// LogAndApplyForRecovery should be called only once during recovery and it
|
||||
// should be called when RocksDB writes to a first new MANIFEST since this
|
||||
// recovery.
|
||||
Status LogAndApplyForRecovery(const RecoveryContext& recovery_ctx);
|
||||
Status FailIfTsSizesMismatch(const ColumnFamilyHandle* column_family,
|
||||
const Slice& ts) const;
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
@@ -1575,38 +1465,12 @@ class DBImpl : public DB {
|
||||
return s;
|
||||
}
|
||||
|
||||
bool IsSyncing() { return getting_synced; }
|
||||
|
||||
uint64_t GetPreSyncSize() {
|
||||
assert(getting_synced);
|
||||
return pre_sync_size;
|
||||
}
|
||||
|
||||
void PrepareForSync() {
|
||||
assert(!getting_synced);
|
||||
// Size is expected to be monotonically increasing.
|
||||
assert(writer->file()->GetFlushedSize() >= pre_sync_size);
|
||||
getting_synced = true;
|
||||
pre_sync_size = writer->file()->GetFlushedSize();
|
||||
}
|
||||
|
||||
void FinishSync() {
|
||||
assert(getting_synced);
|
||||
getting_synced = false;
|
||||
}
|
||||
|
||||
uint64_t number;
|
||||
// Visual Studio doesn't support deque's member to be noncopyable because
|
||||
// of a std::unique_ptr as a member.
|
||||
log::Writer* writer; // own
|
||||
|
||||
private:
|
||||
// true for some prefix of logs_
|
||||
bool getting_synced = false;
|
||||
// The size of the file before the sync happens. This amount is guaranteed
|
||||
// to be persisted even if appends happen during sync so it can be used for
|
||||
// tracking the synced size in MANIFEST.
|
||||
uint64_t pre_sync_size = 0;
|
||||
};
|
||||
|
||||
// PurgeFileInfo is a structure to hold information of files to be deleted in
|
||||
@@ -1662,11 +1526,7 @@ class DBImpl : public DB {
|
||||
output_path_id(_output_path_id),
|
||||
exclusive(_exclusive),
|
||||
disallow_trivial_move(_disallow_trivial_move),
|
||||
canceled(_canceled ? *_canceled : canceled_internal_storage) {}
|
||||
// When _canceled is not provided by ther user, we assign the reference of
|
||||
// canceled_internal_storage to it to consolidate canceled and
|
||||
// manual_compaction_paused since DisableManualCompaction() might be
|
||||
// called
|
||||
canceled(_canceled) {}
|
||||
|
||||
ColumnFamilyData* cfd;
|
||||
int input_level;
|
||||
@@ -1683,12 +1543,7 @@ class DBImpl : public DB {
|
||||
InternalKey* manual_end = nullptr; // how far we are compacting
|
||||
InternalKey tmp_storage; // Used to keep track of compaction progress
|
||||
InternalKey tmp_storage1; // Used to keep track of compaction progress
|
||||
|
||||
// When the user provides a canceled pointer in CompactRangeOptions, the
|
||||
// above varaibe is the reference of the user-provided
|
||||
// `canceled`, otherwise, it is the reference of canceled_internal_storage
|
||||
std::atomic<bool> canceled_internal_storage = false;
|
||||
std::atomic<bool>& canceled; // Compaction canceled pointer reference
|
||||
std::atomic<bool>* canceled; // Compaction canceled by the user?
|
||||
};
|
||||
struct PrepickedCompaction {
|
||||
// background compaction takes ownership of `compaction`.
|
||||
@@ -1790,8 +1645,7 @@ class DBImpl : public DB {
|
||||
// corrupted_log_found is set to true if we recover from a corrupted log file.
|
||||
Status RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
SequenceNumber* next_sequence, bool read_only,
|
||||
bool* corrupted_log_found,
|
||||
RecoveryContext* recovery_ctx);
|
||||
bool* corrupted_log_found);
|
||||
|
||||
// The following two methods are used to flush a memtable to
|
||||
// storage. The first one is used at database RecoveryTime (when the
|
||||
@@ -1925,12 +1779,9 @@ class DBImpl : public DB {
|
||||
Status PreprocessWrite(const WriteOptions& write_options, bool* need_log_sync,
|
||||
WriteContext* write_context);
|
||||
|
||||
// Merge write batches in the write group into merged_batch.
|
||||
// Returns OK if merge is successful.
|
||||
// Returns Corruption if corruption in write batch is detected.
|
||||
Status MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
WriteBatch* tmp_batch, WriteBatch** merged_batch,
|
||||
size_t* write_with_wal, WriteBatch** to_be_cached_state);
|
||||
WriteBatch* MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
WriteBatch* tmp_batch, size_t* write_with_wal,
|
||||
WriteBatch** to_be_cached_state);
|
||||
|
||||
// rate_limiter_priority is used to charge `DBOptions::rate_limiter`
|
||||
// for automatic WAL flush (`Options::manual_wal_flush` == false)
|
||||
@@ -1938,13 +1789,12 @@ class DBImpl : public DB {
|
||||
IOStatus WriteToWAL(const WriteBatch& merged_batch, log::Writer* log_writer,
|
||||
uint64_t* log_used, uint64_t* log_size,
|
||||
Env::IOPriority rate_limiter_priority,
|
||||
LogFileNumberSize& log_file_number_size);
|
||||
bool with_db_mutex = false, bool with_log_mutex = false);
|
||||
|
||||
IOStatus WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence,
|
||||
LogFileNumberSize& log_file_number_size);
|
||||
SequenceNumber sequence);
|
||||
|
||||
IOStatus ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
uint64_t* log_used,
|
||||
@@ -2069,24 +1919,10 @@ class DBImpl : public DB {
|
||||
SnapshotImpl* GetSnapshotImpl(bool is_write_conflict_boundary,
|
||||
bool lock = true);
|
||||
|
||||
// If snapshot_seq != kMaxSequenceNumber, then this function can only be
|
||||
// called from the write thread that publishes sequence numbers to readers.
|
||||
// For 1) write-committed, or 2) write-prepared + one-write-queue, this will
|
||||
// be the write thread performing memtable writes. For write-prepared with
|
||||
// two write queues, this will be the write thread writing commit marker to
|
||||
// the WAL.
|
||||
// If snapshot_seq == kMaxSequenceNumber, this function is called by a caller
|
||||
// ensuring no writes to the database.
|
||||
std::pair<Status, std::shared_ptr<const SnapshotImpl>>
|
||||
CreateTimestampedSnapshotImpl(SequenceNumber snapshot_seq, uint64_t ts,
|
||||
bool lock = true);
|
||||
|
||||
uint64_t GetMaxTotalWalSize() const;
|
||||
|
||||
FSDirectory* GetDataDir(ColumnFamilyData* cfd, size_t path_id) const;
|
||||
|
||||
Status MaybeReleaseTimestampedSnapshotsAndCheck();
|
||||
|
||||
Status CloseHelper();
|
||||
|
||||
void WaitForBackgroundWork();
|
||||
@@ -2138,11 +1974,6 @@ class DBImpl : public DB {
|
||||
IOStatus CreateWAL(uint64_t log_file_num, uint64_t recycle_log_number,
|
||||
size_t preallocate_block_size, log::Writer** new_log);
|
||||
|
||||
// Verify SST file unique id between Manifest and table properties to make
|
||||
// sure they're the same. Currently only used during DB open when
|
||||
// `verify_sst_unique_id_in_manifest = true`.
|
||||
Status VerifySstUniqueIdInManifest();
|
||||
|
||||
// Validate self-consistency of DB options
|
||||
static Status ValidateOptions(const DBOptions& db_options);
|
||||
// Validate self-consistency of DB options and its consistency with cf options
|
||||
@@ -2288,7 +2119,11 @@ class DBImpl : public DB {
|
||||
// are protected by locking both mutex_ and log_write_mutex_, and reads must
|
||||
// be under either mutex_ or log_write_mutex_.
|
||||
std::deque<LogFileNumberSize> alive_log_files_;
|
||||
|
||||
// Caching the result of `alive_log_files_.back()` so that we do not have to
|
||||
// call `alive_log_files_.back()` in the write thread (WriteToWAL()) which
|
||||
// requires locking db mutex if log_mutex_ is not already held in
|
||||
// two-write-queues mode.
|
||||
std::deque<LogFileNumberSize>::reverse_iterator alive_log_files_tail_;
|
||||
// Log files that aren't fully synced, and the current log file.
|
||||
// Synchronization:
|
||||
// - push_back() is done from write_thread_ with locked mutex_ and
|
||||
@@ -2352,8 +2187,6 @@ class DBImpl : public DB {
|
||||
|
||||
SnapshotList snapshots_;
|
||||
|
||||
TimestampedSnapshotList timestamped_snapshots_;
|
||||
|
||||
// For each background job, pending_outputs_ keeps the current file number at
|
||||
// the time that background job started.
|
||||
// FindObsoleteFiles()/PurgeObsoleteFiles() never deletes any file that has
|
||||
@@ -2466,7 +2299,7 @@ class DBImpl : public DB {
|
||||
|
||||
static const int KEEP_LOG_FILE_NUM = 1000;
|
||||
// MSVC version 1800 still does not have constexpr for ::max()
|
||||
static const uint64_t kNoTimeOut = std::numeric_limits<uint64_t>::max();
|
||||
static const uint64_t kNoTimeOut = port::kMaxUint64;
|
||||
|
||||
std::string db_absolute_path_;
|
||||
|
||||
@@ -2562,22 +2395,11 @@ class DBImpl : public DB {
|
||||
std::unique_ptr<StallInterface> wbm_stall_;
|
||||
};
|
||||
|
||||
class GetWithTimestampReadCallback : public ReadCallback {
|
||||
public:
|
||||
explicit GetWithTimestampReadCallback(SequenceNumber seq)
|
||||
: ReadCallback(seq) {}
|
||||
bool IsVisibleFullCheck(SequenceNumber seq) override {
|
||||
return seq <= max_visible_seq_;
|
||||
}
|
||||
};
|
||||
|
||||
extern Options SanitizeOptions(const std::string& db, const Options& src,
|
||||
bool read_only = false,
|
||||
Status* logger_creation_s = nullptr);
|
||||
bool read_only = false);
|
||||
|
||||
extern DBOptions SanitizeOptions(const std::string& db, const DBOptions& src,
|
||||
bool read_only = false,
|
||||
Status* logger_creation_s = nullptr);
|
||||
bool read_only = false);
|
||||
|
||||
extern CompressionType GetCompressionFlush(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
@@ -2643,9 +2465,8 @@ inline Status DBImpl::FailIfCfHasTs(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
inline Status DBImpl::FailIfTsMismatchCf(ColumnFamilyHandle* column_family,
|
||||
const Slice& ts,
|
||||
bool ts_for_read) const {
|
||||
inline Status DBImpl::FailIfTsSizesMismatch(
|
||||
const ColumnFamilyHandle* column_family, const Slice& ts) const {
|
||||
if (!column_family) {
|
||||
return Status::InvalidArgument("column family handle cannot be null");
|
||||
}
|
||||
@@ -2665,19 +2486,6 @@ inline Status DBImpl::FailIfTsMismatchCf(ColumnFamilyHandle* column_family,
|
||||
<< ts_sz << " given";
|
||||
return Status::InvalidArgument(oss.str());
|
||||
}
|
||||
if (ts_for_read) {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
std::string current_ts_low = cfd->GetFullHistoryTsLow();
|
||||
if (!current_ts_low.empty() &&
|
||||
ucmp->CompareTimestamp(ts, current_ts_low) < 0) {
|
||||
std::stringstream oss;
|
||||
oss << "Read timestamp: " << ts.ToString(true)
|
||||
<< " is smaller than full_history_ts_low: "
|
||||
<< Slice(current_ts_low).ToString(true) << std::endl;
|
||||
return Status::InvalidArgument(oss.str());
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
|
||||
@@ -88,13 +88,14 @@ IOStatus DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
autovector<log::Writer*, 1> logs_to_sync;
|
||||
uint64_t current_log_number = logfile_number_;
|
||||
while (logs_.front().number < current_log_number &&
|
||||
logs_.front().IsSyncing()) {
|
||||
logs_.front().getting_synced) {
|
||||
log_sync_cv_.Wait();
|
||||
}
|
||||
for (auto it = logs_.begin();
|
||||
it != logs_.end() && it->number < current_log_number; ++it) {
|
||||
auto& log = *it;
|
||||
log.PrepareForSync();
|
||||
assert(!log.getting_synced);
|
||||
log.getting_synced = true;
|
||||
logs_to_sync.push_back(log.writer);
|
||||
}
|
||||
|
||||
@@ -187,7 +188,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// a memtable without knowing such snapshot(s).
|
||||
uint64_t max_memtable_id = needs_to_sync_closed_wals
|
||||
? cfd->imm()->GetLatestMemTableID()
|
||||
: std::numeric_limits<uint64_t>::max();
|
||||
: port::kMaxUint64;
|
||||
|
||||
// If needs_to_sync_closed_wals is false, then the flush job will pick ALL
|
||||
// existing memtables of the column family when PickMemTable() is called
|
||||
@@ -951,8 +952,6 @@ Status DBImpl::IncreaseFullHistoryTsLowImpl(ColumnFamilyData* cfd,
|
||||
VersionEdit edit;
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
edit.SetFullHistoryTsLow(ts_low);
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::IncreaseFullHistoryTsLowImpl:BeforeEdit",
|
||||
&edit);
|
||||
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
std::string current_ts_low = cfd->GetFullHistoryTsLow();
|
||||
@@ -960,25 +959,12 @@ Status DBImpl::IncreaseFullHistoryTsLowImpl(ColumnFamilyData* cfd,
|
||||
assert(ucmp->timestamp_size() == ts_low.size() && !ts_low.empty());
|
||||
if (!current_ts_low.empty() &&
|
||||
ucmp->CompareTimestamp(ts_low, current_ts_low) < 0) {
|
||||
return Status::InvalidArgument("Cannot decrease full_history_ts_low");
|
||||
return Status::InvalidArgument(
|
||||
"Cannot decrease full_history_timestamp_low");
|
||||
}
|
||||
|
||||
Status s = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
current_ts_low = cfd->GetFullHistoryTsLow();
|
||||
if (!current_ts_low.empty() &&
|
||||
ucmp->CompareTimestamp(current_ts_low, ts_low) > 0) {
|
||||
std::stringstream oss;
|
||||
oss << "full_history_ts_low: " << Slice(current_ts_low).ToString(true)
|
||||
<< " is set to be higher than the requested "
|
||||
"timestamp: "
|
||||
<< Slice(ts_low).ToString(true) << std::endl;
|
||||
return Status::TryAgain(oss.str());
|
||||
}
|
||||
return Status::OK();
|
||||
return versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(), &edit,
|
||||
&mutex_);
|
||||
}
|
||||
|
||||
Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
@@ -1055,8 +1041,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
}
|
||||
s = RunManualCompaction(cfd, ColumnFamilyData::kCompactAllLevels,
|
||||
final_output_level, options, begin, end, exclusive,
|
||||
false, std::numeric_limits<uint64_t>::max(),
|
||||
trim_ts);
|
||||
false, port::kMaxUint64, trim_ts);
|
||||
} else {
|
||||
int first_overlapped_level = kInvalidLevel;
|
||||
int max_overlapped_level = kInvalidLevel;
|
||||
@@ -1093,7 +1078,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
if (s.ok() && first_overlapped_level != kInvalidLevel) {
|
||||
// max_file_num_to_ignore can be used to filter out newly created SST
|
||||
// files, useful for bottom level compaction in a manual compaction
|
||||
uint64_t max_file_num_to_ignore = std::numeric_limits<uint64_t>::max();
|
||||
uint64_t max_file_num_to_ignore = port::kMaxUint64;
|
||||
uint64_t next_file_number = versions_->current_next_file_number();
|
||||
final_output_level = max_overlapped_level;
|
||||
int output_level;
|
||||
@@ -1231,10 +1216,6 @@ Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
|
||||
|
||||
// Perform CompactFiles
|
||||
TEST_SYNC_POINT("TestCompactFiles::IngestExternalFile2");
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"TestCompactFiles:PausingManualCompaction:3",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<int>*>(&manual_compaction_paused_)));
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
@@ -1390,7 +1371,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, Env::Priority::USER, io_tracer_,
|
||||
kManualCompactionCanceledFalse_, db_id_, db_session_id_,
|
||||
&manual_compaction_paused_, nullptr, db_id_, db_session_id_,
|
||||
c->column_family_data()->GetFullHistoryTsLow(), c->trim_ts(),
|
||||
&blob_callback_);
|
||||
|
||||
@@ -1669,8 +1650,7 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
f->smallest, f->largest, f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction, f->temperature, f->oldest_blob_file_number,
|
||||
f->oldest_ancester_time, f->file_creation_time, f->file_checksum,
|
||||
f->file_checksum_func_name, f->min_timestamp, f->max_timestamp,
|
||||
f->unique_id);
|
||||
f->file_checksum_func_name, f->min_timestamp, f->max_timestamp);
|
||||
}
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] Apply version edit:\n%s", cfd->GetName().c_str(),
|
||||
@@ -1856,7 +1836,8 @@ Status DBImpl::RunManualCompaction(
|
||||
// and `CompactRangeOptions::canceled` might not work well together.
|
||||
while (bg_bottom_compaction_scheduled_ > 0 ||
|
||||
bg_compaction_scheduled_ > 0) {
|
||||
if (manual_compaction_paused_ > 0 || manual.canceled == true) {
|
||||
if (manual_compaction_paused_ > 0 ||
|
||||
(manual.canceled != nullptr && *manual.canceled == true)) {
|
||||
// Pretend the error came from compaction so the below cleanup/error
|
||||
// handling code can process it.
|
||||
manual.done = true;
|
||||
@@ -1874,12 +1855,11 @@ Status DBImpl::RunManualCompaction(
|
||||
}
|
||||
}
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] Manual compaction starting", cfd->GetName().c_str());
|
||||
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL,
|
||||
immutable_db_options_.info_log.get());
|
||||
|
||||
ROCKS_LOG_BUFFER(&log_buffer, "[%s] Manual compaction starting",
|
||||
cfd->GetName().c_str());
|
||||
|
||||
// We don't check bg_error_ here, because if we get the error in compaction,
|
||||
// the compaction will set manual.status to bg_error_ and set manual.done to
|
||||
// true.
|
||||
@@ -2033,7 +2013,7 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
// be created and scheduled, status::OK() will be returned.
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
}
|
||||
const uint64_t flush_memtable_id = std::numeric_limits<uint64_t>::max();
|
||||
const uint64_t flush_memtable_id = port::kMaxUint64;
|
||||
if (s.ok()) {
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
!cached_recoverable_state_empty_.load()) {
|
||||
@@ -2393,18 +2373,10 @@ Status DBImpl::EnableAutoCompaction(
|
||||
return s;
|
||||
}
|
||||
|
||||
// NOTE: Calling DisableManualCompaction() may overwrite the
|
||||
// user-provided canceled variable in CompactRangeOptions
|
||||
void DBImpl::DisableManualCompaction() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
manual_compaction_paused_.fetch_add(1, std::memory_order_release);
|
||||
|
||||
// Mark the canceled as true when the cancellation is triggered by
|
||||
// manual_compaction_paused (may overwrite user-provided `canceled`)
|
||||
for (const auto& manual_compaction : manual_compaction_dequeue_) {
|
||||
manual_compaction->canceled = true;
|
||||
}
|
||||
|
||||
// Wake up manual compactions waiting to start.
|
||||
bg_cv_.SignalAll();
|
||||
|
||||
@@ -2417,11 +2389,6 @@ void DBImpl::DisableManualCompaction() {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: In contrast to DisableManualCompaction(), calling
|
||||
// EnableManualCompaction() does NOT overwrite the user-provided *canceled
|
||||
// variable to be false since there is NO CHANCE a canceled compaction
|
||||
// is uncanceled. In other words, a canceled compaction must have been
|
||||
// dropped out of the manual compaction queue, when we disable it.
|
||||
void DBImpl::EnableManualCompaction() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
assert(manual_compaction_paused_ > 0);
|
||||
@@ -3067,7 +3034,10 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
status = Status::ShutdownInProgress();
|
||||
} else if (is_manual &&
|
||||
manual_compaction->canceled.load(std::memory_order_acquire)) {
|
||||
manual_compaction_paused_.load(std::memory_order_acquire) > 0) {
|
||||
status = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
} else if (is_manual && manual_compaction->canceled &&
|
||||
manual_compaction->canceled->load(std::memory_order_acquire)) {
|
||||
status = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
} else {
|
||||
@@ -3305,7 +3275,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
f->fd.largest_seqno, f->marked_for_compaction, f->temperature,
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->file_checksum, f->file_checksum_func_name,
|
||||
f->min_timestamp, f->max_timestamp, f->unique_id);
|
||||
f->min_timestamp, f->max_timestamp);
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
@@ -3384,7 +3354,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
assert(is_snapshot_supported_ || snapshots_.empty());
|
||||
|
||||
CompactionJob compaction_job(
|
||||
job_context->job_id, c.get(), immutable_db_options_,
|
||||
mutable_db_options_, file_options_for_compaction_, versions_.get(),
|
||||
@@ -3396,9 +3365,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, thread_pri, io_tracer_,
|
||||
is_manual ? manual_compaction->canceled
|
||||
: kManualCompactionCanceledFalse_,
|
||||
db_id_, db_session_id_, c->column_family_data()->GetFullHistoryTsLow(),
|
||||
is_manual ? &manual_compaction_paused_ : nullptr,
|
||||
is_manual ? manual_compaction->canceled : nullptr, db_id_,
|
||||
db_session_id_, c->column_family_data()->GetFullHistoryTsLow(),
|
||||
c->trim_ts(), &blob_callback_);
|
||||
compaction_job.Prepare();
|
||||
|
||||
|
||||
@@ -118,11 +118,10 @@ Status DBImpl::TEST_CompactRange(int level, const Slice* begin,
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO)
|
||||
? level
|
||||
: level + 1;
|
||||
return RunManualCompaction(
|
||||
cfd, level, output_level, CompactRangeOptions(), begin, end, true,
|
||||
disallow_trivial_move,
|
||||
std::numeric_limits<uint64_t>::max() /*max_file_num_to_ignore*/,
|
||||
"" /*trim_ts*/);
|
||||
return RunManualCompaction(cfd, level, output_level, CompactRangeOptions(),
|
||||
begin, end, true, disallow_trivial_move,
|
||||
port::kMaxUint64 /*max_file_num_to_ignore*/,
|
||||
"" /*trim_ts*/);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_SwitchMemtable(ColumnFamilyData* cfd) {
|
||||
|
||||
@@ -137,7 +137,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
f->fd.largest_seqno, f->marked_for_compaction, f->temperature,
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->file_checksum, f->file_checksum_func_name,
|
||||
f->min_timestamp, f->max_timestamp, f->unique_id);
|
||||
f->min_timestamp, f->max_timestamp);
|
||||
}
|
||||
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
|
||||
+60
-68
@@ -166,8 +166,8 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
job_context->log_number = MinLogNumberToKeep();
|
||||
job_context->prev_log_number = versions_->prev_log_number();
|
||||
|
||||
versions_->AddLiveFiles(&job_context->sst_live, &job_context->blob_live);
|
||||
if (doing_the_full_scan) {
|
||||
versions_->AddLiveFiles(&job_context->sst_live, &job_context->blob_live);
|
||||
InfoLogPrefix info_log_prefix(!immutable_db_options_.db_log_dir.empty(),
|
||||
dbname_);
|
||||
std::set<std::string> paths;
|
||||
@@ -242,14 +242,6 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
log_file, immutable_db_options_.db_log_dir);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Instead of filling ob_context->sst_live and job_context->blob_live,
|
||||
// directly remove files that show up in any Version. This is because
|
||||
// candidate files tend to be a small percentage of all files, so it is
|
||||
// usually cheaper to check them against every version, compared to
|
||||
// building a map for all files.
|
||||
versions_->RemoveLiveFiles(job_context->sst_delete_files,
|
||||
job_context->blob_delete_files);
|
||||
}
|
||||
|
||||
// logs_ is empty when called during recovery, in which case there can't yet
|
||||
@@ -288,7 +280,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
}
|
||||
while (!logs_.empty() && logs_.front().number < min_log_number) {
|
||||
auto& log = logs_.front();
|
||||
if (log.IsSyncing()) {
|
||||
if (log.getting_synced) {
|
||||
log_sync_cv_.Wait();
|
||||
// logs_ could have changed while we were waiting.
|
||||
continue;
|
||||
@@ -403,10 +395,8 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
state.manifest_delete_files.size());
|
||||
// We may ignore the dbname when generating the file names.
|
||||
for (auto& file : state.sst_delete_files) {
|
||||
if (!file.only_delete_metadata) {
|
||||
candidate_files.emplace_back(
|
||||
MakeTableFileName(file.metadata->fd.GetNumber()), file.path);
|
||||
}
|
||||
candidate_files.emplace_back(
|
||||
MakeTableFileName(file.metadata->fd.GetNumber()), file.path);
|
||||
if (file.metadata->table_reader_handle) {
|
||||
table_cache_->Release(file.metadata->table_reader_handle);
|
||||
}
|
||||
@@ -771,7 +761,7 @@ uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
assert(!cfds_to_flush.empty());
|
||||
assert(cfds_to_flush.size() == edit_lists.size());
|
||||
|
||||
uint64_t min_log_number_to_keep = std::numeric_limits<uint64_t>::max();
|
||||
uint64_t min_log_number_to_keep = port::kMaxUint64;
|
||||
for (const auto& edit_list : edit_lists) {
|
||||
uint64_t log = 0;
|
||||
for (const auto& e : edit_list) {
|
||||
@@ -783,7 +773,7 @@ uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
min_log_number_to_keep = std::min(min_log_number_to_keep, log);
|
||||
}
|
||||
}
|
||||
if (min_log_number_to_keep == std::numeric_limits<uint64_t>::max()) {
|
||||
if (min_log_number_to_keep == port::kMaxUint64) {
|
||||
min_log_number_to_keep = cfds_to_flush[0]->GetLogNumber();
|
||||
for (size_t i = 1; i < cfds_to_flush.size(); i++) {
|
||||
min_log_number_to_keep =
|
||||
@@ -873,61 +863,49 @@ uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
return min_log_number_to_keep;
|
||||
}
|
||||
|
||||
void DBImpl::SetDBId(std::string&& id, bool read_only,
|
||||
RecoveryContext* recovery_ctx) {
|
||||
assert(db_id_.empty());
|
||||
assert(!id.empty());
|
||||
db_id_ = std::move(id);
|
||||
if (!read_only && immutable_db_options_.write_dbid_to_manifest) {
|
||||
assert(recovery_ctx != nullptr);
|
||||
assert(versions_->GetColumnFamilySet() != nullptr);
|
||||
VersionEdit edit;
|
||||
edit.SetDBId(db_id_);
|
||||
versions_->db_id_ = db_id_;
|
||||
recovery_ctx->UpdateVersionEdits(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), edit);
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::SetupDBId(bool read_only, RecoveryContext* recovery_ctx) {
|
||||
Status DBImpl::SetDBId(bool read_only) {
|
||||
Status s;
|
||||
// Check for the IDENTITY file and create it if not there or
|
||||
// broken or not matching manifest
|
||||
std::string db_id_in_file;
|
||||
s = fs_->FileExists(IdentityFileName(dbname_), IOOptions(), nullptr);
|
||||
if (s.ok()) {
|
||||
s = GetDbIdentityFromIdentityFile(&db_id_in_file);
|
||||
if (s.ok() && !db_id_in_file.empty()) {
|
||||
if (db_id_.empty()) {
|
||||
// Loaded from file and wasn't already known from manifest
|
||||
SetDBId(std::move(db_id_in_file), read_only, recovery_ctx);
|
||||
return s;
|
||||
} else if (db_id_ == db_id_in_file) {
|
||||
// Loaded from file and matches manifest
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.IsNotFound()) {
|
||||
s = Status::OK();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
assert(s.IsIOError());
|
||||
return s;
|
||||
}
|
||||
// Otherwise IDENTITY file is missing or no good.
|
||||
// Generate new id if needed
|
||||
// Happens when immutable_db_options_.write_dbid_to_manifest is set to true
|
||||
// the very first time.
|
||||
if (db_id_.empty()) {
|
||||
SetDBId(env_->GenerateUniqueId(), read_only, recovery_ctx);
|
||||
}
|
||||
// Persist it to IDENTITY file if allowed
|
||||
if (!read_only) {
|
||||
// Check for the IDENTITY file and create it if not there.
|
||||
s = fs_->FileExists(IdentityFileName(dbname_), IOOptions(), nullptr);
|
||||
// Typically Identity file is created in NewDB() and for some reason if
|
||||
// it is no longer available then at this point DB ID is not in Identity
|
||||
// file or Manifest.
|
||||
if (s.IsNotFound()) {
|
||||
// Create a new DB ID, saving to file only if allowed
|
||||
if (read_only) {
|
||||
db_id_ = env_->GenerateUniqueId();
|
||||
return Status::OK();
|
||||
} else {
|
||||
s = SetIdentityFile(env_, dbname_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
} else if (!s.ok()) {
|
||||
assert(s.IsIOError());
|
||||
return s;
|
||||
}
|
||||
s = GetDbIdentityFromIdentityFile(&db_id_);
|
||||
if (immutable_db_options_.write_dbid_to_manifest && s.ok()) {
|
||||
VersionEdit edit;
|
||||
edit.SetDBId(db_id_);
|
||||
Options options;
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
versions_->db_id_ = db_id_;
|
||||
s = versions_->LogAndApply(versions_->GetColumnFamilySet()->GetDefault(),
|
||||
mutable_cf_options, &edit, &mutex_, nullptr,
|
||||
/* new_descriptor_log */ false);
|
||||
}
|
||||
} else if (!read_only) {
|
||||
s = SetIdentityFile(env_, dbname_, db_id_);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::DeleteUnreferencedSstFiles(RecoveryContext* recovery_ctx) {
|
||||
Status DBImpl::DeleteUnreferencedSstFiles() {
|
||||
mutex_.AssertHeld();
|
||||
std::vector<std::string> paths;
|
||||
paths.push_back(NormalizePath(dbname_ + std::string(1, kFilePathSeparator)));
|
||||
@@ -947,6 +925,7 @@ Status DBImpl::DeleteUnreferencedSstFiles(RecoveryContext* recovery_ctx) {
|
||||
|
||||
uint64_t next_file_number = versions_->current_next_file_number();
|
||||
uint64_t largest_file_number = next_file_number;
|
||||
std::set<std::string> files_to_delete;
|
||||
Status s;
|
||||
for (const auto& path : paths) {
|
||||
std::vector<std::string> files;
|
||||
@@ -964,9 +943,8 @@ Status DBImpl::DeleteUnreferencedSstFiles(RecoveryContext* recovery_ctx) {
|
||||
const std::string normalized_fpath = path + fname;
|
||||
largest_file_number = std::max(largest_file_number, number);
|
||||
if (type == kTableFile && number >= next_file_number &&
|
||||
recovery_ctx->files_to_delete_.find(normalized_fpath) ==
|
||||
recovery_ctx->files_to_delete_.end()) {
|
||||
recovery_ctx->files_to_delete_.emplace(normalized_fpath);
|
||||
files_to_delete.find(normalized_fpath) == files_to_delete.end()) {
|
||||
files_to_delete.insert(normalized_fpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -983,7 +961,21 @@ Status DBImpl::DeleteUnreferencedSstFiles(RecoveryContext* recovery_ctx) {
|
||||
assert(versions_->GetColumnFamilySet());
|
||||
ColumnFamilyData* default_cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
assert(default_cfd);
|
||||
recovery_ctx->UpdateVersionEdits(default_cfd, edit);
|
||||
s = versions_->LogAndApply(
|
||||
default_cfd, *default_cfd->GetLatestMutableCFOptions(), &edit, &mutex_,
|
||||
directories_.GetDbDir(), /*new_descriptor_log*/ false);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
mutex_.Unlock();
|
||||
for (const auto& fname : files_to_delete) {
|
||||
s = env_->DeleteFile(fname);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_.Lock();
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+96
-174
@@ -27,9 +27,8 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
Options SanitizeOptions(const std::string& dbname, const Options& src,
|
||||
bool read_only, Status* logger_creation_s) {
|
||||
auto db_options =
|
||||
SanitizeOptions(dbname, DBOptions(src), read_only, logger_creation_s);
|
||||
bool read_only) {
|
||||
auto db_options = SanitizeOptions(dbname, DBOptions(src), read_only);
|
||||
ImmutableDBOptions immutable_db_options(db_options);
|
||||
auto cf_options =
|
||||
SanitizeOptions(immutable_db_options, ColumnFamilyOptions(src));
|
||||
@@ -37,7 +36,7 @@ Options SanitizeOptions(const std::string& dbname, const Options& src,
|
||||
}
|
||||
|
||||
DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src,
|
||||
bool read_only, Status* logger_creation_s) {
|
||||
bool read_only) {
|
||||
DBOptions result(src);
|
||||
|
||||
if (result.env == nullptr) {
|
||||
@@ -60,9 +59,6 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src,
|
||||
if (!s.ok()) {
|
||||
// No place suitable for logging
|
||||
result.info_log = nullptr;
|
||||
if (logger_creation_s) {
|
||||
*logger_creation_s = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,7 +399,7 @@ IOStatus Directories::SetDirectories(FileSystem* fs, const std::string& dbname,
|
||||
Status DBImpl::Recover(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families, bool read_only,
|
||||
bool error_if_wal_file_exists, bool error_if_data_exists_in_wals,
|
||||
uint64_t* recovered_seq, RecoveryContext* recovery_ctx) {
|
||||
uint64_t* recovered_seq) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
bool is_new_db = false;
|
||||
@@ -522,23 +518,15 @@ Status DBImpl::Recover(
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (immutable_db_options_.verify_sst_unique_id_in_manifest) {
|
||||
s = VerifySstUniqueIdInManifest();
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
s = SetupDBId(read_only, recovery_ctx);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "DB ID: %s\n", db_id_.c_str());
|
||||
s = SetDBId(read_only);
|
||||
if (s.ok() && !read_only) {
|
||||
s = DeleteUnreferencedSstFiles(recovery_ctx);
|
||||
s = DeleteUnreferencedSstFiles();
|
||||
}
|
||||
|
||||
if (immutable_db_options_.paranoid_checks && s.ok()) {
|
||||
s = CheckConsistency();
|
||||
}
|
||||
if (s.ok() && !read_only) {
|
||||
// TODO: share file descriptors (FSDirectory) with SetDirectories above
|
||||
std::map<std::string, std::shared_ptr<FSDirectory>> created_dirs;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
s = cfd->AddDirectories(&created_dirs);
|
||||
@@ -547,6 +535,10 @@ Status DBImpl::Recover(
|
||||
}
|
||||
}
|
||||
}
|
||||
// DB mutex is already held
|
||||
if (s.ok() && immutable_db_options_.persist_stats_to_disk) {
|
||||
s = InitPersistStatsColumnFamily();
|
||||
}
|
||||
|
||||
std::vector<std::string> files_in_wal_dir;
|
||||
if (s.ok()) {
|
||||
@@ -563,6 +555,10 @@ Status DBImpl::Recover(
|
||||
default_cf_handle_ = new ColumnFamilyHandleImpl(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), this, &mutex_);
|
||||
default_cf_internal_stats_ = default_cf_handle_->cfd()->internal_stats();
|
||||
// TODO(Zhongyi): handle single_column_family_mode_ when
|
||||
// persistent_stats is enabled
|
||||
single_column_family_mode_ =
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() == 1;
|
||||
|
||||
// Recover from all newer log files than the ones named in the
|
||||
// descriptor (new log files may have been added by the previous
|
||||
@@ -612,10 +608,7 @@ Status DBImpl::Recover(
|
||||
WalNumber max_wal_number =
|
||||
versions_->GetWalSet().GetWals().rbegin()->first;
|
||||
edit.DeleteWalsBefore(max_wal_number + 1);
|
||||
assert(recovery_ctx != nullptr);
|
||||
assert(versions_->GetColumnFamilySet() != nullptr);
|
||||
recovery_ctx->UpdateVersionEdits(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), edit);
|
||||
s = versions_->LogAndApplyToDefaultColumnFamily(&edit, &mutex_);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -651,8 +644,8 @@ Status DBImpl::Recover(
|
||||
std::sort(wals.begin(), wals.end());
|
||||
|
||||
bool corrupted_wal_found = false;
|
||||
s = RecoverLogFiles(wals, &next_sequence, read_only, &corrupted_wal_found,
|
||||
recovery_ctx);
|
||||
s = RecoverLogFiles(wals, &next_sequence, read_only,
|
||||
&corrupted_wal_found);
|
||||
if (corrupted_wal_found && recovered_seq != nullptr) {
|
||||
*recovered_seq = next_sequence;
|
||||
}
|
||||
@@ -705,25 +698,6 @@ Status DBImpl::Recover(
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::VerifySstUniqueIdInManifest() {
|
||||
mutex_.AssertHeld();
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Verifying SST unique id between MANIFEST and SST file table properties");
|
||||
Status status;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (!cfd->IsDropped()) {
|
||||
auto version = cfd->current();
|
||||
version->Ref();
|
||||
mutex_.Unlock();
|
||||
status = version->VerifySstUniqueIds();
|
||||
mutex_.Lock();
|
||||
version->Unref();
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::PersistentStatsProcessFormatVersion() {
|
||||
mutex_.AssertHeld();
|
||||
Status s;
|
||||
@@ -786,11 +760,11 @@ Status DBImpl::PersistentStatsProcessFormatVersion() {
|
||||
WriteBatch batch;
|
||||
if (s.ok()) {
|
||||
s = batch.Put(persist_stats_cf_handle_, kFormatVersionKeyString,
|
||||
std::to_string(kStatsCFCurrentFormatVersion));
|
||||
ToString(kStatsCFCurrentFormatVersion));
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = batch.Put(persist_stats_cf_handle_, kCompatibleVersionKeyString,
|
||||
std::to_string(kStatsCFCompatibleFormatVersion));
|
||||
ToString(kStatsCFCompatibleFormatVersion));
|
||||
}
|
||||
if (s.ok()) {
|
||||
WriteOptions wo;
|
||||
@@ -831,30 +805,10 @@ Status DBImpl::InitPersistStatsColumnFamily() {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::LogAndApplyForRecovery(const RecoveryContext& recovery_ctx) {
|
||||
mutex_.AssertHeld();
|
||||
assert(versions_->descriptor_log_ == nullptr);
|
||||
Status s = versions_->LogAndApply(
|
||||
recovery_ctx.cfds_, recovery_ctx.mutable_cf_opts_,
|
||||
recovery_ctx.edit_lists_, &mutex_, directories_.GetDbDir());
|
||||
if (s.ok() && !(recovery_ctx.files_to_delete_.empty())) {
|
||||
mutex_.Unlock();
|
||||
for (const auto& fname : recovery_ctx.files_to_delete_) {
|
||||
s = env_->DeleteFile(fname);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_.Lock();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// REQUIRES: wal_numbers are sorted in ascending order
|
||||
Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
SequenceNumber* next_sequence, bool read_only,
|
||||
bool* corrupted_wal_found,
|
||||
RecoveryContext* recovery_ctx) {
|
||||
bool* corrupted_wal_found) {
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Env* env;
|
||||
Logger* info_log;
|
||||
@@ -993,6 +947,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
// Read all the records and add to a memtable
|
||||
std::string scratch;
|
||||
Slice record;
|
||||
WriteBatch batch;
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::RecoverLogFiles:BeforeReadWal",
|
||||
/*arg=*/nullptr);
|
||||
@@ -1006,20 +961,10 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
continue;
|
||||
}
|
||||
|
||||
// We create a new batch and initialize with a valid prot_info_ to store
|
||||
// the data checksums
|
||||
WriteBatch batch;
|
||||
|
||||
status = WriteBatchInternal::SetContents(&batch, record);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
status = WriteBatchInternal::UpdateProtectionInfo(&batch,
|
||||
8 /* bytes_per_key */);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
SequenceNumber sequence = WriteBatchInternal::Sequence(&batch);
|
||||
|
||||
if (immutable_db_options_.wal_recovery_mode ==
|
||||
@@ -1317,36 +1262,44 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
// VersionSet::next_file_number_ always to be strictly greater than any
|
||||
// log number
|
||||
versions_->MarkFileNumberUsed(max_wal_number + 1);
|
||||
assert(recovery_ctx != nullptr);
|
||||
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
autovector<const MutableCFOptions*> cf_opts;
|
||||
autovector<autovector<VersionEdit*>> edit_lists;
|
||||
for (auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
cfds.push_back(cfd);
|
||||
cf_opts.push_back(cfd->GetLatestMutableCFOptions());
|
||||
auto iter = version_edits.find(cfd->GetID());
|
||||
assert(iter != version_edits.end());
|
||||
recovery_ctx->UpdateVersionEdits(cfd, iter->second);
|
||||
edit_lists.push_back({&iter->second});
|
||||
}
|
||||
|
||||
std::unique_ptr<VersionEdit> wal_deletion;
|
||||
if (flushed) {
|
||||
VersionEdit wal_deletion;
|
||||
wal_deletion = std::make_unique<VersionEdit>();
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest) {
|
||||
wal_deletion.DeleteWalsBefore(max_wal_number + 1);
|
||||
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);
|
||||
wal_deletion->SetMinLogNumberToKeep(max_wal_number + 1);
|
||||
}
|
||||
assert(versions_->GetColumnFamilySet() != nullptr);
|
||||
recovery_ctx->UpdateVersionEdits(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), wal_deletion);
|
||||
edit_lists.back().push_back(wal_deletion.get());
|
||||
}
|
||||
|
||||
// write MANIFEST with update
|
||||
status = versions_->LogAndApply(cfds, cf_opts, edit_lists, &mutex_,
|
||||
directories_.GetDbDir(),
|
||||
/*new_descriptor_log=*/true);
|
||||
}
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
if (data_seen && !flushed) {
|
||||
status = RestoreAliveLogFiles(wal_numbers);
|
||||
} else if (!wal_numbers.empty()) { // If there's no data in the WAL, or we
|
||||
// flushed all the data, still
|
||||
} else {
|
||||
// If there's no data in the WAL, or we flushed all the data, still
|
||||
// truncate the log file. If the process goes into a crash loop before
|
||||
// the file is deleted, the preallocated space will never get freed.
|
||||
const bool truncate = !read_only;
|
||||
@@ -1369,7 +1322,6 @@ Status DBImpl::GetLogSizeAndMaybeTruncate(uint64_t wal_number, bool truncate,
|
||||
Status s;
|
||||
// This gets the appear size of the wals, not including preallocated space.
|
||||
s = env_->GetFileSize(fname, &log.size);
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::GetLogSizeAndMaybeTruncate:0", /*arg=*/&s);
|
||||
if (s.ok() && truncate) {
|
||||
std::unique_ptr<FSWritableFile> last_log;
|
||||
Status truncate_status = fs_->ReopenWritableFile(
|
||||
@@ -1431,6 +1383,7 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& wal_numbers) {
|
||||
total_log_size_ += log.size;
|
||||
alive_log_files_.push_back(log);
|
||||
}
|
||||
alive_log_files_tail_ = alive_log_files_.rbegin();
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
@@ -1540,14 +1493,13 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
constexpr int level = 0;
|
||||
|
||||
if (s.ok() && has_output) {
|
||||
edit->AddFile(level, meta.fd.GetNumber(), meta.fd.GetPathId(),
|
||||
meta.fd.GetFileSize(), meta.smallest, meta.largest,
|
||||
meta.fd.smallest_seqno, meta.fd.largest_seqno,
|
||||
meta.marked_for_compaction, meta.temperature,
|
||||
meta.oldest_blob_file_number, meta.oldest_ancester_time,
|
||||
meta.file_creation_time, meta.file_checksum,
|
||||
meta.file_checksum_func_name, meta.min_timestamp,
|
||||
meta.max_timestamp, meta.unique_id);
|
||||
edit->AddFile(
|
||||
level, meta.fd.GetNumber(), meta.fd.GetPathId(), meta.fd.GetFileSize(),
|
||||
meta.smallest, meta.largest, meta.fd.smallest_seqno,
|
||||
meta.fd.largest_seqno, meta.marked_for_compaction, meta.temperature,
|
||||
meta.oldest_blob_file_number, meta.oldest_ancester_time,
|
||||
meta.file_creation_time, meta.file_checksum,
|
||||
meta.file_checksum_func_name, meta.min_timestamp, meta.max_timestamp);
|
||||
|
||||
for (const auto& blob : blob_file_additions) {
|
||||
edit->AddBlobFile(blob);
|
||||
@@ -1741,7 +1693,6 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
|
||||
*dbptr = nullptr;
|
||||
assert(handles);
|
||||
handles->clear();
|
||||
|
||||
size_t max_write_buffer_size = 0;
|
||||
@@ -1751,13 +1702,6 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
|
||||
DBImpl* impl = new DBImpl(db_options, dbname, seq_per_batch, batch_per_txn);
|
||||
if (!impl->immutable_db_options_.info_log) {
|
||||
s = impl->init_logger_creation_s_;
|
||||
delete impl;
|
||||
return s;
|
||||
} else {
|
||||
assert(impl->init_logger_creation_s_.ok());
|
||||
}
|
||||
s = impl->env_->CreateDirIfMissing(impl->immutable_db_options_.GetWalDir());
|
||||
if (s.ok()) {
|
||||
std::vector<std::string> paths;
|
||||
@@ -1791,13 +1735,11 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
|
||||
impl->wal_in_db_path_ = impl->immutable_db_options_.IsWalDirSameAsDBPath();
|
||||
RecoveryContext recovery_ctx;
|
||||
impl->mutex_.Lock();
|
||||
|
||||
impl->mutex_.Lock();
|
||||
// Handles create_if_missing, error_if_exists
|
||||
uint64_t recovered_seq(kMaxSequenceNumber);
|
||||
s = impl->Recover(column_families, false, false, false, &recovered_seq,
|
||||
&recovery_ctx);
|
||||
s = impl->Recover(column_families, false, false, false, &recovered_seq);
|
||||
if (s.ok()) {
|
||||
uint64_t new_log_number = impl->versions_->NewFileNumber();
|
||||
log::Writer* new_log = nullptr;
|
||||
@@ -1814,11 +1756,46 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
// set column family handles
|
||||
for (auto cf : column_families) {
|
||||
auto cfd =
|
||||
impl->versions_->GetColumnFamilySet()->GetColumnFamily(cf.name);
|
||||
if (cfd != nullptr) {
|
||||
handles->push_back(
|
||||
new ColumnFamilyHandleImpl(cfd, impl, &impl->mutex_));
|
||||
impl->NewThreadStatusCfInfo(cfd);
|
||||
} else {
|
||||
if (db_options.create_missing_column_families) {
|
||||
// missing column family, create it
|
||||
ColumnFamilyHandle* handle;
|
||||
impl->mutex_.Unlock();
|
||||
s = impl->CreateColumnFamily(cf.options, cf.name, &handle);
|
||||
impl->mutex_.Lock();
|
||||
if (s.ok()) {
|
||||
handles->push_back(handle);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
s = Status::InvalidArgument("Column family not found", cf.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
for (auto cfd : *impl->versions_->GetColumnFamilySet()) {
|
||||
impl->InstallSuperVersionAndScheduleWork(
|
||||
cfd, &sv_context, *cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
sv_context.Clean();
|
||||
if (impl->two_write_queues_) {
|
||||
impl->log_write_mutex_.Lock();
|
||||
}
|
||||
impl->alive_log_files_.push_back(
|
||||
DBImpl::LogFileNumberSize(impl->logfile_number_));
|
||||
impl->alive_log_files_tail_ = impl->alive_log_files_.rbegin();
|
||||
if (impl->two_write_queues_) {
|
||||
impl->log_write_mutex_.Unlock();
|
||||
}
|
||||
@@ -1839,16 +1816,11 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
WriteOptions write_options;
|
||||
uint64_t log_used, log_size;
|
||||
log::Writer* log_writer = impl->logs_.back().writer;
|
||||
LogFileNumberSize& log_file_number_size = impl->alive_log_files_.back();
|
||||
|
||||
assert(log_writer->get_log_number() == log_file_number_size.number);
|
||||
impl->mutex_.AssertHeld();
|
||||
s = impl->WriteToWAL(empty_batch, log_writer, &log_used, &log_size,
|
||||
Env::IO_TOTAL, log_file_number_size);
|
||||
Env::IO_TOTAL, /*with_db_mutex==*/true);
|
||||
if (s.ok()) {
|
||||
// Need to fsync, otherwise it might get lost after a power reset.
|
||||
s = impl->FlushWAL(false);
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::Open::BeforeSyncWAL", /*arg=*/&s);
|
||||
if (s.ok()) {
|
||||
s = log_writer->file()->Sync(impl->immutable_db_options_.use_fsync);
|
||||
}
|
||||
@@ -1856,53 +1828,6 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = impl->LogAndApplyForRecovery(recovery_ctx);
|
||||
}
|
||||
|
||||
if (s.ok() && impl->immutable_db_options_.persist_stats_to_disk) {
|
||||
impl->mutex_.AssertHeld();
|
||||
s = impl->InitPersistStatsColumnFamily();
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
// set column family handles
|
||||
for (auto cf : column_families) {
|
||||
auto cfd =
|
||||
impl->versions_->GetColumnFamilySet()->GetColumnFamily(cf.name);
|
||||
if (cfd != nullptr) {
|
||||
handles->push_back(
|
||||
new ColumnFamilyHandleImpl(cfd, impl, &impl->mutex_));
|
||||
impl->NewThreadStatusCfInfo(cfd);
|
||||
} else {
|
||||
if (db_options.create_missing_column_families) {
|
||||
// missing column family, create it
|
||||
ColumnFamilyHandle* handle = nullptr;
|
||||
impl->mutex_.Unlock();
|
||||
s = impl->CreateColumnFamily(cf.options, cf.name, &handle);
|
||||
impl->mutex_.Lock();
|
||||
if (s.ok()) {
|
||||
handles->push_back(handle);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
s = Status::InvalidArgument("Column family not found", cf.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
for (auto cfd : *impl->versions_->GetColumnFamilySet()) {
|
||||
impl->InstallSuperVersionAndScheduleWork(
|
||||
cfd, &sv_context, *cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
sv_context.Clean();
|
||||
}
|
||||
|
||||
if (s.ok() && impl->immutable_db_options_.persist_stats_to_disk) {
|
||||
// try to read format version
|
||||
s = impl->PersistentStatsProcessFormatVersion();
|
||||
@@ -1973,24 +1898,21 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
// vast majority of all files), we'll pass the size to SstFileManager.
|
||||
// For all other files SstFileManager will query the size from filesystem.
|
||||
|
||||
std::vector<ColumnFamilyMetaData> metadata;
|
||||
impl->GetAllColumnFamilyMetaData(&metadata);
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
|
||||
// TODO: Once GetLiveFilesMetaData supports blob files, update the logic
|
||||
// below to get known_file_sizes for blob files.
|
||||
impl->mutex_.Lock();
|
||||
impl->versions_->GetLiveFilesMetaData(&metadata);
|
||||
impl->mutex_.Unlock();
|
||||
|
||||
std::unordered_map<std::string, uint64_t> known_file_sizes;
|
||||
for (const auto& md : metadata) {
|
||||
for (const auto& lmd : md.levels) {
|
||||
for (const auto& fmd : lmd.files) {
|
||||
known_file_sizes[fmd.relative_filename] = fmd.size;
|
||||
}
|
||||
}
|
||||
for (const auto& bmd : md.blob_files) {
|
||||
std::string name = bmd.blob_file_name;
|
||||
// The BlobMetaData.blob_file_name may start with "/".
|
||||
if (!name.empty() && name[0] == '/') {
|
||||
name = name.substr(1);
|
||||
}
|
||||
known_file_sizes[name] = bmd.blob_file_size;
|
||||
std::string name = md.name;
|
||||
if (!name.empty() && name[0] == '/') {
|
||||
name = name.substr(1);
|
||||
}
|
||||
known_file_sizes[name] = md.size;
|
||||
}
|
||||
|
||||
std::vector<std::string> paths;
|
||||
|
||||
@@ -33,45 +33,20 @@ DBImplReadOnly::~DBImplReadOnly() {}
|
||||
Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* pinnable_val) {
|
||||
return Get(read_options, column_family, key, pinnable_val,
|
||||
/*timestamp*/ nullptr);
|
||||
}
|
||||
|
||||
Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* pinnable_val,
|
||||
std::string* timestamp) {
|
||||
assert(pinnable_val != nullptr);
|
||||
// TODO: stopwatch DB_GET needed?, perf timer needed?
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
assert(column_family);
|
||||
if (read_options.timestamp) {
|
||||
const Status s = FailIfTsMismatchCf(
|
||||
column_family, *(read_options.timestamp), /*ts_for_read=*/true);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
const Status s = FailIfCfHasTs(column_family);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the timestamps for returning results so that we can distinguish
|
||||
// between tombstone or key that has never been written
|
||||
if (timestamp) {
|
||||
timestamp->clear();
|
||||
}
|
||||
|
||||
const Comparator* ucmp = column_family->GetComparator();
|
||||
assert(ucmp);
|
||||
std::string* ts = ucmp->timestamp_size() > 0 ? timestamp : nullptr;
|
||||
if (ucmp->timestamp_size() || read_options.timestamp) {
|
||||
// TODO: support timestamp
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
Status s;
|
||||
SequenceNumber snapshot = versions_->LastSequence();
|
||||
GetWithTimestampReadCallback read_cb(snapshot);
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
if (tracer_) {
|
||||
@@ -83,23 +58,19 @@ Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
SuperVersion* super_version = cfd->GetSuperVersion();
|
||||
MergeContext merge_context;
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
LookupKey lkey(key, snapshot, read_options.timestamp);
|
||||
LookupKey lkey(key, snapshot);
|
||||
PERF_TIMER_STOP(get_snapshot_time);
|
||||
if (super_version->mem->Get(lkey, pinnable_val->GetSelf(), ts, &s,
|
||||
&merge_context, &max_covering_tombstone_seq,
|
||||
read_options, &read_cb)) {
|
||||
if (super_version->mem->Get(lkey, pinnable_val->GetSelf(),
|
||||
/*timestamp=*/nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options)) {
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
PinnedIteratorsManager pinned_iters_mgr;
|
||||
super_version->current->Get(
|
||||
read_options, lkey, pinnable_val, ts, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, &pinned_iters_mgr,
|
||||
/*value_found*/ nullptr,
|
||||
/*key_exists*/ nullptr, /*seq*/ nullptr, &read_cb,
|
||||
/*is_blob*/ nullptr,
|
||||
/*do_merge*/ true);
|
||||
super_version->current->Get(read_options, lkey, pinnable_val,
|
||||
/*timestamp=*/nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, &pinned_iters_mgr);
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
RecordTick(stats_, NUMBER_KEYS_READ);
|
||||
@@ -113,17 +84,11 @@ Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family) {
|
||||
assert(column_family);
|
||||
if (read_options.timestamp) {
|
||||
const Status s = FailIfTsMismatchCf(
|
||||
column_family, *(read_options.timestamp), /*ts_for_read=*/true);
|
||||
if (!s.ok()) {
|
||||
return NewErrorIterator(s);
|
||||
}
|
||||
} else {
|
||||
const Status s = FailIfCfHasTs(column_family);
|
||||
if (!s.ok()) {
|
||||
return NewErrorIterator(s);
|
||||
}
|
||||
const Comparator* ucmp = column_family->GetComparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() || read_options.timestamp) {
|
||||
// TODO: support timestamp
|
||||
return NewErrorIterator(Status::NotSupported());
|
||||
}
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
@@ -153,20 +118,16 @@ Status DBImplReadOnly::NewIterators(
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators) {
|
||||
if (read_options.timestamp) {
|
||||
for (auto* cf : column_families) {
|
||||
assert(cf);
|
||||
const Status s = FailIfTsMismatchCf(cf, *(read_options.timestamp),
|
||||
/*ts_for_read=*/true);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
// TODO: support timestamp
|
||||
return Status::NotSupported();
|
||||
} else {
|
||||
for (auto* cf : column_families) {
|
||||
assert(cf);
|
||||
const Status s = FailIfCfHasTs(cf);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
const Comparator* ucmp = cf->GetComparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size()) {
|
||||
// TODO: support timestamp
|
||||
return Status::NotSupported();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,6 @@ class DBImplReadOnly : public DBImpl {
|
||||
virtual Status Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) override;
|
||||
Status Get(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* value,
|
||||
std::string* timestamp) override;
|
||||
|
||||
// TODO: Implement ReadOnly MultiGet?
|
||||
|
||||
|
||||
@@ -33,8 +33,7 @@ DBImplSecondary::~DBImplSecondary() {}
|
||||
Status DBImplSecondary::Recover(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool /*readonly*/, bool /*error_if_wal_file_exists*/,
|
||||
bool /*error_if_data_exists_in_wals*/, uint64_t*,
|
||||
RecoveryContext* /*recovery_ctx*/) {
|
||||
bool /*error_if_data_exists_in_wals*/, uint64_t*) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
JobContext job_context(0);
|
||||
@@ -62,6 +61,8 @@ Status DBImplSecondary::Recover(
|
||||
default_cf_handle_ = new ColumnFamilyHandleImpl(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), this, &mutex_);
|
||||
default_cf_internal_stats_ = default_cf_handle_->cfd()->internal_stats();
|
||||
single_column_family_mode_ =
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() == 1;
|
||||
|
||||
std::unordered_set<ColumnFamilyData*> cfds_changed;
|
||||
s = FindAndRecoverLogFiles(&cfds_changed, &job_context);
|
||||
@@ -246,16 +247,15 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
if (seq_of_batch <= seq) {
|
||||
continue;
|
||||
}
|
||||
auto curr_log_num = std::numeric_limits<uint64_t>::max();
|
||||
auto curr_log_num = port::kMaxUint64;
|
||||
if (cfd_to_current_log_.count(cfd) > 0) {
|
||||
curr_log_num = cfd_to_current_log_[cfd];
|
||||
}
|
||||
// If the active memtable contains records added by replaying an
|
||||
// earlier WAL, then we need to seal the memtable, add it to the
|
||||
// immutable memtable list and create a new active memtable.
|
||||
if (!cfd->mem()->IsEmpty() &&
|
||||
(curr_log_num == std::numeric_limits<uint64_t>::max() ||
|
||||
curr_log_num != log_number)) {
|
||||
if (!cfd->mem()->IsEmpty() && (curr_log_num == port::kMaxUint64 ||
|
||||
curr_log_num != log_number)) {
|
||||
const MutableCFOptions mutable_cf_options =
|
||||
*cfd->GetLatestMutableCFOptions();
|
||||
MemTable* new_mem =
|
||||
@@ -328,43 +328,23 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
Status DBImplSecondary::Get(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) {
|
||||
return GetImpl(read_options, column_family, key, value,
|
||||
/*timestamp*/ nullptr);
|
||||
}
|
||||
|
||||
Status DBImplSecondary::Get(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value, std::string* timestamp) {
|
||||
return GetImpl(read_options, column_family, key, value, timestamp);
|
||||
return GetImpl(read_options, column_family, key, value);
|
||||
}
|
||||
|
||||
Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* pinnable_val,
|
||||
std::string* timestamp) {
|
||||
const Slice& key, PinnableSlice* pinnable_val) {
|
||||
assert(pinnable_val != nullptr);
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, immutable_db_options_.clock);
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, DB_GET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
assert(column_family);
|
||||
if (read_options.timestamp) {
|
||||
const Status s = FailIfTsMismatchCf(
|
||||
column_family, *(read_options.timestamp), /*ts_for_read=*/true);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
const Status s = FailIfCfHasTs(column_family);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the timestamp for returning results so that we can distinguish
|
||||
// between tombstone or key that has never been written later.
|
||||
if (timestamp) {
|
||||
timestamp->clear();
|
||||
const Comparator* ucmp = column_family->GetComparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() || read_options.timestamp) {
|
||||
// TODO: support timestamp
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
@@ -378,27 +358,23 @@ Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
|
||||
// Acquire SuperVersion
|
||||
SuperVersion* super_version = GetAndRefSuperVersion(cfd);
|
||||
SequenceNumber snapshot = versions_->LastSequence();
|
||||
GetWithTimestampReadCallback read_cb(snapshot);
|
||||
MergeContext merge_context;
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
Status s;
|
||||
LookupKey lkey(key, snapshot, read_options.timestamp);
|
||||
LookupKey lkey(key, snapshot);
|
||||
PERF_TIMER_STOP(get_snapshot_time);
|
||||
|
||||
bool done = false;
|
||||
const Comparator* ucmp = column_family->GetComparator();
|
||||
assert(ucmp);
|
||||
std::string* ts = ucmp->timestamp_size() > 0 ? timestamp : nullptr;
|
||||
if (super_version->mem->Get(lkey, pinnable_val->GetSelf(), ts, &s,
|
||||
&merge_context, &max_covering_tombstone_seq,
|
||||
read_options, &read_cb)) {
|
||||
if (super_version->mem->Get(lkey, pinnable_val->GetSelf(),
|
||||
/*timestamp=*/nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options)) {
|
||||
done = true;
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if ((s.ok() || s.IsMergeInProgress()) &&
|
||||
super_version->imm->Get(
|
||||
lkey, pinnable_val->GetSelf(), ts, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options, &read_cb)) {
|
||||
lkey, pinnable_val->GetSelf(), /*timestamp=*/nullptr, &s,
|
||||
&merge_context, &max_covering_tombstone_seq, read_options)) {
|
||||
done = true;
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
@@ -410,11 +386,9 @@ Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
|
||||
if (!done) {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
PinnedIteratorsManager pinned_iters_mgr;
|
||||
super_version->current->Get(
|
||||
read_options, lkey, pinnable_val, ts, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, &pinned_iters_mgr, /*value_found*/ nullptr,
|
||||
/*key_exists*/ nullptr, /*seq*/ nullptr, &read_cb, /*is_blob*/ nullptr,
|
||||
/*do_merge*/ true);
|
||||
super_version->current->Get(read_options, lkey, pinnable_val,
|
||||
/*timestamp=*/nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, &pinned_iters_mgr);
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
{
|
||||
@@ -441,17 +415,11 @@ Iterator* DBImplSecondary::NewIterator(const ReadOptions& read_options,
|
||||
}
|
||||
|
||||
assert(column_family);
|
||||
if (read_options.timestamp) {
|
||||
const Status s = FailIfTsMismatchCf(
|
||||
column_family, *(read_options.timestamp), /*ts_for_read=*/true);
|
||||
if (!s.ok()) {
|
||||
return NewErrorIterator(s);
|
||||
}
|
||||
} else {
|
||||
const Status s = FailIfCfHasTs(column_family);
|
||||
if (!s.ok()) {
|
||||
return NewErrorIterator(s);
|
||||
}
|
||||
const Comparator* ucmp = column_family->GetComparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() || read_options.timestamp) {
|
||||
// TODO: support timestamp
|
||||
return NewErrorIterator(Status::NotSupported());
|
||||
}
|
||||
|
||||
Iterator* result = nullptr;
|
||||
@@ -510,22 +478,17 @@ Status DBImplSecondary::NewIterators(
|
||||
if (iterators == nullptr) {
|
||||
return Status::InvalidArgument("iterators not allowed to be nullptr");
|
||||
}
|
||||
|
||||
if (read_options.timestamp) {
|
||||
for (auto* cf : column_families) {
|
||||
assert(cf);
|
||||
const Status s = FailIfTsMismatchCf(cf, *(read_options.timestamp),
|
||||
/*ts_for_read=*/true);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
// TODO: support timestamp
|
||||
return Status::NotSupported();
|
||||
} else {
|
||||
for (auto* cf : column_families) {
|
||||
assert(cf);
|
||||
const Status s = FailIfCfHasTs(cf);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
const Comparator* ucmp = cf->GetComparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size()) {
|
||||
// TODO: support timestamp
|
||||
return Status::NotSupported();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -538,7 +501,7 @@ Status DBImplSecondary::NewIterators(
|
||||
// TODO (yanqin) support snapshot.
|
||||
return Status::NotSupported("snapshot not supported in secondary mode");
|
||||
} else {
|
||||
SequenceNumber read_seq(kMaxSequenceNumber);
|
||||
SequenceNumber read_seq = versions_->LastSequence();
|
||||
for (auto cfh : column_families) {
|
||||
ColumnFamilyData* cfd = static_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||
iterators->push_back(
|
||||
@@ -695,7 +658,6 @@ Status DB::OpenAsSecondary(
|
||||
s = CreateLoggerFromOptions(secondary_path, tmp_opts, &tmp_opts.info_log);
|
||||
if (!s.ok()) {
|
||||
tmp_opts.info_log = nullptr;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -809,19 +771,12 @@ Status DBImplSecondary::CompactWithoutInstallation(
|
||||
|
||||
const int job_id = next_job_id_.fetch_add(1);
|
||||
|
||||
// use primary host's db_id for running the compaction, but db_session_id is
|
||||
// using the local one, which is to make sure the unique id is unique from
|
||||
// the remote compactors. Because the id is generated from db_id,
|
||||
// db_session_id and orig_file_number, unlike the local compaction, remote
|
||||
// compaction cannot guarantee the uniqueness of orig_file_number, the file
|
||||
// number is only assigned when compaction is done.
|
||||
CompactionServiceCompactionJob compaction_job(
|
||||
job_id, c.get(), immutable_db_options_, mutable_db_options_,
|
||||
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_,
|
||||
options.canceled ? *options.canceled : kManualCompactionCanceledFalse_,
|
||||
input.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();
|
||||
@@ -876,8 +831,6 @@ Status DB::OpenAndCompact(
|
||||
override_options.table_factory;
|
||||
compaction_input.column_family.options.sst_partitioner_factory =
|
||||
override_options.sst_partitioner_factory;
|
||||
compaction_input.column_family.options.table_properties_collector_factories =
|
||||
override_options.table_properties_collector_factories;
|
||||
compaction_input.db_options.listeners = override_options.listeners;
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
|
||||
@@ -81,21 +81,16 @@ class DBImplSecondary : public DBImpl {
|
||||
// and log_readers_ to facilitate future operations.
|
||||
Status Recover(const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool read_only, bool error_if_wal_file_exists,
|
||||
bool error_if_data_exists_in_wals, uint64_t* = nullptr,
|
||||
RecoveryContext* recovery_ctx = nullptr) override;
|
||||
bool error_if_data_exists_in_wals,
|
||||
uint64_t* = nullptr) override;
|
||||
|
||||
// Implementations of the DB interface
|
||||
using DB::Get;
|
||||
Status Get(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* value) override;
|
||||
|
||||
Status Get(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* value,
|
||||
std::string* timestamp) override;
|
||||
|
||||
Status GetImpl(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* value,
|
||||
std::string* timestamp);
|
||||
const Slice& key, PinnableSlice* value);
|
||||
|
||||
using DBImpl::NewIterator;
|
||||
Iterator* NewIterator(const ReadOptions&,
|
||||
|
||||
+79
-174
@@ -30,7 +30,7 @@ Status DBImpl::Put(const WriteOptions& o, ColumnFamilyHandle* column_family,
|
||||
|
||||
Status DBImpl::Put(const WriteOptions& o, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, const Slice& ts, const Slice& val) {
|
||||
const Status s = FailIfTsMismatchCf(column_family, ts, /*ts_for_read=*/false);
|
||||
const Status s = FailIfTsSizesMismatch(column_family, ts);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -63,7 +63,7 @@ Status DBImpl::Delete(const WriteOptions& write_options,
|
||||
Status DBImpl::Delete(const WriteOptions& write_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& ts) {
|
||||
const Status s = FailIfTsMismatchCf(column_family, ts, /*ts_for_read=*/false);
|
||||
const Status s = FailIfTsSizesMismatch(column_family, ts);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ Status DBImpl::SingleDelete(const WriteOptions& write_options,
|
||||
Status DBImpl::SingleDelete(const WriteOptions& write_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& ts) {
|
||||
const Status s = FailIfTsMismatchCf(column_family, ts, /*ts_for_read=*/false);
|
||||
const Status s = FailIfTsSizesMismatch(column_family, ts);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -106,31 +106,15 @@ void DBImpl::SetRecoverableStatePreReleaseCallback(
|
||||
}
|
||||
|
||||
Status DBImpl::Write(const WriteOptions& write_options, WriteBatch* my_batch) {
|
||||
Status s;
|
||||
if (write_options.protection_bytes_per_key > 0) {
|
||||
s = WriteBatchInternal::UpdateProtectionInfo(
|
||||
my_batch, write_options.protection_bytes_per_key);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = WriteImpl(write_options, my_batch, /*callback=*/nullptr,
|
||||
/*log_used=*/nullptr);
|
||||
}
|
||||
return s;
|
||||
return WriteImpl(write_options, my_batch, /*callback=*/nullptr,
|
||||
/*log_used=*/nullptr);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status DBImpl::WriteWithCallback(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch,
|
||||
WriteCallback* callback) {
|
||||
Status s;
|
||||
if (write_options.protection_bytes_per_key > 0) {
|
||||
s = WriteBatchInternal::UpdateProtectionInfo(
|
||||
my_batch, write_options.protection_bytes_per_key);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = WriteImpl(write_options, my_batch, callback, nullptr);
|
||||
}
|
||||
return s;
|
||||
return WriteImpl(write_options, my_batch, callback, nullptr);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -142,13 +126,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
uint64_t* log_used, uint64_t log_ref,
|
||||
bool disable_memtable, uint64_t* seq_used,
|
||||
size_t batch_cnt,
|
||||
PreReleaseCallback* pre_release_callback,
|
||||
PostMemTableCallback* post_memtable_callback) {
|
||||
PreReleaseCallback* pre_release_callback) {
|
||||
assert(!seq_per_batch_ || batch_cnt != 0);
|
||||
assert(my_batch == nullptr || my_batch->Count() == 0 ||
|
||||
write_options.protection_bytes_per_key == 0 ||
|
||||
write_options.protection_bytes_per_key ==
|
||||
my_batch->GetProtectionBytesPerKey());
|
||||
if (my_batch == nullptr) {
|
||||
return Status::InvalidArgument("Batch is nullptr!");
|
||||
} else if (!disable_memtable &&
|
||||
@@ -176,10 +155,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
"rate-limiting automatic WAL flush, which requires "
|
||||
"`WriteOptions::disableWAL` and "
|
||||
"`DBOptions::manual_wal_flush` both set to false");
|
||||
} else if (write_options.protection_bytes_per_key != 0 &&
|
||||
write_options.protection_bytes_per_key != 8) {
|
||||
return Status::InvalidArgument(
|
||||
"`WriteOptions::protection_bytes_per_key` must be zero or eight");
|
||||
}
|
||||
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
|
||||
// grabs but does not seem thread-safe.
|
||||
@@ -210,15 +185,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
return Status::NotSupported(
|
||||
"pipelined_writes is not compatible with unordered_write");
|
||||
}
|
||||
if (immutable_db_options_.enable_pipelined_write &&
|
||||
post_memtable_callback != nullptr) {
|
||||
return Status::NotSupported(
|
||||
"pipelined write currently does not honor post_memtable_callback");
|
||||
}
|
||||
if (seq_per_batch_ && post_memtable_callback != nullptr) {
|
||||
return Status::NotSupported(
|
||||
"seq_per_batch currently does not honor post_memtable_callback");
|
||||
}
|
||||
// Otherwise IsLatestPersistentState optimization does not make sense
|
||||
assert(!WriteBatchInternal::IsLatestPersistentState(my_batch) ||
|
||||
disable_memtable);
|
||||
@@ -275,8 +241,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable, batch_cnt, pre_release_callback,
|
||||
post_memtable_callback);
|
||||
disable_memtable, batch_cnt, pre_release_callback);
|
||||
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
|
||||
|
||||
write_thread_.JoinBatchGroup(&w);
|
||||
@@ -303,16 +268,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// we're responsible for exit batch group
|
||||
// TODO(myabandeh): propagate status to write_group
|
||||
auto last_sequence = w.write_group->last_sequence;
|
||||
for (auto* tmp_w : *(w.write_group)) {
|
||||
assert(tmp_w);
|
||||
if (tmp_w->post_memtable_callback) {
|
||||
Status tmp_s =
|
||||
(*tmp_w->post_memtable_callback)(last_sequence, disable_memtable);
|
||||
// TODO: propagate the execution status of post_memtable_callback to
|
||||
// caller.
|
||||
assert(tmp_s.ok());
|
||||
}
|
||||
}
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
MemTableInsertStatusCheck(w.status);
|
||||
write_thread_.ExitAsBatchGroupFollower(&w);
|
||||
@@ -364,11 +319,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
}
|
||||
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
LogFileNumberSize& log_file_number_size = alive_log_files_.back();
|
||||
|
||||
assert(log_writer->get_log_number() == log_file_number_size.number);
|
||||
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -468,8 +419,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
io_s = WriteToWAL(write_group, log_writer, log_used, need_log_sync,
|
||||
need_log_dir_sync, last_sequence + 1,
|
||||
log_file_number_size);
|
||||
need_log_dir_sync, last_sequence + 1);
|
||||
}
|
||||
} else {
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
@@ -557,18 +507,15 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (!io_s.ok()) {
|
||||
// Check WriteToWAL status
|
||||
IOStatusCheck(io_s);
|
||||
}
|
||||
if (!w.CallbackFailed()) {
|
||||
if (!io_s.ok()) {
|
||||
assert(pre_release_cb_status.ok());
|
||||
IOStatusCheck(io_s);
|
||||
} else {
|
||||
WriteStatusCheck(pre_release_cb_status);
|
||||
}
|
||||
} else {
|
||||
assert(pre_release_cb_status.ok());
|
||||
assert(io_s.ok() && pre_release_cb_status.ok());
|
||||
}
|
||||
|
||||
if (need_log_sync) {
|
||||
@@ -598,16 +545,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
if (should_exit_batch_group) {
|
||||
if (status.ok()) {
|
||||
for (auto* tmp_w : write_group) {
|
||||
assert(tmp_w);
|
||||
if (tmp_w->post_memtable_callback) {
|
||||
Status tmp_s =
|
||||
(*tmp_w->post_memtable_callback)(last_sequence, disable_memtable);
|
||||
// TODO: propagate the execution status of post_memtable_callback to
|
||||
// caller.
|
||||
assert(tmp_s.ok());
|
||||
}
|
||||
}
|
||||
// Note: if we are to resume after non-OK statuses we need to revisit how
|
||||
// we reacts to non-OK statuses here.
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
@@ -649,10 +586,6 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
w.status = PreprocessWrite(write_options, &need_log_sync, &write_context);
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
LogFileNumberSize& log_file_number_size = alive_log_files_.back();
|
||||
|
||||
assert(log_writer->get_log_number() == log_file_number_size.number);
|
||||
|
||||
mutex_.Unlock();
|
||||
|
||||
// This can set non-OK status if callback fail.
|
||||
@@ -716,17 +649,17 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
wal_write_group.size - 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, wal_write_group.size - 1);
|
||||
}
|
||||
io_s =
|
||||
WriteToWAL(wal_write_group, log_writer, log_used, need_log_sync,
|
||||
need_log_dir_sync, current_sequence, log_file_number_size);
|
||||
io_s = WriteToWAL(wal_write_group, log_writer, log_used, need_log_sync,
|
||||
need_log_dir_sync, current_sequence);
|
||||
w.status = io_s;
|
||||
}
|
||||
|
||||
if (!io_s.ok()) {
|
||||
// Check WriteToWAL status
|
||||
IOStatusCheck(io_s);
|
||||
} else if (!w.CallbackFailed()) {
|
||||
WriteStatusCheck(w.status);
|
||||
if (!w.CallbackFailed()) {
|
||||
if (!io_s.ok()) {
|
||||
IOStatusCheck(io_s);
|
||||
} else {
|
||||
WriteStatusCheck(w.status);
|
||||
}
|
||||
}
|
||||
|
||||
if (need_log_sync) {
|
||||
@@ -962,18 +895,11 @@ Status DBImpl::WriteImplWALOnly(
|
||||
seq_inc = total_batch_cnt;
|
||||
}
|
||||
Status status;
|
||||
IOStatus io_s;
|
||||
io_s.PermitUncheckedError(); // Allow io_s to be uninitialized
|
||||
if (!write_options.disableWAL) {
|
||||
IOStatus io_s =
|
||||
ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
|
||||
io_s = ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
|
||||
status = io_s;
|
||||
// last_sequence may not be set if there is an error
|
||||
// This error checking and return is moved up to avoid using uninitialized
|
||||
// last_sequence.
|
||||
if (!io_s.ok()) {
|
||||
IOStatusCheck(io_s);
|
||||
write_thread->ExitAsBatchGroupLeader(write_group, status);
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
// Otherwise we inc seq number to do solely the seq allocation
|
||||
last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
|
||||
@@ -1008,7 +934,11 @@ Status DBImpl::WriteImplWALOnly(
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
WriteStatusCheck(status);
|
||||
if (!io_s.ok()) {
|
||||
IOStatusCheck(io_s);
|
||||
} else {
|
||||
WriteStatusCheck(status);
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
size_t index = 0;
|
||||
@@ -1111,18 +1041,12 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
|
||||
PERF_TIMER_GUARD(write_scheduling_flushes_compactions_time);
|
||||
|
||||
if (UNLIKELY(status.ok() && total_log_size_ > GetMaxTotalWalSize())) {
|
||||
assert(versions_);
|
||||
const ColumnFamilySet* const column_families =
|
||||
versions_->GetColumnFamilySet();
|
||||
assert(column_families);
|
||||
size_t num_cfs = column_families->NumberOfColumnFamilies();
|
||||
|
||||
assert(num_cfs >= 1);
|
||||
if (num_cfs > 1) {
|
||||
WaitForPendingWrites();
|
||||
status = SwitchWAL(write_context);
|
||||
}
|
||||
assert(!single_column_family_mode_ ||
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() == 1);
|
||||
if (UNLIKELY(status.ok() && !single_column_family_mode_ &&
|
||||
total_log_size_ > GetMaxTotalWalSize())) {
|
||||
WaitForPendingWrites();
|
||||
status = SwitchWAL(write_context);
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && write_buffer_manager_->ShouldFlush())) {
|
||||
@@ -1181,16 +1105,17 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
// Note: there does not seem to be a reason to wait for parallel sync at
|
||||
// this early step but it is not important since parallel sync (SyncWAL) and
|
||||
// need_log_sync are usually not used together.
|
||||
while (logs_.front().IsSyncing()) {
|
||||
while (logs_.front().getting_synced) {
|
||||
log_sync_cv_.Wait();
|
||||
}
|
||||
for (auto& log : logs_) {
|
||||
assert(!log.getting_synced);
|
||||
// This is just to prevent the logs to be synced by a parallel SyncWAL
|
||||
// call. We will do the actual syncing later after we will write to the
|
||||
// WAL.
|
||||
// Note: there does not seem to be a reason to set this early before we
|
||||
// actually write to the WAL
|
||||
log.PrepareForSync();
|
||||
log.getting_synced = true;
|
||||
}
|
||||
} else {
|
||||
*need_log_sync = false;
|
||||
@@ -1199,13 +1124,13 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
WriteBatch* tmp_batch, WriteBatch** merged_batch,
|
||||
size_t* write_with_wal,
|
||||
WriteBatch** to_be_cached_state) {
|
||||
WriteBatch* DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
WriteBatch* tmp_batch, size_t* write_with_wal,
|
||||
WriteBatch** to_be_cached_state) {
|
||||
assert(write_with_wal != nullptr);
|
||||
assert(tmp_batch != nullptr);
|
||||
assert(*to_be_cached_state == nullptr);
|
||||
WriteBatch* merged_batch = nullptr;
|
||||
*write_with_wal = 0;
|
||||
auto* leader = write_group.leader;
|
||||
assert(!leader->disable_wal); // Same holds for all in the batch group
|
||||
@@ -1214,24 +1139,22 @@ Status DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
// we simply write the first WriteBatch to WAL if the group only
|
||||
// contains one batch, that batch should be written to the WAL,
|
||||
// and the batch is not wanting to be truncated
|
||||
*merged_batch = leader->batch;
|
||||
if (WriteBatchInternal::IsLatestPersistentState(*merged_batch)) {
|
||||
*to_be_cached_state = *merged_batch;
|
||||
merged_batch = leader->batch;
|
||||
if (WriteBatchInternal::IsLatestPersistentState(merged_batch)) {
|
||||
*to_be_cached_state = merged_batch;
|
||||
}
|
||||
*write_with_wal = 1;
|
||||
} else {
|
||||
// WAL needs all of the batches flattened into a single batch.
|
||||
// We could avoid copying here with an iov-like AddRecord
|
||||
// interface
|
||||
*merged_batch = tmp_batch;
|
||||
merged_batch = tmp_batch;
|
||||
for (auto writer : write_group) {
|
||||
if (!writer->CallbackFailed()) {
|
||||
Status s = WriteBatchInternal::Append(*merged_batch, writer->batch,
|
||||
Status s = WriteBatchInternal::Append(merged_batch, writer->batch,
|
||||
/*WAL_only*/ true);
|
||||
if (!s.ok()) {
|
||||
tmp_batch->Clear();
|
||||
return s;
|
||||
}
|
||||
// Always returns Status::OK.
|
||||
assert(s.ok());
|
||||
if (WriteBatchInternal::IsLatestPersistentState(writer->batch)) {
|
||||
// We only need to cache the last of such write batch
|
||||
*to_be_cached_state = writer->batch;
|
||||
@@ -1240,8 +1163,7 @@ Status DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
}
|
||||
}
|
||||
}
|
||||
// return merged_batch;
|
||||
return Status::OK();
|
||||
return merged_batch;
|
||||
}
|
||||
|
||||
// When two_write_queues_ is disabled, this function is called from the only
|
||||
@@ -1250,15 +1172,18 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
uint64_t* log_size,
|
||||
Env::IOPriority rate_limiter_priority,
|
||||
LogFileNumberSize& log_file_number_size) {
|
||||
bool with_db_mutex, bool with_log_mutex) {
|
||||
assert(log_size != nullptr);
|
||||
|
||||
Slice log_entry = WriteBatchInternal::Contents(&merged_batch);
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::WriteToWAL:log_entry", &log_entry);
|
||||
auto s = merged_batch.VerifyChecksum();
|
||||
if (!s.ok()) {
|
||||
return status_to_io_status(std::move(s));
|
||||
// Assert mutex explicitly.
|
||||
if (with_db_mutex) {
|
||||
mutex_.AssertHeld();
|
||||
} else if (two_write_queues_) {
|
||||
log_write_mutex_.AssertHeld();
|
||||
assert(with_log_mutex);
|
||||
}
|
||||
|
||||
Slice log_entry = WriteBatchInternal::Contents(&merged_batch);
|
||||
*log_size = log_entry.size();
|
||||
// When two_write_queues_ WriteToWAL has to be protected from concurretn calls
|
||||
// from the two queues anyway and log_write_mutex_ is already held. Otherwise
|
||||
@@ -1280,7 +1205,12 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
*log_used = logfile_number_;
|
||||
}
|
||||
total_log_size_ += log_entry.size();
|
||||
log_file_number_size.AddSize(*log_size);
|
||||
if (with_db_mutex || with_log_mutex) {
|
||||
assert(alive_log_files_tail_ == alive_log_files_.rbegin());
|
||||
assert(alive_log_files_tail_ != alive_log_files_.rend());
|
||||
}
|
||||
LogFileNumberSize& last_alive_log = *alive_log_files_tail_;
|
||||
last_alive_log.AddSize(*log_size);
|
||||
log_empty_ = false;
|
||||
return io_s;
|
||||
}
|
||||
@@ -1288,21 +1218,15 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence,
|
||||
LogFileNumberSize& log_file_number_size) {
|
||||
SequenceNumber sequence) {
|
||||
IOStatus io_s;
|
||||
assert(!two_write_queues_);
|
||||
assert(!write_group.leader->disable_wal);
|
||||
// Same holds for all in the batch group
|
||||
size_t write_with_wal = 0;
|
||||
WriteBatch* to_be_cached_state = nullptr;
|
||||
WriteBatch* merged_batch;
|
||||
io_s = status_to_io_status(MergeBatch(write_group, &tmp_batch_, &merged_batch,
|
||||
&write_with_wal, &to_be_cached_state));
|
||||
if (UNLIKELY(!io_s.ok())) {
|
||||
return io_s;
|
||||
}
|
||||
|
||||
WriteBatch* merged_batch = MergeBatch(write_group, &tmp_batch_,
|
||||
&write_with_wal, &to_be_cached_state);
|
||||
if (merged_batch == write_group.leader->batch) {
|
||||
write_group.leader->log_used = logfile_number_;
|
||||
} else if (write_with_wal > 1) {
|
||||
@@ -1315,8 +1239,7 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
|
||||
uint64_t log_size;
|
||||
io_s = WriteToWAL(*merged_batch, log_writer, log_used, &log_size,
|
||||
write_group.leader->rate_limiter_priority,
|
||||
log_file_number_size);
|
||||
write_group.leader->rate_limiter_priority);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
@@ -1392,12 +1315,8 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
WriteBatch tmp_batch;
|
||||
size_t write_with_wal = 0;
|
||||
WriteBatch* to_be_cached_state = nullptr;
|
||||
WriteBatch* merged_batch;
|
||||
io_s = status_to_io_status(MergeBatch(write_group, &tmp_batch, &merged_batch,
|
||||
&write_with_wal, &to_be_cached_state));
|
||||
if (UNLIKELY(!io_s.ok())) {
|
||||
return io_s;
|
||||
}
|
||||
WriteBatch* merged_batch =
|
||||
MergeBatch(write_group, &tmp_batch, &write_with_wal, &to_be_cached_state);
|
||||
|
||||
// We need to lock log_write_mutex_ since logs_ and alive_log_files might be
|
||||
// pushed back concurrently
|
||||
@@ -1414,14 +1333,10 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
WriteBatchInternal::SetSequence(merged_batch, sequence);
|
||||
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
LogFileNumberSize& log_file_number_size = alive_log_files_.back();
|
||||
|
||||
assert(log_writer->get_log_number() == log_file_number_size.number);
|
||||
|
||||
uint64_t log_size;
|
||||
io_s = WriteToWAL(*merged_batch, log_writer, log_used, &log_size,
|
||||
write_group.leader->rate_limiter_priority,
|
||||
log_file_number_size);
|
||||
/*with_db_mutex=*/false, /*with_log_mutex=*/true);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
@@ -2077,6 +1992,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
log_dir_synced_ = false;
|
||||
logs_.emplace_back(logfile_number_, new_log);
|
||||
alive_log_files_.push_back(LogFileNumberSize(logfile_number_));
|
||||
alive_log_files_tail_ = alive_log_files_.rbegin();
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
@@ -2211,8 +2127,7 @@ Status DB::Put(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
// Pre-allocate size of write batch conservatively.
|
||||
// 8 bytes are taken by header, 4 bytes for count, 1 byte for type,
|
||||
// and we allocate 11 extra bytes for key length, as well as value length.
|
||||
WriteBatch batch(key.size() + value.size() + 24, 0 /* max_bytes */,
|
||||
opt.protection_bytes_per_key, 0 /* default_cf_ts_sz */);
|
||||
WriteBatch batch(key.size() + value.size() + 24);
|
||||
Status s = batch.Put(column_family, key, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -2226,9 +2141,7 @@ Status DB::Put(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
assert(default_cf);
|
||||
const Comparator* const default_cf_ucmp = default_cf->GetComparator();
|
||||
assert(default_cf_ucmp);
|
||||
WriteBatch batch(0 /* reserved_bytes */, 0 /* max_bytes */,
|
||||
opt.protection_bytes_per_key,
|
||||
default_cf_ucmp->timestamp_size());
|
||||
WriteBatch batch(0, 0, 0, default_cf_ucmp->timestamp_size());
|
||||
Status s = batch.Put(column_family, key, ts, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -2238,8 +2151,7 @@ Status DB::Put(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
|
||||
Status DB::Delete(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
const Slice& key) {
|
||||
WriteBatch batch(0 /* reserved_bytes */, 0 /* max_bytes */,
|
||||
opt.protection_bytes_per_key, 0 /* default_cf_ts_sz */);
|
||||
WriteBatch batch;
|
||||
Status s = batch.Delete(column_family, key);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -2253,9 +2165,7 @@ Status DB::Delete(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
assert(default_cf);
|
||||
const Comparator* const default_cf_ucmp = default_cf->GetComparator();
|
||||
assert(default_cf_ucmp);
|
||||
WriteBatch batch(0 /* reserved_bytes */, 0 /* max_bytes */,
|
||||
opt.protection_bytes_per_key,
|
||||
default_cf_ucmp->timestamp_size());
|
||||
WriteBatch batch(0, 0, 0, default_cf_ucmp->timestamp_size());
|
||||
Status s = batch.Delete(column_family, key, ts);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -2265,8 +2175,7 @@ Status DB::Delete(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
|
||||
Status DB::SingleDelete(const WriteOptions& opt,
|
||||
ColumnFamilyHandle* column_family, const Slice& key) {
|
||||
WriteBatch batch(0 /* reserved_bytes */, 0 /* max_bytes */,
|
||||
opt.protection_bytes_per_key, 0 /* default_cf_ts_sz */);
|
||||
WriteBatch batch;
|
||||
Status s = batch.SingleDelete(column_family, key);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -2281,9 +2190,7 @@ Status DB::SingleDelete(const WriteOptions& opt,
|
||||
assert(default_cf);
|
||||
const Comparator* const default_cf_ucmp = default_cf->GetComparator();
|
||||
assert(default_cf_ucmp);
|
||||
WriteBatch batch(0 /* reserved_bytes */, 0 /* max_bytes */,
|
||||
opt.protection_bytes_per_key,
|
||||
default_cf_ucmp->timestamp_size());
|
||||
WriteBatch batch(0, 0, 0, default_cf_ucmp->timestamp_size());
|
||||
Status s = batch.SingleDelete(column_family, key, ts);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -2294,8 +2201,7 @@ Status DB::SingleDelete(const WriteOptions& opt,
|
||||
Status DB::DeleteRange(const WriteOptions& opt,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice& begin_key, const Slice& end_key) {
|
||||
WriteBatch batch(0 /* reserved_bytes */, 0 /* max_bytes */,
|
||||
opt.protection_bytes_per_key, 0 /* default_cf_ts_sz */);
|
||||
WriteBatch batch;
|
||||
Status s = batch.DeleteRange(column_family, begin_key, end_key);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -2305,8 +2211,7 @@ Status DB::DeleteRange(const WriteOptions& opt,
|
||||
|
||||
Status DB::Merge(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, const Slice& value) {
|
||||
WriteBatch batch(0 /* reserved_bytes */, 0 /* max_bytes */,
|
||||
opt.protection_bytes_per_key, 0 /* default_cf_ts_sz */);
|
||||
WriteBatch batch;
|
||||
Status s = batch.Merge(column_family, key, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
|
||||
+18
-26
@@ -35,12 +35,10 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
Header(options.info_log, "DB SUMMARY\n");
|
||||
Header(options.info_log, "DB Session ID: %s\n", session_id.c_str());
|
||||
|
||||
Status s;
|
||||
// Get files in dbname dir
|
||||
s = env->GetChildren(dbname, &files);
|
||||
if (!s.ok()) {
|
||||
Error(options.info_log, "Error when reading %s dir %s\n", dbname.c_str(),
|
||||
s.ToString().c_str());
|
||||
if (!env->GetChildren(dbname, &files).ok()) {
|
||||
Error(options.info_log,
|
||||
"Error when reading %s dir\n", dbname.c_str());
|
||||
}
|
||||
std::sort(files.begin(), files.end());
|
||||
for (const std::string& file : files) {
|
||||
@@ -55,27 +53,24 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
Header(options.info_log, "IDENTITY file: %s\n", file.c_str());
|
||||
break;
|
||||
case kDescriptorFile:
|
||||
s = env->GetFileSize(dbname + "/" + file, &file_size);
|
||||
if (s.ok()) {
|
||||
if (env->GetFileSize(dbname + "/" + file, &file_size).ok()) {
|
||||
Header(options.info_log,
|
||||
"MANIFEST file: %s size: %" PRIu64 " Bytes\n", file.c_str(),
|
||||
file_size);
|
||||
} else {
|
||||
Error(options.info_log,
|
||||
"Error when reading MANIFEST file: %s/%s %s\n", dbname.c_str(),
|
||||
file.c_str(), s.ToString().c_str());
|
||||
Error(options.info_log, "Error when reading MANIFEST file: %s/%s\n",
|
||||
dbname.c_str(), file.c_str());
|
||||
}
|
||||
break;
|
||||
case kWalFile:
|
||||
s = env->GetFileSize(dbname + "/" + file, &file_size);
|
||||
if (s.ok()) {
|
||||
if (env->GetFileSize(dbname + "/" + file, &file_size).ok()) {
|
||||
wal_info.append(file)
|
||||
.append(" size: ")
|
||||
.append(std::to_string(file_size))
|
||||
.append(" ; ");
|
||||
} else {
|
||||
Error(options.info_log, "Error when reading LOG file: %s/%s %s\n",
|
||||
dbname.c_str(), file.c_str(), s.ToString().c_str());
|
||||
Error(options.info_log, "Error when reading LOG file: %s/%s\n",
|
||||
dbname.c_str(), file.c_str());
|
||||
}
|
||||
break;
|
||||
case kTableFile:
|
||||
@@ -91,10 +86,10 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
// Get sst files in db_path dir
|
||||
for (auto& db_path : options.db_paths) {
|
||||
if (dbname.compare(db_path.path) != 0) {
|
||||
s = env->GetChildren(db_path.path, &files);
|
||||
if (!s.ok()) {
|
||||
Error(options.info_log, "Error when reading %s dir %s\n",
|
||||
db_path.path.c_str(), s.ToString().c_str());
|
||||
if (!env->GetChildren(db_path.path, &files).ok()) {
|
||||
Error(options.info_log,
|
||||
"Error when reading %s dir\n",
|
||||
db_path.path.c_str());
|
||||
continue;
|
||||
}
|
||||
std::sort(files.begin(), files.end());
|
||||
@@ -116,25 +111,22 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
// Get wal file in wal_dir
|
||||
const auto& wal_dir = options.GetWalDir(dbname);
|
||||
if (!options.IsWalDirSameAsDBPath(dbname)) {
|
||||
s = env->GetChildren(wal_dir, &files);
|
||||
if (!s.ok()) {
|
||||
Error(options.info_log, "Error when reading %s dir %s\n", wal_dir.c_str(),
|
||||
s.ToString().c_str());
|
||||
if (!env->GetChildren(wal_dir, &files).ok()) {
|
||||
Error(options.info_log, "Error when reading %s dir\n", wal_dir.c_str());
|
||||
return;
|
||||
}
|
||||
wal_info.clear();
|
||||
for (const std::string& file : files) {
|
||||
if (ParseFileName(file, &number, &type)) {
|
||||
if (type == kWalFile) {
|
||||
s = env->GetFileSize(wal_dir + "/" + file, &file_size);
|
||||
if (s.ok()) {
|
||||
if (env->GetFileSize(wal_dir + "/" + file, &file_size).ok()) {
|
||||
wal_info.append(file)
|
||||
.append(" size: ")
|
||||
.append(std::to_string(file_size))
|
||||
.append(" ; ");
|
||||
} else {
|
||||
Error(options.info_log, "Error when reading LOG file %s/%s %s\n",
|
||||
wal_dir.c_str(), file.c_str(), s.ToString().c_str());
|
||||
Error(options.info_log, "Error when reading LOG file %s/%s\n",
|
||||
wal_dir.c_str(), file.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,7 +414,7 @@ TEST_F(DBIteratorStressTest, StressTest) {
|
||||
a /= 10;
|
||||
++len;
|
||||
}
|
||||
std::string s = std::to_string(rnd.Next() % static_cast<uint64_t>(max_key));
|
||||
std::string s = ToString(rnd.Next() % static_cast<uint64_t>(max_key));
|
||||
s.insert(0, len - (int)s.size(), '0');
|
||||
return s;
|
||||
};
|
||||
@@ -444,13 +444,12 @@ TEST_F(DBIteratorStressTest, StressTest) {
|
||||
for (double mutation_probability : {0.01, 0.5}) {
|
||||
for (double target_hidden_fraction : {0.1, 0.5}) {
|
||||
std::string trace_str =
|
||||
"entries: " + std::to_string(num_entries) +
|
||||
", key_space: " + std::to_string(key_space) +
|
||||
", error_probability: " + std::to_string(error_probability) +
|
||||
", mutation_probability: " +
|
||||
std::to_string(mutation_probability) +
|
||||
"entries: " + ToString(num_entries) +
|
||||
", key_space: " + ToString(key_space) +
|
||||
", error_probability: " + ToString(error_probability) +
|
||||
", mutation_probability: " + ToString(mutation_probability) +
|
||||
", target_hidden_fraction: " +
|
||||
std::to_string(target_hidden_fraction);
|
||||
ToString(target_hidden_fraction);
|
||||
SCOPED_TRACE(trace_str);
|
||||
if (trace) {
|
||||
std::cout << trace_str << std::endl;
|
||||
@@ -471,7 +470,7 @@ TEST_F(DBIteratorStressTest, StressTest) {
|
||||
types[rnd.Next() % (sizeof(types) / sizeof(types[0]))];
|
||||
}
|
||||
e.sequence = i;
|
||||
e.value = "v" + std::to_string(i);
|
||||
e.value = "v" + ToString(i);
|
||||
ParsedInternalKey internal_key(e.key, e.sequence, e.type);
|
||||
AppendInternalKey(&e.ikey, internal_key);
|
||||
|
||||
|
||||
+8
-8
@@ -766,7 +766,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->AddMerge("b", "merge_1");
|
||||
internal_iter->AddMerge("a", "merge_2");
|
||||
for (size_t k = 0; k < 200; ++k) {
|
||||
internal_iter->AddPut("c", std::to_string(k));
|
||||
internal_iter->AddPut("c", ToString(k));
|
||||
}
|
||||
internal_iter->Finish();
|
||||
|
||||
@@ -780,7 +780,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
ASSERT_EQ(db_iter->value().ToString(), std::to_string(i));
|
||||
ASSERT_EQ(db_iter->value().ToString(), ToString(i));
|
||||
db_iter->Prev();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -925,11 +925,11 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->AddMerge("b", "merge_1");
|
||||
internal_iter->AddMerge("a", "merge_2");
|
||||
for (size_t k = 0; k < 200; ++k) {
|
||||
internal_iter->AddPut("d", std::to_string(k));
|
||||
internal_iter->AddPut("d", ToString(k));
|
||||
}
|
||||
|
||||
for (size_t k = 0; k < 200; ++k) {
|
||||
internal_iter->AddPut("c", std::to_string(k));
|
||||
internal_iter->AddPut("c", ToString(k));
|
||||
}
|
||||
internal_iter->Finish();
|
||||
|
||||
@@ -942,7 +942,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
ASSERT_EQ(db_iter->key().ToString(), "d");
|
||||
ASSERT_EQ(db_iter->value().ToString(), std::to_string(i));
|
||||
ASSERT_EQ(db_iter->value().ToString(), ToString(i));
|
||||
db_iter->Prev();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -966,7 +966,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->AddMerge("b", "b");
|
||||
internal_iter->AddMerge("a", "a");
|
||||
for (size_t k = 0; k < 200; ++k) {
|
||||
internal_iter->AddMerge("c", std::to_string(k));
|
||||
internal_iter->AddMerge("c", ToString(k));
|
||||
}
|
||||
internal_iter->Finish();
|
||||
|
||||
@@ -981,7 +981,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
std::string merge_result = "0";
|
||||
for (size_t j = 1; j <= i; ++j) {
|
||||
merge_result += "," + std::to_string(j);
|
||||
merge_result += "," + ToString(j);
|
||||
}
|
||||
ASSERT_EQ(db_iter->value().ToString(), merge_result);
|
||||
|
||||
@@ -3156,7 +3156,7 @@ TEST_F(DBIteratorTest, ReverseToForwardWithDisappearingKeys) {
|
||||
internal_iter->AddPut("a", "A");
|
||||
internal_iter->AddPut("b", "B");
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
internal_iter->AddPut("c" + std::to_string(i), "");
|
||||
internal_iter->AddPut("c" + ToString(i), "");
|
||||
}
|
||||
internal_iter->Finish();
|
||||
|
||||
|
||||
@@ -111,12 +111,9 @@ TEST_P(DBIteratorTest, PersistedTierOnIterator) {
|
||||
TEST_P(DBIteratorTest, NonBlockingIteration) {
|
||||
do {
|
||||
ReadOptions non_blocking_opts, regular_opts;
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.full_block_cache = true;
|
||||
Options options = CurrentOptions(options_override);
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
non_blocking_opts.read_tier = kBlockCacheTier;
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
// write one kv to the database.
|
||||
ASSERT_OK(Put(1, "a", "b"));
|
||||
@@ -3160,7 +3157,7 @@ TEST_F(DBIteratorWithReadCallbackTest, ReadCallback) {
|
||||
uint64_t num_versions =
|
||||
CurrentOptions().max_sequential_skip_in_iterations + 10;
|
||||
for (uint64_t i = 0; i < num_versions; i++) {
|
||||
ASSERT_OK(Put("bar", std::to_string(i)));
|
||||
ASSERT_OK(Put("bar", ToString(i)));
|
||||
}
|
||||
SequenceNumber seq3 = db_->GetLatestSequenceNumber();
|
||||
TestReadCallback callback2(seq3);
|
||||
@@ -3189,7 +3186,7 @@ TEST_F(DBIteratorWithReadCallbackTest, ReadCallback) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("bar", iter->key());
|
||||
ASSERT_EQ(std::to_string(num_versions - 1), iter->value());
|
||||
ASSERT_EQ(ToString(num_versions - 1), iter->value());
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
+71
-471
@@ -15,6 +15,7 @@ enum class WriteBatchOpType {
|
||||
kSingleDelete,
|
||||
kDeleteRange,
|
||||
kMerge,
|
||||
kBlobIndex,
|
||||
kNum,
|
||||
};
|
||||
|
||||
@@ -24,87 +25,61 @@ WriteBatchOpType operator+(WriteBatchOpType lhs, const int rhs) {
|
||||
return static_cast<WriteBatchOpType>(static_cast<T>(lhs) + rhs);
|
||||
}
|
||||
|
||||
enum class WriteMode {
|
||||
// `Write()` a `WriteBatch` constructed with `protection_bytes_per_key > 0`.
|
||||
kWriteProtectedBatch = 0,
|
||||
// `Write()` a `WriteBatch` constructed with `protection_bytes_per_key == 0`.
|
||||
// Protection is enabled via `WriteOptions::protection_bytes_per_key > 0`.
|
||||
kWriteUnprotectedBatch,
|
||||
// TODO(ajkr): add a mode that uses `Write()` wrappers, e.g., `Put()`.
|
||||
kNum,
|
||||
};
|
||||
|
||||
// Integer addition is needed for `::testing::Range()` to take the enum type.
|
||||
WriteMode operator+(WriteMode lhs, const int rhs) {
|
||||
using T = std::underlying_type<WriteMode>::type;
|
||||
return static_cast<WriteMode>(static_cast<T>(lhs) + rhs);
|
||||
}
|
||||
|
||||
std::pair<WriteBatch, Status> GetWriteBatch(ColumnFamilyHandle* cf_handle,
|
||||
size_t protection_bytes_per_key,
|
||||
WriteBatchOpType op_type) {
|
||||
Status s;
|
||||
WriteBatch wb(0 /* reserved_bytes */, 0 /* max_bytes */,
|
||||
protection_bytes_per_key, 0 /* default_cf_ts_sz */);
|
||||
switch (op_type) {
|
||||
case WriteBatchOpType::kPut:
|
||||
s = wb.Put(cf_handle, "key", "val");
|
||||
break;
|
||||
case WriteBatchOpType::kDelete:
|
||||
s = wb.Delete(cf_handle, "key");
|
||||
break;
|
||||
case WriteBatchOpType::kSingleDelete:
|
||||
s = wb.SingleDelete(cf_handle, "key");
|
||||
break;
|
||||
case WriteBatchOpType::kDeleteRange:
|
||||
s = wb.DeleteRange(cf_handle, "begin", "end");
|
||||
break;
|
||||
case WriteBatchOpType::kMerge:
|
||||
s = wb.Merge(cf_handle, "key", "val");
|
||||
break;
|
||||
case WriteBatchOpType::kNum:
|
||||
assert(false);
|
||||
}
|
||||
return {std::move(wb), std::move(s)};
|
||||
}
|
||||
|
||||
class DbKvChecksumTest : public DBTestBase,
|
||||
public ::testing::WithParamInterface<
|
||||
std::tuple<WriteBatchOpType, char, WriteMode>> {
|
||||
class DbKvChecksumTest
|
||||
: public DBTestBase,
|
||||
public ::testing::WithParamInterface<std::tuple<WriteBatchOpType, char>> {
|
||||
public:
|
||||
DbKvChecksumTest()
|
||||
: DBTestBase("db_kv_checksum_test", /*env_do_fsync=*/false) {
|
||||
op_type_ = std::get<0>(GetParam());
|
||||
corrupt_byte_addend_ = std::get<1>(GetParam());
|
||||
write_mode_ = std::get<2>(GetParam());
|
||||
}
|
||||
|
||||
Status ExecuteWrite(ColumnFamilyHandle* cf_handle) {
|
||||
switch (write_mode_) {
|
||||
case WriteMode::kWriteProtectedBatch: {
|
||||
auto batch_and_status = GetWriteBatch(
|
||||
cf_handle, 8 /* protection_bytes_per_key */, op_type_);
|
||||
assert(batch_and_status.second.ok());
|
||||
return db_->Write(WriteOptions(), &batch_and_status.first);
|
||||
std::pair<WriteBatch, Status> GetWriteBatch(ColumnFamilyHandle* cf_handle) {
|
||||
Status s;
|
||||
WriteBatch wb(0 /* reserved_bytes */, 0 /* max_bytes */,
|
||||
8 /* protection_bytes_per_entry */, 0 /* default_cf_ts_sz */);
|
||||
switch (op_type_) {
|
||||
case WriteBatchOpType::kPut:
|
||||
s = wb.Put(cf_handle, "key", "val");
|
||||
break;
|
||||
case WriteBatchOpType::kDelete:
|
||||
s = wb.Delete(cf_handle, "key");
|
||||
break;
|
||||
case WriteBatchOpType::kSingleDelete:
|
||||
s = wb.SingleDelete(cf_handle, "key");
|
||||
break;
|
||||
case WriteBatchOpType::kDeleteRange:
|
||||
s = wb.DeleteRange(cf_handle, "begin", "end");
|
||||
break;
|
||||
case WriteBatchOpType::kMerge:
|
||||
s = wb.Merge(cf_handle, "key", "val");
|
||||
break;
|
||||
case WriteBatchOpType::kBlobIndex: {
|
||||
// TODO(ajkr): use public API once available.
|
||||
uint32_t cf_id;
|
||||
if (cf_handle == nullptr) {
|
||||
cf_id = 0;
|
||||
} else {
|
||||
cf_id = cf_handle->GetID();
|
||||
}
|
||||
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeInlinedTTL(&blob_index, /* expiration */ 9876543210,
|
||||
"val");
|
||||
|
||||
s = WriteBatchInternal::PutBlobIndex(&wb, cf_id, "key", blob_index);
|
||||
break;
|
||||
}
|
||||
case WriteMode::kWriteUnprotectedBatch: {
|
||||
auto batch_and_status = GetWriteBatch(
|
||||
cf_handle, 0 /* protection_bytes_per_key */, op_type_);
|
||||
assert(batch_and_status.second.ok());
|
||||
WriteOptions write_opts;
|
||||
write_opts.protection_bytes_per_key = 8;
|
||||
return db_->Write(write_opts, &batch_and_status.first);
|
||||
}
|
||||
case WriteMode::kNum:
|
||||
case WriteBatchOpType::kNum:
|
||||
assert(false);
|
||||
}
|
||||
return Status::NotSupported("WriteMode " +
|
||||
std::to_string(static_cast<int>(write_mode_)));
|
||||
return {std::move(wb), std::move(s)};
|
||||
}
|
||||
|
||||
void CorruptNextByteCallBack(void* arg) {
|
||||
Slice encoded = *static_cast<Slice*>(arg);
|
||||
if (entry_len_ == std::numeric_limits<size_t>::max()) {
|
||||
if (entry_len_ == port::kMaxSizet) {
|
||||
// We learn the entry size on the first attempt
|
||||
entry_len_ = encoded.size();
|
||||
}
|
||||
@@ -120,60 +95,46 @@ class DbKvChecksumTest : public DBTestBase,
|
||||
protected:
|
||||
WriteBatchOpType op_type_;
|
||||
char corrupt_byte_addend_;
|
||||
WriteMode write_mode_;
|
||||
size_t corrupt_byte_offset_ = 0;
|
||||
size_t entry_len_ = std::numeric_limits<size_t>::max();
|
||||
size_t entry_len_ = port::kMaxSizet;
|
||||
};
|
||||
|
||||
std::string GetOpTypeString(const WriteBatchOpType& op_type) {
|
||||
switch (op_type) {
|
||||
std::string GetTestNameSuffix(
|
||||
::testing::TestParamInfo<std::tuple<WriteBatchOpType, char>> info) {
|
||||
std::ostringstream oss;
|
||||
switch (std::get<0>(info.param)) {
|
||||
case WriteBatchOpType::kPut:
|
||||
return "Put";
|
||||
oss << "Put";
|
||||
break;
|
||||
case WriteBatchOpType::kDelete:
|
||||
return "Delete";
|
||||
oss << "Delete";
|
||||
break;
|
||||
case WriteBatchOpType::kSingleDelete:
|
||||
return "SingleDelete";
|
||||
oss << "SingleDelete";
|
||||
break;
|
||||
case WriteBatchOpType::kDeleteRange:
|
||||
return "DeleteRange";
|
||||
oss << "DeleteRange";
|
||||
break;
|
||||
case WriteBatchOpType::kMerge:
|
||||
return "Merge";
|
||||
oss << "Merge";
|
||||
break;
|
||||
case WriteBatchOpType::kBlobIndex:
|
||||
oss << "BlobIndex";
|
||||
break;
|
||||
case WriteBatchOpType::kNum:
|
||||
assert(false);
|
||||
}
|
||||
assert(false);
|
||||
return "";
|
||||
oss << "Add"
|
||||
<< static_cast<int>(static_cast<unsigned char>(std::get<1>(info.param)));
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DbKvChecksumTest, DbKvChecksumTest,
|
||||
::testing::Combine(::testing::Range(static_cast<WriteBatchOpType>(0),
|
||||
WriteBatchOpType::kNum),
|
||||
::testing::Values(2, 103, 251),
|
||||
::testing::Range(static_cast<WriteMode>(0),
|
||||
WriteMode::kNum)),
|
||||
[](const testing::TestParamInfo<
|
||||
std::tuple<WriteBatchOpType, char, WriteMode>>& args) {
|
||||
std::ostringstream oss;
|
||||
oss << GetOpTypeString(std::get<0>(args.param)) << "Add"
|
||||
<< static_cast<int>(
|
||||
static_cast<unsigned char>(std::get<1>(args.param)));
|
||||
switch (std::get<2>(args.param)) {
|
||||
case WriteMode::kWriteProtectedBatch:
|
||||
oss << "WriteProtectedBatch";
|
||||
break;
|
||||
case WriteMode::kWriteUnprotectedBatch:
|
||||
oss << "WriteUnprotectedBatch";
|
||||
break;
|
||||
case WriteMode::kNum:
|
||||
assert(false);
|
||||
}
|
||||
return oss.str();
|
||||
});
|
||||
|
||||
// TODO(ajkr): add a test that corrupts the `WriteBatch` contents. Such
|
||||
// corruptions should only be detectable in `WriteMode::kWriteProtectedBatch`.
|
||||
::testing::Values(2, 103, 251)),
|
||||
GetTestNameSuffix);
|
||||
|
||||
TEST_P(DbKvChecksumTest, MemTableAddCorrupted) {
|
||||
// This test repeatedly attempts to write `WriteBatch`es containing a single
|
||||
@@ -196,13 +157,11 @@ TEST_P(DbKvChecksumTest, MemTableAddCorrupted) {
|
||||
Reopen(options);
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_TRUE(ExecuteWrite(nullptr /* cf_handle */).IsCorruption());
|
||||
auto batch_and_status = GetWriteBatch(nullptr /* cf_handle */);
|
||||
ASSERT_OK(batch_and_status.second);
|
||||
ASSERT_TRUE(
|
||||
db_->Write(WriteOptions(), &batch_and_status.first).IsCorruption());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// In case the above callback is not invoked, this test will run
|
||||
// numeric_limits<size_t>::max() times until it reports an error (or will
|
||||
// exhaust disk space). Added this assert to report error early.
|
||||
ASSERT_TRUE(entry_len_ < std::numeric_limits<size_t>::max());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,373 +188,14 @@ TEST_P(DbKvChecksumTest, MemTableAddWithColumnFamilyCorrupted) {
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_TRUE(ExecuteWrite(handles_[1]).IsCorruption());
|
||||
auto batch_and_status = GetWriteBatch(handles_[1]);
|
||||
ASSERT_OK(batch_and_status.second);
|
||||
ASSERT_TRUE(
|
||||
db_->Write(WriteOptions(), &batch_and_status.first).IsCorruption());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// In case the above callback is not invoked, this test will run
|
||||
// numeric_limits<size_t>::max() times until it reports an error (or will
|
||||
// exhaust disk space). Added this assert to report error early.
|
||||
ASSERT_TRUE(entry_len_ < std::numeric_limits<size_t>::max());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DbKvChecksumTest, NoCorruptionCase) {
|
||||
// If this test fails, we may have found a piece of malfunctioned hardware
|
||||
auto batch_and_status =
|
||||
GetWriteBatch(nullptr, 8 /* protection_bytes_per_key */, op_type_);
|
||||
ASSERT_OK(batch_and_status.second);
|
||||
ASSERT_OK(batch_and_status.first.VerifyChecksum());
|
||||
}
|
||||
|
||||
TEST_P(DbKvChecksumTest, WriteToWALCorrupted) {
|
||||
// This test repeatedly attempts to write `WriteBatch`es containing a single
|
||||
// entry of type `op_type_`. Each attempt has one byte corrupted by adding
|
||||
// `corrupt_byte_addend_` to its original value. The test repeats until an
|
||||
// attempt has been made on each byte in the encoded write batch. All attempts
|
||||
// are expected to fail with `Status::Corruption`
|
||||
Options options = CurrentOptions();
|
||||
if (op_type_ == WriteBatchOpType::kMerge) {
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
}
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::WriteToWAL:log_entry",
|
||||
std::bind(&DbKvChecksumTest::CorruptNextByteCallBack, this,
|
||||
std::placeholders::_1));
|
||||
// First 8 bytes are for sequence number which is not protected in write batch
|
||||
corrupt_byte_offset_ = 8;
|
||||
|
||||
while (MoreBytesToCorrupt()) {
|
||||
// Corrupted write batch leads to read-only mode, so we have to
|
||||
// reopen for every attempt.
|
||||
Reopen(options);
|
||||
auto log_size_pre_write = dbfull()->TEST_total_log_size();
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_TRUE(ExecuteWrite(nullptr /* cf_handle */).IsCorruption());
|
||||
// Confirm that nothing was written to WAL
|
||||
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_total_log_size());
|
||||
ASSERT_TRUE(dbfull()->TEST_GetBGError().IsCorruption());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// In case the above callback is not invoked, this test will run
|
||||
// numeric_limits<size_t>::max() times until it reports an error (or will
|
||||
// exhaust disk space). Added this assert to report error early.
|
||||
ASSERT_TRUE(entry_len_ < std::numeric_limits<size_t>::max());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DbKvChecksumTest, WriteToWALWithColumnFamilyCorrupted) {
|
||||
// This test repeatedly attempts to write `WriteBatch`es containing a single
|
||||
// entry of type `op_type_`. Each attempt has one byte corrupted by adding
|
||||
// `corrupt_byte_addend_` to its original value. The test repeats until an
|
||||
// attempt has been made on each byte in the encoded write batch. All attempts
|
||||
// are expected to fail with `Status::Corruption`
|
||||
Options options = CurrentOptions();
|
||||
if (op_type_ == WriteBatchOpType::kMerge) {
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
}
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::WriteToWAL:log_entry",
|
||||
std::bind(&DbKvChecksumTest::CorruptNextByteCallBack, this,
|
||||
std::placeholders::_1));
|
||||
// First 8 bytes are for sequence number which is not protected in write batch
|
||||
corrupt_byte_offset_ = 8;
|
||||
|
||||
while (MoreBytesToCorrupt()) {
|
||||
// Corrupted write batch leads to read-only mode, so we have to
|
||||
// reopen for every attempt.
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
auto log_size_pre_write = dbfull()->TEST_total_log_size();
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_TRUE(ExecuteWrite(nullptr /* cf_handle */).IsCorruption());
|
||||
// Confirm that nothing was written to WAL
|
||||
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_total_log_size());
|
||||
ASSERT_TRUE(dbfull()->TEST_GetBGError().IsCorruption());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// In case the above callback is not invoked, this test will run
|
||||
// numeric_limits<size_t>::max() times until it reports an error (or will
|
||||
// exhaust disk space). Added this assert to report error early.
|
||||
ASSERT_TRUE(entry_len_ < std::numeric_limits<size_t>::max());
|
||||
}
|
||||
}
|
||||
|
||||
class DbKvChecksumTestMergedBatch
|
||||
: public DBTestBase,
|
||||
public ::testing::WithParamInterface<
|
||||
std::tuple<WriteBatchOpType, WriteBatchOpType, char>> {
|
||||
public:
|
||||
DbKvChecksumTestMergedBatch()
|
||||
: DBTestBase("db_kv_checksum_test", /*env_do_fsync=*/false) {
|
||||
op_type1_ = std::get<0>(GetParam());
|
||||
op_type2_ = std::get<1>(GetParam());
|
||||
corrupt_byte_addend_ = std::get<2>(GetParam());
|
||||
}
|
||||
|
||||
protected:
|
||||
WriteBatchOpType op_type1_;
|
||||
WriteBatchOpType op_type2_;
|
||||
char corrupt_byte_addend_;
|
||||
};
|
||||
|
||||
void CorruptWriteBatch(Slice* content, size_t offset,
|
||||
char corrupt_byte_addend) {
|
||||
ASSERT_TRUE(offset < content->size());
|
||||
char* buf = const_cast<char*>(content->data());
|
||||
buf[offset] += corrupt_byte_addend;
|
||||
}
|
||||
|
||||
TEST_P(DbKvChecksumTestMergedBatch, NoCorruptionCase) {
|
||||
// Veirfy write batch checksum after write batch append
|
||||
auto batch1 = GetWriteBatch(nullptr /* cf_handle */,
|
||||
8 /* protection_bytes_per_key */, op_type1_);
|
||||
ASSERT_OK(batch1.second);
|
||||
auto batch2 = GetWriteBatch(nullptr /* cf_handle */,
|
||||
8 /* protection_bytes_per_key */, op_type2_);
|
||||
ASSERT_OK(batch2.second);
|
||||
ASSERT_OK(WriteBatchInternal::Append(&batch1.first, &batch2.first));
|
||||
ASSERT_OK(batch1.first.VerifyChecksum());
|
||||
}
|
||||
|
||||
TEST_P(DbKvChecksumTestMergedBatch, WriteToWALCorrupted) {
|
||||
// This test has two writers repeatedly attempt to write `WriteBatch`es
|
||||
// containing a single entry of type op_type1_ and op_type2_ respectively. The
|
||||
// leader of the write group writes the batch containinng the entry of type
|
||||
// op_type1_. One byte of the pre-merged write batches is corrupted by adding
|
||||
// `corrupt_byte_addend_` to the batch's original value during each attempt.
|
||||
// The test repeats until an attempt has been made on each byte in both
|
||||
// pre-merged write batches. All attempts are expected to fail with
|
||||
// `Status::Corruption`.
|
||||
Options options = CurrentOptions();
|
||||
if (op_type1_ == WriteBatchOpType::kMerge ||
|
||||
op_type2_ == WriteBatchOpType::kMerge) {
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
}
|
||||
|
||||
auto leader_batch_and_status = GetWriteBatch(
|
||||
nullptr /* cf_handle */, 8 /* protection_bytes_per_key */, op_type1_);
|
||||
ASSERT_OK(leader_batch_and_status.second);
|
||||
auto follower_batch_and_status = GetWriteBatch(
|
||||
nullptr /* cf_handle */, 8 /* protection_bytes_per_key */, op_type2_);
|
||||
size_t leader_batch_size = leader_batch_and_status.first.GetDataSize();
|
||||
size_t total_bytes =
|
||||
leader_batch_size + follower_batch_and_status.first.GetDataSize();
|
||||
// First 8 bytes are for sequence number which is not protected in write batch
|
||||
size_t corrupt_byte_offset = 8;
|
||||
|
||||
std::atomic<bool> follower_joined{false};
|
||||
std::atomic<int> leader_count{0};
|
||||
port::Thread follower_thread;
|
||||
// This callback should only be called by the leader thread
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Wait2", [&](void* arg_leader) {
|
||||
auto* leader = reinterpret_cast<WriteThread::Writer*>(arg_leader);
|
||||
ASSERT_EQ(leader->state, WriteThread::STATE_GROUP_LEADER);
|
||||
|
||||
// This callback should only be called by the follower thread
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Wait", [&](void* arg_follower) {
|
||||
auto* follower =
|
||||
reinterpret_cast<WriteThread::Writer*>(arg_follower);
|
||||
// The leader thread will wait on this bool and hence wait until
|
||||
// this writer joins the write group
|
||||
ASSERT_NE(follower->state, WriteThread::STATE_GROUP_LEADER);
|
||||
if (corrupt_byte_offset >= leader_batch_size) {
|
||||
Slice batch_content = follower->batch->Data();
|
||||
CorruptWriteBatch(&batch_content,
|
||||
corrupt_byte_offset - leader_batch_size,
|
||||
corrupt_byte_addend_);
|
||||
}
|
||||
// Leader busy waits on this flag
|
||||
follower_joined = true;
|
||||
// So the follower does not enter the outer callback at
|
||||
// WriteThread::JoinBatchGroup:Wait2
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
});
|
||||
|
||||
// Start the other writer thread which will join the write group as
|
||||
// follower
|
||||
follower_thread = port::Thread([&]() {
|
||||
follower_batch_and_status =
|
||||
GetWriteBatch(nullptr /* cf_handle */,
|
||||
8 /* protection_bytes_per_key */, op_type2_);
|
||||
ASSERT_OK(follower_batch_and_status.second);
|
||||
ASSERT_TRUE(
|
||||
db_->Write(WriteOptions(), &follower_batch_and_status.first)
|
||||
.IsCorruption());
|
||||
});
|
||||
|
||||
ASSERT_EQ(leader->batch->GetDataSize(), leader_batch_size);
|
||||
if (corrupt_byte_offset < leader_batch_size) {
|
||||
Slice batch_content = leader->batch->Data();
|
||||
CorruptWriteBatch(&batch_content, corrupt_byte_offset,
|
||||
corrupt_byte_addend_);
|
||||
}
|
||||
leader_count++;
|
||||
while (!follower_joined) {
|
||||
// busy waiting
|
||||
}
|
||||
});
|
||||
while (corrupt_byte_offset < total_bytes) {
|
||||
// Reopen DB since it failed WAL write which lead to read-only mode
|
||||
Reopen(options);
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
auto log_size_pre_write = dbfull()->TEST_total_log_size();
|
||||
leader_batch_and_status = GetWriteBatch(
|
||||
nullptr /* cf_handle */, 8 /* protection_bytes_per_key */, op_type1_);
|
||||
ASSERT_OK(leader_batch_and_status.second);
|
||||
ASSERT_TRUE(db_->Write(WriteOptions(), &leader_batch_and_status.first)
|
||||
.IsCorruption());
|
||||
follower_thread.join();
|
||||
// Prevent leader thread from entering this callback
|
||||
SyncPoint::GetInstance()->ClearCallBack("WriteThread::JoinBatchGroup:Wait");
|
||||
ASSERT_EQ(1, leader_count);
|
||||
// Nothing should have been written to WAL
|
||||
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_total_log_size());
|
||||
ASSERT_TRUE(dbfull()->TEST_GetBGError().IsCorruption());
|
||||
|
||||
corrupt_byte_offset++;
|
||||
if (corrupt_byte_offset == leader_batch_size) {
|
||||
// skip over the sequence number part of follower's write batch
|
||||
corrupt_byte_offset += 8;
|
||||
}
|
||||
follower_joined = false;
|
||||
leader_count = 0;
|
||||
}
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_P(DbKvChecksumTestMergedBatch, WriteToWALWithColumnFamilyCorrupted) {
|
||||
// This test has two writers repeatedly attempt to write `WriteBatch`es
|
||||
// containing a single entry of type op_type1_ and op_type2_ respectively. The
|
||||
// leader of the write group writes the batch containinng the entry of type
|
||||
// op_type1_. One byte of the pre-merged write batches is corrupted by adding
|
||||
// `corrupt_byte_addend_` to the batch's original value during each attempt.
|
||||
// The test repeats until an attempt has been made on each byte in both
|
||||
// pre-merged write batches. All attempts are expected to fail with
|
||||
// `Status::Corruption`.
|
||||
Options options = CurrentOptions();
|
||||
if (op_type1_ == WriteBatchOpType::kMerge ||
|
||||
op_type2_ == WriteBatchOpType::kMerge) {
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
}
|
||||
CreateAndReopenWithCF({"ramen"}, options);
|
||||
|
||||
auto leader_batch_and_status =
|
||||
GetWriteBatch(handles_[1], 8 /* protection_bytes_per_key */, op_type1_);
|
||||
ASSERT_OK(leader_batch_and_status.second);
|
||||
auto follower_batch_and_status =
|
||||
GetWriteBatch(handles_[1], 8 /* protection_bytes_per_key */, op_type2_);
|
||||
size_t leader_batch_size = leader_batch_and_status.first.GetDataSize();
|
||||
size_t total_bytes =
|
||||
leader_batch_size + follower_batch_and_status.first.GetDataSize();
|
||||
// First 8 bytes are for sequence number which is not protected in write batch
|
||||
size_t corrupt_byte_offset = 8;
|
||||
|
||||
std::atomic<bool> follower_joined{false};
|
||||
std::atomic<int> leader_count{0};
|
||||
port::Thread follower_thread;
|
||||
// This callback should only be called by the leader thread
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Wait2", [&](void* arg_leader) {
|
||||
auto* leader = reinterpret_cast<WriteThread::Writer*>(arg_leader);
|
||||
ASSERT_EQ(leader->state, WriteThread::STATE_GROUP_LEADER);
|
||||
|
||||
// This callback should only be called by the follower thread
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Wait", [&](void* arg_follower) {
|
||||
auto* follower =
|
||||
reinterpret_cast<WriteThread::Writer*>(arg_follower);
|
||||
// The leader thread will wait on this bool and hence wait until
|
||||
// this writer joins the write group
|
||||
ASSERT_NE(follower->state, WriteThread::STATE_GROUP_LEADER);
|
||||
if (corrupt_byte_offset >= leader_batch_size) {
|
||||
Slice batch_content =
|
||||
WriteBatchInternal::Contents(follower->batch);
|
||||
CorruptWriteBatch(&batch_content,
|
||||
corrupt_byte_offset - leader_batch_size,
|
||||
corrupt_byte_addend_);
|
||||
}
|
||||
follower_joined = true;
|
||||
// So the follower does not enter the outer callback at
|
||||
// WriteThread::JoinBatchGroup:Wait2
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
});
|
||||
|
||||
// Start the other writer thread which will join the write group as
|
||||
// follower
|
||||
follower_thread = port::Thread([&]() {
|
||||
follower_batch_and_status = GetWriteBatch(
|
||||
handles_[1], 8 /* protection_bytes_per_key */, op_type2_);
|
||||
ASSERT_OK(follower_batch_and_status.second);
|
||||
ASSERT_TRUE(
|
||||
db_->Write(WriteOptions(), &follower_batch_and_status.first)
|
||||
.IsCorruption());
|
||||
});
|
||||
|
||||
ASSERT_EQ(leader->batch->GetDataSize(), leader_batch_size);
|
||||
if (corrupt_byte_offset < leader_batch_size) {
|
||||
Slice batch_content = WriteBatchInternal::Contents(leader->batch);
|
||||
CorruptWriteBatch(&batch_content, corrupt_byte_offset,
|
||||
corrupt_byte_addend_);
|
||||
}
|
||||
leader_count++;
|
||||
while (!follower_joined) {
|
||||
// busy waiting
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
while (corrupt_byte_offset < total_bytes) {
|
||||
// Reopen DB since it failed WAL write which lead to read-only mode
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "ramen"}, options);
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
auto log_size_pre_write = dbfull()->TEST_total_log_size();
|
||||
leader_batch_and_status =
|
||||
GetWriteBatch(handles_[1], 8 /* protection_bytes_per_key */, op_type1_);
|
||||
ASSERT_OK(leader_batch_and_status.second);
|
||||
ASSERT_TRUE(db_->Write(WriteOptions(), &leader_batch_and_status.first)
|
||||
.IsCorruption());
|
||||
follower_thread.join();
|
||||
// Prevent leader thread from entering this callback
|
||||
SyncPoint::GetInstance()->ClearCallBack("WriteThread::JoinBatchGroup:Wait");
|
||||
|
||||
ASSERT_EQ(1, leader_count);
|
||||
// Nothing should have been written to WAL
|
||||
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_total_log_size());
|
||||
ASSERT_TRUE(dbfull()->TEST_GetBGError().IsCorruption());
|
||||
|
||||
corrupt_byte_offset++;
|
||||
if (corrupt_byte_offset == leader_batch_size) {
|
||||
// skip over the sequence number part of follower's write batch
|
||||
corrupt_byte_offset += 8;
|
||||
}
|
||||
follower_joined = false;
|
||||
leader_count = 0;
|
||||
}
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DbKvChecksumTestMergedBatch, DbKvChecksumTestMergedBatch,
|
||||
::testing::Combine(::testing::Range(static_cast<WriteBatchOpType>(0),
|
||||
WriteBatchOpType::kNum),
|
||||
::testing::Range(static_cast<WriteBatchOpType>(0),
|
||||
WriteBatchOpType::kNum),
|
||||
::testing::Values(2, 103, 251)),
|
||||
[](const testing::TestParamInfo<
|
||||
std::tuple<WriteBatchOpType, WriteBatchOpType, char>>& args) {
|
||||
std::ostringstream oss;
|
||||
oss << GetOpTypeString(std::get<0>(args.param))
|
||||
<< GetOpTypeString(std::get<1>(args.param)) << "Add"
|
||||
<< static_cast<int>(
|
||||
static_cast<unsigned char>(std::get<2>(args.param)));
|
||||
return oss.str();
|
||||
});
|
||||
|
||||
// TODO: add test for transactions
|
||||
// TODO: add test for corrupted write batch with WAL disabled
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -187,7 +187,7 @@ TEST_F(DBTestXactLogIterator, TransactionLogIteratorCorruptedLog) {
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
ASSERT_OK(Put("key" + std::to_string(i), DummyString(10)));
|
||||
ASSERT_OK(Put("key" + ToString(i), DummyString(10)));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
@@ -263,20 +263,20 @@ TEST_F(DBTestXactLogIterator, TransactionLogIteratorBlobs) {
|
||||
struct Handler : public WriteBatch::Handler {
|
||||
std::string seen;
|
||||
Status PutCF(uint32_t cf, const Slice& key, const Slice& value) override {
|
||||
seen += "Put(" + std::to_string(cf) + ", " + key.ToString() + ", " +
|
||||
std::to_string(value.size()) + ")";
|
||||
seen += "Put(" + ToString(cf) + ", " + key.ToString() + ", " +
|
||||
ToString(value.size()) + ")";
|
||||
return Status::OK();
|
||||
}
|
||||
Status MergeCF(uint32_t cf, const Slice& key, const Slice& value) override {
|
||||
seen += "Merge(" + std::to_string(cf) + ", " + key.ToString() + ", " +
|
||||
std::to_string(value.size()) + ")";
|
||||
seen += "Merge(" + ToString(cf) + ", " + key.ToString() + ", " +
|
||||
ToString(value.size()) + ")";
|
||||
return Status::OK();
|
||||
}
|
||||
void LogData(const Slice& blob) override {
|
||||
seen += "LogData(" + blob.ToString() + ")";
|
||||
}
|
||||
Status DeleteCF(uint32_t cf, const Slice& key) override {
|
||||
seen += "Delete(" + std::to_string(cf) + ", " + key.ToString() + ")";
|
||||
seen += "Delete(" + ToString(cf) + ", " + key.ToString() + ")";
|
||||
return Status::OK();
|
||||
}
|
||||
} handler;
|
||||
|
||||
@@ -97,7 +97,7 @@ class MockMemTableRepFactory : public MemTableRepFactory {
|
||||
|
||||
private:
|
||||
MockMemTableRep* mock_rep_;
|
||||
// workaround since there's no std::numeric_limits<uint32_t>::max() yet.
|
||||
// workaround since there's no port::kMaxUint32 yet.
|
||||
uint32_t last_column_family_id_ = static_cast<uint32_t>(-1);
|
||||
};
|
||||
|
||||
@@ -171,7 +171,7 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
if (!insert_dup) {
|
||||
seq++;
|
||||
}
|
||||
Status s = mem->Add(seq, kTypeValue, "foo", "value" + std::to_string(seq),
|
||||
Status s = mem->Add(seq, kTypeValue, "foo", "value" + ToString(seq),
|
||||
nullptr /* kv_prot_info */);
|
||||
if (insert_dup) {
|
||||
ASSERT_TRUE(s.IsTryAgain());
|
||||
|
||||
+12
-29
@@ -95,22 +95,6 @@ TEST_F(DBOptionsTest, ImmutableTrackAndVerifyWalsInManifest) {
|
||||
ASSERT_FALSE(s.ok());
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, ImmutableVerifySstUniqueIdInManifest) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.verify_sst_unique_id_in_manifest = true;
|
||||
|
||||
ImmutableDBOptions db_options(options);
|
||||
ASSERT_TRUE(db_options.verify_sst_unique_id_in_manifest);
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_TRUE(dbfull()->GetDBOptions().verify_sst_unique_id_in_manifest);
|
||||
|
||||
Status s =
|
||||
dbfull()->SetDBOptions({{"verify_sst_unique_id_in_manifest", "false"}});
|
||||
ASSERT_FALSE(s.ok());
|
||||
}
|
||||
|
||||
// RocksDB lite don't support dynamic options.
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
@@ -220,7 +204,6 @@ TEST_F(DBOptionsTest, SetMutableTableOptions) {
|
||||
|
||||
ColumnFamilyHandle* cfh = dbfull()->DefaultColumnFamily();
|
||||
Options c_opts = dbfull()->GetOptions(cfh);
|
||||
|
||||
const auto* c_bbto =
|
||||
c_opts.table_factory->GetOptions<BlockBasedTableOptions>();
|
||||
ASSERT_NE(c_bbto, nullptr);
|
||||
@@ -441,8 +424,8 @@ TEST_F(DBOptionsTest, WritableFileMaxBufferSize) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
int i = 0;
|
||||
for (; i < 3; i++) {
|
||||
ASSERT_OK(Put("foo", std::to_string(i)));
|
||||
ASSERT_OK(Put("bar", std::to_string(i)));
|
||||
ASSERT_OK(Put("foo", ToString(i)));
|
||||
ASSERT_OK(Put("bar", ToString(i)));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -459,8 +442,8 @@ TEST_F(DBOptionsTest, WritableFileMaxBufferSize) {
|
||||
dbfull()->GetDBOptions().writable_file_max_buffer_size);
|
||||
i = 0;
|
||||
for (; i < 3; i++) {
|
||||
ASSERT_OK(Put("foo", std::to_string(i)));
|
||||
ASSERT_OK(Put("bar", std::to_string(i)));
|
||||
ASSERT_OK(Put("foo", ToString(i)));
|
||||
ASSERT_OK(Put("bar", ToString(i)));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -590,8 +573,8 @@ TEST_F(DBOptionsTest, SetOptionsMayTriggerCompaction) {
|
||||
Reopen(options);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Need to insert two keys to avoid trivial move.
|
||||
ASSERT_OK(Put("foo", std::to_string(i)));
|
||||
ASSERT_OK(Put("bar", std::to_string(i)));
|
||||
ASSERT_OK(Put("foo", ToString(i)));
|
||||
ASSERT_OK(Put("bar", ToString(i)));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ("3", FilesPerLevel());
|
||||
@@ -734,8 +717,8 @@ TEST_F(DBOptionsTest, SetStatsDumpPeriodSec) {
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
unsigned int num = rand() % 5000 + 1;
|
||||
ASSERT_OK(dbfull()->SetDBOptions(
|
||||
{{"stats_dump_period_sec", std::to_string(num)}}));
|
||||
ASSERT_OK(
|
||||
dbfull()->SetDBOptions({{"stats_dump_period_sec", ToString(num)}}));
|
||||
ASSERT_EQ(num, dbfull()->GetDBOptions().stats_dump_period_sec);
|
||||
}
|
||||
Close();
|
||||
@@ -926,7 +909,7 @@ TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -957,7 +940,7 @@ TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -989,7 +972,7 @@ TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -1053,7 +1036,7 @@ TEST_F(DBOptionsTest, FIFOTtlBackwardCompatible) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
+15
-16
@@ -593,9 +593,9 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ResetTableProperties(&sum_tp);
|
||||
for (int level = 0; level < kMaxLevel; ++level) {
|
||||
db_->GetProperty(DB::Properties::kAggregatedTablePropertiesAtLevel +
|
||||
std::to_string(level),
|
||||
&level_tp_strings[level]);
|
||||
db_->GetProperty(
|
||||
DB::Properties::kAggregatedTablePropertiesAtLevel + ToString(level),
|
||||
&level_tp_strings[level]);
|
||||
ParseTablePropertiesString(level_tp_strings[level], &level_tps[level]);
|
||||
sum_tp.data_size += level_tps[level].data_size;
|
||||
sum_tp.index_size += level_tps[level].index_size;
|
||||
@@ -1091,7 +1091,7 @@ TEST_F(DBPropertiesTest, EstimateCompressionRatio) {
|
||||
for (int j = 0; j < kNumEntriesPerFile; ++j) {
|
||||
// Put common data ("key") at end to prevent delta encoding from
|
||||
// compressing the key effectively
|
||||
std::string key = std::to_string(i) + std::to_string(j) + "key";
|
||||
std::string key = ToString(i) + ToString(j) + "key";
|
||||
ASSERT_OK(dbfull()->Put(WriteOptions(), key, kVal));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
@@ -1185,7 +1185,7 @@ class CountingDeleteTabPropCollector : public TablePropertiesCollector {
|
||||
|
||||
Status Finish(UserCollectedProperties* properties) override {
|
||||
*properties =
|
||||
UserCollectedProperties{{"num_delete", std::to_string(num_deletes_)}};
|
||||
UserCollectedProperties{{"num_delete", ToString(num_deletes_)}};
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -1215,7 +1215,7 @@ class BlockCountingTablePropertiesCollector : public TablePropertiesCollector {
|
||||
|
||||
Status Finish(UserCollectedProperties* properties) override {
|
||||
(*properties)[kNumSampledBlocksPropertyName] =
|
||||
std::to_string(num_sampled_blocks_);
|
||||
ToString(num_sampled_blocks_);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -1235,7 +1235,7 @@ class BlockCountingTablePropertiesCollector : public TablePropertiesCollector {
|
||||
|
||||
UserCollectedProperties GetReadableProperties() const override {
|
||||
return UserCollectedProperties{
|
||||
{kNumSampledBlocksPropertyName, std::to_string(num_sampled_blocks_)},
|
||||
{kNumSampledBlocksPropertyName, ToString(num_sampled_blocks_)},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1272,8 +1272,7 @@ TEST_F(DBPropertiesTest, GetUserDefinedTableProperties) {
|
||||
// Create 4 tables
|
||||
for (int table = 0; table < 4; ++table) {
|
||||
for (int i = 0; i < 10 + table; ++i) {
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), std::to_string(table * 100 + i), "val"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), ToString(table * 100 + i), "val"));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
@@ -1313,7 +1312,7 @@ TEST_F(DBPropertiesTest, UserDefinedTablePropertiesContext) {
|
||||
// Create 2 files
|
||||
for (int table = 0; table < 2; ++table) {
|
||||
for (int i = 0; i < 10 + table; ++i) {
|
||||
ASSERT_OK(Put(1, std::to_string(table * 100 + i), "val"));
|
||||
ASSERT_OK(Put(1, ToString(table * 100 + i), "val"));
|
||||
}
|
||||
ASSERT_OK(Flush(1));
|
||||
}
|
||||
@@ -1323,7 +1322,7 @@ TEST_F(DBPropertiesTest, UserDefinedTablePropertiesContext) {
|
||||
// Trigger automatic compactions.
|
||||
for (int table = 0; table < 3; ++table) {
|
||||
for (int i = 0; i < 10 + table; ++i) {
|
||||
ASSERT_OK(Put(1, std::to_string(table * 100 + i), "val"));
|
||||
ASSERT_OK(Put(1, ToString(table * 100 + i), "val"));
|
||||
}
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -1340,7 +1339,7 @@ TEST_F(DBPropertiesTest, UserDefinedTablePropertiesContext) {
|
||||
// Create 4 tables in default column family
|
||||
for (int table = 0; table < 2; ++table) {
|
||||
for (int i = 0; i < 10 + table; ++i) {
|
||||
ASSERT_OK(Put(std::to_string(table * 100 + i), "val"));
|
||||
ASSERT_OK(Put(ToString(table * 100 + i), "val"));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -1350,7 +1349,7 @@ TEST_F(DBPropertiesTest, UserDefinedTablePropertiesContext) {
|
||||
// Trigger automatic compactions.
|
||||
for (int table = 0; table < 3; ++table) {
|
||||
for (int i = 0; i < 10 + table; ++i) {
|
||||
ASSERT_OK(Put(std::to_string(table * 100 + i), "val"));
|
||||
ASSERT_OK(Put(ToString(table * 100 + i), "val"));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -1546,7 +1545,7 @@ TEST_F(DBPropertiesTest, BlockAddForCompressionSampling) {
|
||||
user_props.end());
|
||||
ASSERT_EQ(user_props.at(BlockCountingTablePropertiesCollector::
|
||||
kNumSampledBlocksPropertyName),
|
||||
std::to_string(sample_for_compression ? 1 : 0));
|
||||
ToString(sample_for_compression ? 1 : 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1743,11 +1742,11 @@ TEST_F(DBPropertiesTest, SstFilesSize) {
|
||||
Reopen(options);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put("key" + std::to_string(i), std::string(1000, 'v')));
|
||||
ASSERT_OK(Put("key" + ToString(i), std::string(1000, 'v')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ASSERT_OK(Delete("key" + std::to_string(i)));
|
||||
ASSERT_OK(Delete("key" + ToString(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
uint64_t sst_size;
|
||||
|
||||
@@ -190,10 +190,9 @@ TEST_F(DBRangeDelTest, MaxCompactionBytesCutsOutputFiles) {
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(NumTableFilesAtLevel(2), 2);
|
||||
|
||||
ASSERT_OK(
|
||||
db_->SetOptions(db_->DefaultColumnFamily(),
|
||||
{{"target_file_size_base",
|
||||
std::to_string(100 * opts.max_compaction_bytes)}}));
|
||||
ASSERT_OK(db_->SetOptions(
|
||||
db_->DefaultColumnFamily(),
|
||||
{{"target_file_size_base", ToString(100 * opts.max_compaction_bytes)}}));
|
||||
|
||||
// It spans the whole key-range, thus will be included in all output files
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
@@ -501,8 +500,7 @@ TEST_F(DBRangeDelTest, ValidUniversalSubcompactionBoundaries) {
|
||||
1 /* input_level */, 2 /* output_level */, CompactRangeOptions(),
|
||||
nullptr /* begin */, nullptr /* end */, true /* exclusive */,
|
||||
true /* disallow_trivial_move */,
|
||||
std::numeric_limits<uint64_t>::max() /* max_file_num_to_ignore */,
|
||||
"" /*trim_ts*/));
|
||||
port::kMaxUint64 /* max_file_num_to_ignore */, "" /*trim_ts*/));
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
|
||||
@@ -139,6 +139,8 @@ TEST_P(DBRateLimiterOnReadTest, Get) {
|
||||
}
|
||||
|
||||
TEST_P(DBRateLimiterOnReadTest, NewMultiGet) {
|
||||
// The new void-returning `MultiGet()` APIs use `MultiRead()`, which does not
|
||||
// yet support rate limiting.
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
return;
|
||||
}
|
||||
@@ -147,7 +149,6 @@ TEST_P(DBRateLimiterOnReadTest, NewMultiGet) {
|
||||
ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
|
||||
const int kNumKeys = kNumFiles * kNumKeysPerFile;
|
||||
int64_t expected = 0;
|
||||
{
|
||||
std::vector<std::string> key_bufs;
|
||||
key_bufs.reserve(kNumKeys);
|
||||
@@ -159,19 +160,13 @@ TEST_P(DBRateLimiterOnReadTest, NewMultiGet) {
|
||||
}
|
||||
std::vector<Status> statuses(kNumKeys);
|
||||
std::vector<PinnableSlice> values(kNumKeys);
|
||||
const int64_t prev_total_rl_req = options_.rate_limiter->GetTotalRequests();
|
||||
db_->MultiGet(GetReadOptions(), dbfull()->DefaultColumnFamily(), kNumKeys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
const int64_t cur_total_rl_req = options_.rate_limiter->GetTotalRequests();
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_TRUE(statuses[i].ok());
|
||||
ASSERT_TRUE(statuses[i].IsNotSupported());
|
||||
}
|
||||
ASSERT_GT(cur_total_rl_req, prev_total_rl_req);
|
||||
ASSERT_EQ(cur_total_rl_req - prev_total_rl_req,
|
||||
options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
}
|
||||
expected += kNumKeys;
|
||||
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
}
|
||||
|
||||
TEST_P(DBRateLimiterOnReadTest, OldMultiGet) {
|
||||
|
||||
@@ -1,960 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_with_timestamp_test_util.h"
|
||||
#include "test_util/testutil.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class DBReadOnlyTestWithTimestamp : public DBBasicTestWithTimestampBase {
|
||||
public:
|
||||
DBReadOnlyTestWithTimestamp()
|
||||
: DBBasicTestWithTimestampBase("db_readonly_test_with_timestamp") {}
|
||||
|
||||
protected:
|
||||
#ifndef ROCKSDB_LITE
|
||||
void CheckDBOpenedAsCompactedDBWithOneLevel0File() {
|
||||
VersionSet* const versions = dbfull()->GetVersionSet();
|
||||
ASSERT_NE(versions, nullptr);
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
ASSERT_NE(cfd, nullptr);
|
||||
|
||||
Version* const current = cfd->current();
|
||||
ASSERT_NE(current, nullptr);
|
||||
|
||||
const VersionStorageInfo* const storage_info = current->storage_info();
|
||||
ASSERT_NE(storage_info, nullptr);
|
||||
|
||||
// Only 1 L0 file.
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
// L0 is the max level.
|
||||
ASSERT_EQ(storage_info->num_non_empty_levels(), 1);
|
||||
}
|
||||
|
||||
void CheckDBOpenedAsCompactedDBWithOnlyHighestNonEmptyLevelFiles() {
|
||||
VersionSet* const versions = dbfull()->GetVersionSet();
|
||||
ASSERT_NE(versions, nullptr);
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
ASSERT_NE(cfd, nullptr);
|
||||
|
||||
Version* const current = cfd->current();
|
||||
ASSERT_NE(current, nullptr);
|
||||
|
||||
const VersionStorageInfo* const storage_info = current->storage_info();
|
||||
ASSERT_NE(storage_info, nullptr);
|
||||
|
||||
// L0 has no files.
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
|
||||
// All other levels have no files except the highest level with files.
|
||||
for (int i = 1; i < storage_info->num_non_empty_levels() - 1; ++i) {
|
||||
ASSERT_FALSE(storage_info->LevelFilesBrief(i).num_files > 0);
|
||||
}
|
||||
|
||||
// The highest level with files have some files.
|
||||
int highest_non_empty_level = storage_info->num_non_empty_levels() - 1;
|
||||
ASSERT_TRUE(
|
||||
storage_info->LevelFilesBrief(highest_non_empty_level).num_files > 0);
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBReadOnlyTestWithTimestamp, IteratorAndGetReadTimestampSizeMismatch) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::string write_timestamp = Timestamp(1, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database in read only mode to test its timestamp support.
|
||||
Close();
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ReadOptions read_opts;
|
||||
std::string different_size_read_timestamp;
|
||||
PutFixed32(&different_size_read_timestamp, 2);
|
||||
Slice different_size_read_ts = different_size_read_timestamp;
|
||||
read_opts.timestamp = &different_size_read_ts;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsInvalidArgument());
|
||||
}
|
||||
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
std::string value_from_get;
|
||||
std::string timestamp;
|
||||
ASSERT_TRUE(db_->Get(read_opts, Key1(key), &value_from_get, ×tamp)
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp,
|
||||
IteratorAndGetReadTimestampSpecifiedWithoutWriteTimestamp) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), "value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database in read only mode to test its timestamp support.
|
||||
Close();
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ReadOptions read_opts;
|
||||
const std::string read_timestamp = Timestamp(2, 0);
|
||||
Slice read_ts = read_timestamp;
|
||||
read_opts.timestamp = &read_ts;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsInvalidArgument());
|
||||
}
|
||||
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
std::string value_from_get;
|
||||
std::string timestamp;
|
||||
ASSERT_TRUE(db_->Get(read_opts, Key1(key), &value_from_get, ×tamp)
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp,
|
||||
IteratorAndGetWriteWithTimestampReadWithoutTimestamp) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::string write_timestamp = Timestamp(1, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database in read only mode to test its timestamp support.
|
||||
Close();
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ReadOptions read_opts;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsInvalidArgument());
|
||||
}
|
||||
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
std::string value_from_get;
|
||||
ASSERT_TRUE(
|
||||
db_->Get(read_opts, Key1(key), &value_from_get).IsInvalidArgument());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp, IteratorAndGet) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::vector<uint64_t> start_keys = {1, 0};
|
||||
const std::vector<std::string> write_timestamps = {Timestamp(1, 0),
|
||||
Timestamp(3, 0)};
|
||||
const std::vector<std::string> read_timestamps = {Timestamp(2, 0),
|
||||
Timestamp(4, 0)};
|
||||
for (size_t i = 0; i < write_timestamps.size(); ++i) {
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = start_keys[i]; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamps[i],
|
||||
"value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
|
||||
// Reopen the database in read only mode to test its timestamp support.
|
||||
Close();
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
|
||||
auto get_value_and_check = [](DB* db, ReadOptions read_opts, Slice key,
|
||||
Slice expected_value, std::string expected_ts) {
|
||||
std::string value_from_get;
|
||||
std::string timestamp;
|
||||
ASSERT_OK(db->Get(read_opts, key.ToString(), &value_from_get, ×tamp));
|
||||
ASSERT_EQ(expected_value, value_from_get);
|
||||
ASSERT_EQ(expected_ts, timestamp);
|
||||
};
|
||||
for (size_t i = 0; i < read_timestamps.size(); ++i) {
|
||||
ReadOptions read_opts;
|
||||
Slice read_ts = read_timestamps[i];
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
int count = 0;
|
||||
uint64_t key = 0;
|
||||
// Forward iterate.
|
||||
for (it->Seek(Key1(0)), key = start_keys[i]; it->Valid();
|
||||
it->Next(), ++count, ++key) {
|
||||
CheckIterUserEntry(it.get(), Key1(key), kTypeValue,
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
get_value_and_check(db_, read_opts, it->key(), it->value(),
|
||||
write_timestamps[i]);
|
||||
}
|
||||
size_t expected_count = kMaxKey - start_keys[i] + 1;
|
||||
ASSERT_EQ(expected_count, count);
|
||||
|
||||
// Backward iterate.
|
||||
count = 0;
|
||||
for (it->SeekForPrev(Key1(kMaxKey)), key = kMaxKey; it->Valid();
|
||||
it->Prev(), ++count, --key) {
|
||||
CheckIterUserEntry(it.get(), Key1(key), kTypeValue,
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
get_value_and_check(db_, read_opts, it->key(), it->value(),
|
||||
write_timestamps[i]);
|
||||
}
|
||||
ASSERT_EQ(static_cast<size_t>(kMaxKey) - start_keys[i] + 1, count);
|
||||
|
||||
// SeekToFirst()/SeekToLast() with lower/upper bounds.
|
||||
// Then iter with lower and upper bounds.
|
||||
uint64_t l = 0;
|
||||
uint64_t r = kMaxKey + 1;
|
||||
while (l < r) {
|
||||
std::string lb_str = Key1(l);
|
||||
Slice lb = lb_str;
|
||||
std::string ub_str = Key1(r);
|
||||
Slice ub = ub_str;
|
||||
read_opts.iterate_lower_bound = &lb;
|
||||
read_opts.iterate_upper_bound = &ub;
|
||||
it.reset(db_->NewIterator(read_opts));
|
||||
for (it->SeekToFirst(), key = std::max(l, start_keys[i]), count = 0;
|
||||
it->Valid(); it->Next(), ++key, ++count) {
|
||||
CheckIterUserEntry(it.get(), Key1(key), kTypeValue,
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
get_value_and_check(db_, read_opts, it->key(), it->value(),
|
||||
write_timestamps[i]);
|
||||
}
|
||||
ASSERT_EQ(r - std::max(l, start_keys[i]), count);
|
||||
|
||||
for (it->SeekToLast(), key = std::min(r, kMaxKey + 1), count = 0;
|
||||
it->Valid(); it->Prev(), --key, ++count) {
|
||||
CheckIterUserEntry(it.get(), Key1(key - 1), kTypeValue,
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
get_value_and_check(db_, read_opts, it->key(), it->value(),
|
||||
write_timestamps[i]);
|
||||
}
|
||||
l += (kMaxKey / 100);
|
||||
r -= (kMaxKey / 100);
|
||||
}
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp, Iterators) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::string write_timestamp = Timestamp(1, 0);
|
||||
const std::string read_timestamp = Timestamp(2, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database in read only mode to test its timestamp support.
|
||||
Close();
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ReadOptions read_opts;
|
||||
Slice read_ts = read_timestamp;
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::vector<Iterator*> iters;
|
||||
ASSERT_OK(db_->NewIterators(read_opts, {db_->DefaultColumnFamily()}, &iters));
|
||||
ASSERT_EQ(static_cast<uint64_t>(1), iters.size());
|
||||
|
||||
int count = 0;
|
||||
uint64_t key = 0;
|
||||
// Forward iterate.
|
||||
for (iters[0]->Seek(Key1(0)), key = 0; iters[0]->Valid();
|
||||
iters[0]->Next(), ++count, ++key) {
|
||||
CheckIterUserEntry(iters[0], Key1(key), kTypeValue,
|
||||
"value" + std::to_string(key), write_timestamp);
|
||||
}
|
||||
|
||||
size_t expected_count = kMaxKey - 0 + 1;
|
||||
ASSERT_EQ(expected_count, count);
|
||||
delete iters[0];
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp, IteratorsReadTimestampSizeMismatch) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::string write_timestamp = Timestamp(1, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database in read only mode to test its timestamp support.
|
||||
Close();
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ReadOptions read_opts;
|
||||
std::string different_size_read_timestamp;
|
||||
PutFixed32(&different_size_read_timestamp, 2);
|
||||
Slice different_size_read_ts = different_size_read_timestamp;
|
||||
read_opts.timestamp = &different_size_read_ts;
|
||||
{
|
||||
std::vector<Iterator*> iters;
|
||||
ASSERT_TRUE(
|
||||
db_->NewIterators(read_opts, {db_->DefaultColumnFamily()}, &iters)
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp,
|
||||
IteratorsReadTimestampSpecifiedWithoutWriteTimestamp) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), "value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database in read only mode to test its timestamp support.
|
||||
Close();
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ReadOptions read_opts;
|
||||
const std::string read_timestamp = Timestamp(2, 0);
|
||||
Slice read_ts = read_timestamp;
|
||||
read_opts.timestamp = &read_ts;
|
||||
{
|
||||
std::vector<Iterator*> iters;
|
||||
ASSERT_TRUE(
|
||||
db_->NewIterators(read_opts, {db_->DefaultColumnFamily()}, &iters)
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp,
|
||||
IteratorsWriteWithTimestampReadWithoutTimestamp) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::string write_timestamp = Timestamp(1, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database in read only mode to test its timestamp support.
|
||||
Close();
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ReadOptions read_opts;
|
||||
{
|
||||
std::vector<Iterator*> iters;
|
||||
ASSERT_TRUE(
|
||||
db_->NewIterators(read_opts, {db_->DefaultColumnFamily()}, &iters)
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp, CompactedDBGetReadTimestampSizeMismatch) {
|
||||
const int kNumKeysPerFile = 1026;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
std::string write_timestamp = Timestamp(1, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(0));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
Close();
|
||||
|
||||
// Reopen the database in read only mode as a Compacted DB to test its
|
||||
// timestamp support.
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
CheckDBOpenedAsCompactedDBWithOneLevel0File();
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string different_size_read_timestamp;
|
||||
PutFixed32(&different_size_read_timestamp, 2);
|
||||
Slice different_size_read_ts = different_size_read_timestamp;
|
||||
read_opts.timestamp = &different_size_read_ts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
std::string value_from_get;
|
||||
std::string timestamp;
|
||||
ASSERT_TRUE(db_->Get(read_opts, Key1(key), &value_from_get, ×tamp)
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp,
|
||||
CompactedDBGetReadTimestampSpecifiedWithoutWriteTimestamp) {
|
||||
const int kNumKeysPerFile = 1026;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), "value" + std::to_string(0));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
Close();
|
||||
|
||||
// Reopen the database in read only mode as a Compacted DB to test its
|
||||
// timestamp support.
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
CheckDBOpenedAsCompactedDBWithOneLevel0File();
|
||||
|
||||
ReadOptions read_opts;
|
||||
const std::string read_timestamp = Timestamp(2, 0);
|
||||
Slice read_ts = read_timestamp;
|
||||
read_opts.timestamp = &read_ts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
std::string value_from_get;
|
||||
std::string timestamp;
|
||||
ASSERT_TRUE(db_->Get(read_opts, Key1(key), &value_from_get, ×tamp)
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp,
|
||||
CompactedDBGetWriteWithTimestampReadWithoutTimestamp) {
|
||||
const int kNumKeysPerFile = 1026;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
std::string write_timestamp = Timestamp(1, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(0));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
Close();
|
||||
|
||||
// Reopen the database in read only mode as a Compacted DB to test its
|
||||
// timestamp support.
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
CheckDBOpenedAsCompactedDBWithOneLevel0File();
|
||||
|
||||
ReadOptions read_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
std::string value_from_get;
|
||||
ASSERT_TRUE(
|
||||
db_->Get(read_opts, Key1(key), &value_from_get).IsInvalidArgument());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp, CompactedDBGetWithOnlyOneL0File) {
|
||||
const int kNumKeysPerFile = 1026 * 2;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::vector<uint64_t> start_keys = {1, 0};
|
||||
const std::vector<std::string> write_timestamps = {Timestamp(1, 0),
|
||||
Timestamp(3, 0)};
|
||||
const std::vector<std::string> read_timestamps = {Timestamp(2, 0),
|
||||
Timestamp(4, 0)};
|
||||
for (size_t i = 0; i < write_timestamps.size(); ++i) {
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = start_keys[i]; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamps[i],
|
||||
"value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
Close();
|
||||
|
||||
// Reopen the database in read only mode as a Compacted DB to test its
|
||||
// timestamp support.
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
CheckDBOpenedAsCompactedDBWithOneLevel0File();
|
||||
|
||||
for (size_t i = 0; i < read_timestamps.size(); ++i) {
|
||||
ReadOptions read_opts;
|
||||
Slice read_ts = read_timestamps[i];
|
||||
read_opts.timestamp = &read_ts;
|
||||
int count = 0;
|
||||
for (uint64_t key = start_keys[i]; key <= kMaxKey; ++key, ++count) {
|
||||
std::string value_from_get;
|
||||
std::string timestamp;
|
||||
ASSERT_OK(db_->Get(read_opts, Key1(key), &value_from_get, ×tamp));
|
||||
ASSERT_EQ("value" + std::to_string(i), value_from_get);
|
||||
ASSERT_EQ(write_timestamps[i], timestamp);
|
||||
}
|
||||
size_t expected_count = kMaxKey - start_keys[i] + 1;
|
||||
ASSERT_EQ(expected_count, count);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp,
|
||||
CompactedDBGetWithOnlyHighestNonEmptyLevelFiles) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::vector<uint64_t> start_keys = {1, 0};
|
||||
const std::vector<std::string> write_timestamps = {Timestamp(1, 0),
|
||||
Timestamp(3, 0)};
|
||||
const std::vector<std::string> read_timestamps = {Timestamp(2, 0),
|
||||
Timestamp(4, 0)};
|
||||
for (size_t i = 0; i < write_timestamps.size(); ++i) {
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = start_keys[i]; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamps[i],
|
||||
"value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
Close();
|
||||
|
||||
// Reopen the database in read only mode as a Compacted DB to test its
|
||||
// timestamp support.
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
CheckDBOpenedAsCompactedDBWithOnlyHighestNonEmptyLevelFiles();
|
||||
|
||||
for (size_t i = 0; i < read_timestamps.size(); ++i) {
|
||||
ReadOptions read_opts;
|
||||
Slice read_ts = read_timestamps[i];
|
||||
read_opts.timestamp = &read_ts;
|
||||
int count = 0;
|
||||
for (uint64_t key = start_keys[i]; key <= kMaxKey; ++key, ++count) {
|
||||
std::string value_from_get;
|
||||
std::string timestamp;
|
||||
ASSERT_OK(db_->Get(read_opts, Key1(key), &value_from_get, ×tamp));
|
||||
ASSERT_EQ("value" + std::to_string(i), value_from_get);
|
||||
ASSERT_EQ(write_timestamps[i], timestamp);
|
||||
}
|
||||
size_t expected_count = kMaxKey - start_keys[i] + 1;
|
||||
ASSERT_EQ(expected_count, count);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp,
|
||||
CompactedDBMultiGetReadTimestampSizeMismatch) {
|
||||
const int kNumKeysPerFile = 1026;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
std::string write_timestamp = Timestamp(1, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(0));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
Close();
|
||||
|
||||
// Reopen the database in read only mode as a Compacted DB to test its
|
||||
// timestamp support.
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
CheckDBOpenedAsCompactedDBWithOneLevel0File();
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string different_size_read_timestamp;
|
||||
PutFixed32(&different_size_read_timestamp, 2);
|
||||
Slice different_size_read_ts = different_size_read_timestamp;
|
||||
read_opts.timestamp = &different_size_read_ts;
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
key_strs.push_back(Key1(key));
|
||||
}
|
||||
for (const auto& key_str : key_strs) {
|
||||
keys.emplace_back(key_str);
|
||||
}
|
||||
std::vector<std::string> values;
|
||||
std::vector<std::string> timestamps;
|
||||
std::vector<Status> status_list =
|
||||
db_->MultiGet(read_opts, keys, &values, ×tamps);
|
||||
for (const auto& status : status_list) {
|
||||
ASSERT_TRUE(status.IsInvalidArgument());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp,
|
||||
CompactedDBMultiGetReadTimestampSpecifiedWithoutWriteTimestamp) {
|
||||
const int kNumKeysPerFile = 1026;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), "value" + std::to_string(0));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
Close();
|
||||
|
||||
// Reopen the database in read only mode as a Compacted DB to test its
|
||||
// timestamp support.
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
CheckDBOpenedAsCompactedDBWithOneLevel0File();
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string read_timestamp = Timestamp(2, 0);
|
||||
Slice read_ts = read_timestamp;
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
key_strs.push_back(Key1(key));
|
||||
}
|
||||
for (const auto& key_str : key_strs) {
|
||||
keys.emplace_back(key_str);
|
||||
}
|
||||
std::vector<std::string> values;
|
||||
std::vector<std::string> timestamps;
|
||||
std::vector<Status> status_list =
|
||||
db_->MultiGet(read_opts, keys, &values, ×tamps);
|
||||
for (const auto& status : status_list) {
|
||||
ASSERT_TRUE(status.IsInvalidArgument());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp,
|
||||
CompactedDBMultiGetWriteWithTimestampReadWithoutTimestamp) {
|
||||
const int kNumKeysPerFile = 1026;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
std::string write_timestamp = Timestamp(1, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(0));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
Close();
|
||||
|
||||
// Reopen the database in read only mode as a Compacted DB to test its
|
||||
// timestamp support.
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
CheckDBOpenedAsCompactedDBWithOneLevel0File();
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
key_strs.push_back(Key1(key));
|
||||
}
|
||||
for (const auto& key_str : key_strs) {
|
||||
keys.emplace_back(key_str);
|
||||
}
|
||||
std::vector<std::string> values;
|
||||
std::vector<Status> status_list = db_->MultiGet(read_opts, keys, &values);
|
||||
for (const auto& status : status_list) {
|
||||
ASSERT_TRUE(status.IsInvalidArgument());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp, CompactedDBMultiGetWithOnlyOneL0File) {
|
||||
const int kNumKeysPerFile = 1026 * 2;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::vector<uint64_t> start_keys = {1, 0};
|
||||
const std::vector<std::string> write_timestamps = {Timestamp(1, 0),
|
||||
Timestamp(3, 0)};
|
||||
const std::vector<std::string> read_timestamps = {Timestamp(2, 0),
|
||||
Timestamp(4, 0)};
|
||||
for (size_t i = 0; i < write_timestamps.size(); ++i) {
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = start_keys[i]; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamps[i],
|
||||
"value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
Close();
|
||||
|
||||
// Reopen the database in read only mode as a Compacted DB to test its
|
||||
// timestamp support.
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
CheckDBOpenedAsCompactedDBWithOneLevel0File();
|
||||
|
||||
for (size_t i = 0; i < write_timestamps.size(); ++i) {
|
||||
ReadOptions read_opts;
|
||||
Slice read_ts = read_timestamps[i];
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
for (uint64_t key = start_keys[i]; key <= kMaxKey; ++key) {
|
||||
key_strs.push_back(Key1(key));
|
||||
}
|
||||
for (const auto& key_str : key_strs) {
|
||||
keys.emplace_back(key_str);
|
||||
}
|
||||
size_t batch_size = kMaxKey - start_keys[i] + 1;
|
||||
std::vector<std::string> values;
|
||||
std::vector<std::string> timestamps;
|
||||
std::vector<Status> status_list =
|
||||
db_->MultiGet(read_opts, keys, &values, ×tamps);
|
||||
ASSERT_EQ(batch_size, values.size());
|
||||
ASSERT_EQ(batch_size, timestamps.size());
|
||||
for (uint64_t idx = 0; idx < values.size(); ++idx) {
|
||||
ASSERT_EQ("value" + std::to_string(i), values[idx]);
|
||||
ASSERT_EQ(write_timestamps[i], timestamps[idx]);
|
||||
ASSERT_OK(status_list[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBReadOnlyTestWithTimestamp,
|
||||
CompactedDBMultiGetWithOnlyHighestNonEmptyLevelFiles) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::vector<uint64_t> start_keys = {1, 0};
|
||||
const std::vector<std::string> write_timestamps = {Timestamp(1, 0),
|
||||
Timestamp(3, 0)};
|
||||
const std::vector<std::string> read_timestamps = {Timestamp(2, 0),
|
||||
Timestamp(4, 0)};
|
||||
for (size_t i = 0; i < write_timestamps.size(); ++i) {
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = start_keys[i]; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamps[i],
|
||||
"value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
Close();
|
||||
|
||||
// Reopen the database in read only mode as a Compacted DB to test its
|
||||
// timestamp support.
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
CheckDBOpenedAsCompactedDBWithOnlyHighestNonEmptyLevelFiles();
|
||||
|
||||
for (size_t i = 0; i < write_timestamps.size(); ++i) {
|
||||
ReadOptions read_opts;
|
||||
Slice read_ts = read_timestamps[i];
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
for (uint64_t key = start_keys[i]; key <= kMaxKey; ++key) {
|
||||
key_strs.push_back(Key1(key));
|
||||
}
|
||||
for (const auto& key_str : key_strs) {
|
||||
keys.emplace_back(key_str);
|
||||
}
|
||||
size_t batch_size = kMaxKey - start_keys[i] + 1;
|
||||
std::vector<std::string> values;
|
||||
std::vector<std::string> timestamps;
|
||||
std::vector<Status> status_list =
|
||||
db_->MultiGet(read_opts, keys, &values, ×tamps);
|
||||
ASSERT_EQ(batch_size, values.size());
|
||||
ASSERT_EQ(batch_size, timestamps.size());
|
||||
for (uint64_t idx = 0; idx < values.size(); ++idx) {
|
||||
ASSERT_EQ("value" + std::to_string(i), values[idx]);
|
||||
ASSERT_EQ(write_timestamps[i], timestamps[idx]);
|
||||
ASSERT_OK(status_list[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+16
-449
@@ -9,7 +9,6 @@
|
||||
|
||||
#include "db/db_impl/db_impl_secondary.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/db_with_timestamp_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/utilities/transaction_db.h"
|
||||
#include "test_util/sync_point.h"
|
||||
@@ -19,10 +18,10 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class DBSecondaryTestBase : public DBBasicTestWithTimestampBase {
|
||||
class DBSecondaryTest : public DBTestBase {
|
||||
public:
|
||||
explicit DBSecondaryTestBase(const std::string& dbname)
|
||||
: DBBasicTestWithTimestampBase(dbname),
|
||||
DBSecondaryTest()
|
||||
: DBTestBase("db_secondary_test", /*env_do_fsync=*/true),
|
||||
secondary_path_(),
|
||||
handles_secondary_(),
|
||||
db_secondary_(nullptr) {
|
||||
@@ -30,7 +29,7 @@ class DBSecondaryTestBase : public DBBasicTestWithTimestampBase {
|
||||
test::PerThreadDBPath(env_, "/db_secondary_test_secondary");
|
||||
}
|
||||
|
||||
~DBSecondaryTestBase() override {
|
||||
~DBSecondaryTest() override {
|
||||
CloseSecondary();
|
||||
if (getenv("KEEP_DB") != nullptr) {
|
||||
fprintf(stdout, "Secondary DB is still at %s\n", secondary_path_.c_str());
|
||||
@@ -74,17 +73,17 @@ class DBSecondaryTestBase : public DBBasicTestWithTimestampBase {
|
||||
DB* db_secondary_;
|
||||
};
|
||||
|
||||
void DBSecondaryTestBase::OpenSecondary(const Options& options) {
|
||||
void DBSecondaryTest::OpenSecondary(const Options& options) {
|
||||
ASSERT_OK(TryOpenSecondary(options));
|
||||
}
|
||||
|
||||
Status DBSecondaryTestBase::TryOpenSecondary(const Options& options) {
|
||||
Status DBSecondaryTest::TryOpenSecondary(const Options& options) {
|
||||
Status s =
|
||||
DB::OpenAsSecondary(options, dbname_, secondary_path_, &db_secondary_);
|
||||
return s;
|
||||
}
|
||||
|
||||
void DBSecondaryTestBase::OpenSecondaryWithColumnFamilies(
|
||||
void DBSecondaryTest::OpenSecondaryWithColumnFamilies(
|
||||
const std::vector<std::string>& column_families, const Options& options) {
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs;
|
||||
cf_descs.emplace_back(kDefaultColumnFamilyName, options);
|
||||
@@ -96,10 +95,9 @@ void DBSecondaryTestBase::OpenSecondaryWithColumnFamilies(
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
void DBSecondaryTestBase::CheckFileTypeCounts(const std::string& dir,
|
||||
int expected_log,
|
||||
int expected_sst,
|
||||
int expected_manifest) const {
|
||||
void DBSecondaryTest::CheckFileTypeCounts(const std::string& dir,
|
||||
int expected_log, int expected_sst,
|
||||
int expected_manifest) const {
|
||||
std::vector<std::string> filenames;
|
||||
ASSERT_OK(env_->GetChildren(dir, &filenames));
|
||||
|
||||
@@ -118,35 +116,6 @@ void DBSecondaryTestBase::CheckFileTypeCounts(const std::string& dir,
|
||||
ASSERT_EQ(expected_manifest, manifest_cnt);
|
||||
}
|
||||
|
||||
class DBSecondaryTest : public DBSecondaryTestBase {
|
||||
public:
|
||||
explicit DBSecondaryTest() : DBSecondaryTestBase("db_secondary_test") {}
|
||||
};
|
||||
|
||||
TEST_F(DBSecondaryTest, FailOpenIfLoggerCreationFail) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
Reopen(options);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"rocksdb::CreateLoggerFromOptions:AfterGetPath", [&](void* arg) {
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
assert(s);
|
||||
*s = Status::IOError("Injected");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
options.max_open_files = -1;
|
||||
Status s = TryOpenSecondary(options);
|
||||
ASSERT_EQ(nullptr, options.info_log);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, NonExistingDb) {
|
||||
Destroy(last_options_);
|
||||
|
||||
@@ -212,7 +181,6 @@ TEST_F(DBSecondaryTest, SimpleInternalCompaction) {
|
||||
ASSERT_EQ(input.input_files.size(), 3);
|
||||
|
||||
input.output_level = 1;
|
||||
ASSERT_OK(db_->GetDbIdentity(input.db_id));
|
||||
Close();
|
||||
|
||||
options.max_open_files = -1;
|
||||
@@ -244,20 +212,20 @@ TEST_F(DBSecondaryTest, InternalCompactionMultiLevels) {
|
||||
const int kRangeL2 = 10;
|
||||
const int kRangeL1 = 30;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put(Key(i * kRangeL2), "value" + std::to_string(i)));
|
||||
ASSERT_OK(Put(Key((i + 1) * kRangeL2 - 1), "value" + std::to_string(i)));
|
||||
ASSERT_OK(Put(Key(i * kRangeL2), "value" + ToString(i)));
|
||||
ASSERT_OK(Put(Key((i + 1) * kRangeL2 - 1), "value" + ToString(i)));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
MoveFilesToLevel(2);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ASSERT_OK(Put(Key(i * kRangeL1), "value" + std::to_string(i)));
|
||||
ASSERT_OK(Put(Key((i + 1) * kRangeL1 - 1), "value" + std::to_string(i)));
|
||||
ASSERT_OK(Put(Key(i * kRangeL1), "value" + ToString(i)));
|
||||
ASSERT_OK(Put(Key((i + 1) * kRangeL1 - 1), "value" + ToString(i)));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
MoveFilesToLevel(1);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ASSERT_OK(Put(Key(i * 30), "value" + std::to_string(i)));
|
||||
ASSERT_OK(Put(Key(i * 30 + 50), "value" + std::to_string(i)));
|
||||
ASSERT_OK(Put(Key(i * 30), "value" + ToString(i)));
|
||||
ASSERT_OK(Put(Key(i * 30 + 50), "value" + ToString(i)));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
@@ -273,7 +241,6 @@ TEST_F(DBSecondaryTest, InternalCompactionMultiLevels) {
|
||||
input1.input_files.push_back(meta.levels[1].files[2].name);
|
||||
|
||||
input1.output_level = 1;
|
||||
ASSERT_OK(db_->GetDbIdentity(input1.db_id));
|
||||
|
||||
options.max_open_files = -1;
|
||||
Close();
|
||||
@@ -294,7 +261,6 @@ TEST_F(DBSecondaryTest, InternalCompactionMultiLevels) {
|
||||
}
|
||||
|
||||
input2.output_level = 2;
|
||||
input2.db_id = input1.db_id;
|
||||
ASSERT_OK(db_secondary_full()->TEST_CompactWithoutInstallation(
|
||||
OpenAndCompactOptions(), cfh, input2, &result));
|
||||
ASSERT_OK(result.status);
|
||||
@@ -339,7 +305,6 @@ TEST_F(DBSecondaryTest, InternalCompactionCompactedFiles) {
|
||||
ASSERT_EQ(input.input_files.size(), 3);
|
||||
|
||||
input.output_level = 1;
|
||||
ASSERT_OK(db_->GetDbIdentity(input.db_id));
|
||||
|
||||
// trigger compaction to delete the files for secondary instance compaction
|
||||
ASSERT_OK(Put("foo", "foo_value" + std::to_string(3)));
|
||||
@@ -381,7 +346,6 @@ TEST_F(DBSecondaryTest, InternalCompactionMissingFiles) {
|
||||
ASSERT_EQ(input.input_files.size(), 3);
|
||||
|
||||
input.output_level = 1;
|
||||
ASSERT_OK(db_->GetDbIdentity(input.db_id));
|
||||
|
||||
Close();
|
||||
|
||||
@@ -1285,403 +1249,6 @@ TEST_F(DBSecondaryTest, OpenWithTransactionDB) {
|
||||
ASSERT_OK(TryOpenSecondary(options));
|
||||
}
|
||||
|
||||
class DBSecondaryTestWithTimestamp : public DBSecondaryTestBase {
|
||||
public:
|
||||
explicit DBSecondaryTestWithTimestamp()
|
||||
: DBSecondaryTestBase("db_secondary_test_with_timestamp") {}
|
||||
};
|
||||
TEST_F(DBSecondaryTestWithTimestamp, IteratorAndGetReadTimestampSizeMismatch) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::string write_timestamp = Timestamp(1, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database as secondary instance to test its timestamp support.
|
||||
Close();
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReopenAsSecondary(options));
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string different_size_read_timestamp;
|
||||
PutFixed32(&different_size_read_timestamp, 2);
|
||||
Slice different_size_read_ts = different_size_read_timestamp;
|
||||
read_opts.timestamp = &different_size_read_ts;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsInvalidArgument());
|
||||
}
|
||||
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
std::string value_from_get;
|
||||
std::string timestamp;
|
||||
ASSERT_TRUE(db_->Get(read_opts, Key1(key), &value_from_get, ×tamp)
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTestWithTimestamp,
|
||||
IteratorAndGetReadTimestampSpecifiedWithoutWriteTimestamp) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), "value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database as secondary instance to test its timestamp support.
|
||||
Close();
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReopenAsSecondary(options));
|
||||
|
||||
ReadOptions read_opts;
|
||||
const std::string read_timestamp = Timestamp(2, 0);
|
||||
Slice read_ts = read_timestamp;
|
||||
read_opts.timestamp = &read_ts;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsInvalidArgument());
|
||||
}
|
||||
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
std::string value_from_get;
|
||||
std::string timestamp;
|
||||
ASSERT_TRUE(db_->Get(read_opts, Key1(key), &value_from_get, ×tamp)
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTestWithTimestamp,
|
||||
IteratorAndGetWriteWithTimestampReadWithoutTimestamp) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::string write_timestamp = Timestamp(1, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database as secondary instance to test its timestamp support.
|
||||
Close();
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReopenAsSecondary(options));
|
||||
|
||||
ReadOptions read_opts;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsInvalidArgument());
|
||||
}
|
||||
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
std::string value_from_get;
|
||||
ASSERT_TRUE(
|
||||
db_->Get(read_opts, Key1(key), &value_from_get).IsInvalidArgument());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTestWithTimestamp, IteratorAndGet) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::vector<uint64_t> start_keys = {1, 0};
|
||||
const std::vector<std::string> write_timestamps = {Timestamp(1, 0),
|
||||
Timestamp(3, 0)};
|
||||
const std::vector<std::string> read_timestamps = {Timestamp(2, 0),
|
||||
Timestamp(4, 0)};
|
||||
for (size_t i = 0; i < write_timestamps.size(); ++i) {
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = start_keys[i]; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamps[i],
|
||||
"value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
|
||||
// Reopen the database as secondary instance to test its timestamp support.
|
||||
Close();
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReopenAsSecondary(options));
|
||||
|
||||
auto get_value_and_check = [](DB* db, ReadOptions read_opts, Slice key,
|
||||
Slice expected_value, std::string expected_ts) {
|
||||
std::string value_from_get;
|
||||
std::string timestamp;
|
||||
ASSERT_OK(db->Get(read_opts, key.ToString(), &value_from_get, ×tamp));
|
||||
ASSERT_EQ(expected_value, value_from_get);
|
||||
ASSERT_EQ(expected_ts, timestamp);
|
||||
};
|
||||
for (size_t i = 0; i < read_timestamps.size(); ++i) {
|
||||
ReadOptions read_opts;
|
||||
Slice read_ts = read_timestamps[i];
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
int count = 0;
|
||||
uint64_t key = 0;
|
||||
// Forward iterate.
|
||||
for (it->Seek(Key1(0)), key = start_keys[i]; it->Valid();
|
||||
it->Next(), ++count, ++key) {
|
||||
CheckIterUserEntry(it.get(), Key1(key), kTypeValue,
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
get_value_and_check(db_, read_opts, it->key(), it->value(),
|
||||
write_timestamps[i]);
|
||||
}
|
||||
size_t expected_count = kMaxKey - start_keys[i] + 1;
|
||||
ASSERT_EQ(expected_count, count);
|
||||
|
||||
// Backward iterate.
|
||||
count = 0;
|
||||
for (it->SeekForPrev(Key1(kMaxKey)), key = kMaxKey; it->Valid();
|
||||
it->Prev(), ++count, --key) {
|
||||
CheckIterUserEntry(it.get(), Key1(key), kTypeValue,
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
get_value_and_check(db_, read_opts, it->key(), it->value(),
|
||||
write_timestamps[i]);
|
||||
}
|
||||
ASSERT_EQ(static_cast<size_t>(kMaxKey) - start_keys[i] + 1, count);
|
||||
|
||||
// SeekToFirst()/SeekToLast() with lower/upper bounds.
|
||||
// Then iter with lower and upper bounds.
|
||||
uint64_t l = 0;
|
||||
uint64_t r = kMaxKey + 1;
|
||||
while (l < r) {
|
||||
std::string lb_str = Key1(l);
|
||||
Slice lb = lb_str;
|
||||
std::string ub_str = Key1(r);
|
||||
Slice ub = ub_str;
|
||||
read_opts.iterate_lower_bound = &lb;
|
||||
read_opts.iterate_upper_bound = &ub;
|
||||
it.reset(db_->NewIterator(read_opts));
|
||||
for (it->SeekToFirst(), key = std::max(l, start_keys[i]), count = 0;
|
||||
it->Valid(); it->Next(), ++key, ++count) {
|
||||
CheckIterUserEntry(it.get(), Key1(key), kTypeValue,
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
get_value_and_check(db_, read_opts, it->key(), it->value(),
|
||||
write_timestamps[i]);
|
||||
}
|
||||
ASSERT_EQ(r - std::max(l, start_keys[i]), count);
|
||||
|
||||
for (it->SeekToLast(), key = std::min(r, kMaxKey + 1), count = 0;
|
||||
it->Valid(); it->Prev(), --key, ++count) {
|
||||
CheckIterUserEntry(it.get(), Key1(key - 1), kTypeValue,
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
get_value_and_check(db_, read_opts, it->key(), it->value(),
|
||||
write_timestamps[i]);
|
||||
}
|
||||
l += (kMaxKey / 100);
|
||||
r -= (kMaxKey / 100);
|
||||
}
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTestWithTimestamp, IteratorsReadTimestampSizeMismatch) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::string write_timestamp = Timestamp(1, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database as secondary instance to test its timestamp support.
|
||||
Close();
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReopenAsSecondary(options));
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string different_size_read_timestamp;
|
||||
PutFixed32(&different_size_read_timestamp, 2);
|
||||
Slice different_size_read_ts = different_size_read_timestamp;
|
||||
read_opts.timestamp = &different_size_read_ts;
|
||||
{
|
||||
std::vector<Iterator*> iters;
|
||||
ASSERT_TRUE(
|
||||
db_->NewIterators(read_opts, {db_->DefaultColumnFamily()}, &iters)
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTestWithTimestamp,
|
||||
IteratorsReadTimestampSpecifiedWithoutWriteTimestamp) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), "value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database as secondary instance to test its timestamp support.
|
||||
Close();
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReopenAsSecondary(options));
|
||||
|
||||
ReadOptions read_opts;
|
||||
const std::string read_timestamp = Timestamp(2, 0);
|
||||
Slice read_ts = read_timestamp;
|
||||
read_opts.timestamp = &read_ts;
|
||||
{
|
||||
std::vector<Iterator*> iters;
|
||||
ASSERT_TRUE(
|
||||
db_->NewIterators(read_opts, {db_->DefaultColumnFamily()}, &iters)
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTestWithTimestamp,
|
||||
IteratorsWriteWithTimestampReadWithoutTimestamp) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::string write_timestamp = Timestamp(1, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database as secondary instance to test its timestamp support.
|
||||
Close();
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReopenAsSecondary(options));
|
||||
|
||||
ReadOptions read_opts;
|
||||
{
|
||||
std::vector<Iterator*> iters;
|
||||
ASSERT_TRUE(
|
||||
db_->NewIterators(read_opts, {db_->DefaultColumnFamily()}, &iters)
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTestWithTimestamp, Iterators) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::string write_timestamp = Timestamp(1, 0);
|
||||
const std::string read_timestamp = Timestamp(2, 0);
|
||||
WriteOptions write_opts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), write_timestamp,
|
||||
"value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
// Reopen the database as secondary instance to test its timestamp support.
|
||||
Close();
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(ReopenAsSecondary(options));
|
||||
|
||||
ReadOptions read_opts;
|
||||
Slice read_ts = read_timestamp;
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::vector<Iterator*> iters;
|
||||
ASSERT_OK(db_->NewIterators(read_opts, {db_->DefaultColumnFamily()}, &iters));
|
||||
ASSERT_EQ(static_cast<uint64_t>(1), iters.size());
|
||||
|
||||
int count = 0;
|
||||
uint64_t key = 0;
|
||||
// Forward iterate.
|
||||
for (iters[0]->Seek(Key1(0)), key = 0; iters[0]->Valid();
|
||||
iters[0]->Next(), ++count, ++key) {
|
||||
CheckIterUserEntry(iters[0], Key1(key), kTypeValue,
|
||||
"value" + std::to_string(key), write_timestamp);
|
||||
}
|
||||
|
||||
size_t expected_count = kMaxKey - 0 + 1;
|
||||
ASSERT_EQ(expected_count, count);
|
||||
delete iters[0];
|
||||
|
||||
Close();
|
||||
}
|
||||
#endif //! ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+9
-111
@@ -8,7 +8,6 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
@@ -281,58 +280,6 @@ TEST_F(DBSSTTest, DeleteObsoleteFilesPendingOutputs) {
|
||||
listener->VerifyMatchedCount(1);
|
||||
}
|
||||
|
||||
// Test that producing an empty .sst file does not write it out to
|
||||
// disk, and that the DeleteFile() env method is not called for
|
||||
// removing the non-existing file later.
|
||||
TEST_F(DBSSTTest, DeleteFileNotCalledForNotCreatedSSTFile) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
|
||||
OnFileDeletionListener* listener = new OnFileDeletionListener();
|
||||
options.listeners.emplace_back(listener);
|
||||
|
||||
Reopen(options);
|
||||
|
||||
// Flush the empty database.
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("", FilesPerLevel(0));
|
||||
|
||||
// We expect no .sst files.
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
ASSERT_EQ(metadata.size(), 0U);
|
||||
|
||||
// We expect no file deletions.
|
||||
listener->VerifyMatchedCount(0);
|
||||
}
|
||||
|
||||
// Test that producing a non-empty .sst file does write it out to
|
||||
// disk, and that the DeleteFile() env method is not called for removing
|
||||
// the file later.
|
||||
TEST_F(DBSSTTest, DeleteFileNotCalledForCreatedSSTFile) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
|
||||
OnFileDeletionListener* listener = new OnFileDeletionListener();
|
||||
options.listeners.emplace_back(listener);
|
||||
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("pika", "choo"));
|
||||
|
||||
// Flush the non-empty database.
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("1", FilesPerLevel(0));
|
||||
|
||||
// We expect 1 .sst files.
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
ASSERT_EQ(metadata.size(), 1U);
|
||||
|
||||
// We expect no file deletions.
|
||||
listener->VerifyMatchedCount(0);
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, DBWithSstFileManager) {
|
||||
std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
|
||||
@@ -962,6 +909,7 @@ TEST_F(DBSSTTest, OpenDBWithExistingTrash) {
|
||||
ASSERT_NOK(env_->FileExists(dbname_ + "/" + "003.sst.trash"));
|
||||
}
|
||||
|
||||
|
||||
// Create a DB with 2 db_paths, and generate multiple files in the 2
|
||||
// db_paths using CompactRangeOptions, make sure that files that were
|
||||
// deleted from first db_path were deleted using DeleteScheduler and
|
||||
@@ -999,7 +947,7 @@ TEST_F(DBSSTTest, DeleteSchedulerMultipleDBPaths) {
|
||||
|
||||
// Create 4 files in L0
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ASSERT_OK(Put("Key" + std::to_string(i), DummyString(1024, 'A'), wo));
|
||||
ASSERT_OK(Put("Key" + ToString(i), DummyString(1024, 'A'), wo));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
// We created 4 sst files in L0
|
||||
@@ -1015,7 +963,7 @@ TEST_F(DBSSTTest, DeleteSchedulerMultipleDBPaths) {
|
||||
|
||||
// Create 4 files in L0
|
||||
for (int i = 4; i < 8; i++) {
|
||||
ASSERT_OK(Put("Key" + std::to_string(i), DummyString(1024, 'B'), wo));
|
||||
ASSERT_OK(Put("Key" + ToString(i), DummyString(1024, 'B'), wo));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ("4,1", FilesPerLevel(0));
|
||||
@@ -1061,7 +1009,7 @@ TEST_F(DBSSTTest, DestroyDBWithRateLimitedDelete) {
|
||||
|
||||
// Create 4 files in L0
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ASSERT_OK(Put("Key" + std::to_string(i), DummyString(1024, 'A')));
|
||||
ASSERT_OK(Put("Key" + ToString(i), DummyString(1024, 'A')));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
// We created 4 sst files in L0
|
||||
@@ -1233,9 +1181,7 @@ TEST_F(DBSSTTest, CancellingCompactionsWorks) {
|
||||
ASSERT_GT(completed_compactions, 0);
|
||||
ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
|
||||
// Make sure the stat is bumped
|
||||
ASSERT_GT(dbfull()->immutable_db_options().statistics.get()->getTickerCount(
|
||||
COMPACTION_CANCELLED),
|
||||
0);
|
||||
ASSERT_GT(dbfull()->immutable_db_options().statistics.get()->getTickerCount(COMPACTION_CANCELLED), 0);
|
||||
ASSERT_EQ(0,
|
||||
dbfull()->immutable_db_options().statistics.get()->getTickerCount(
|
||||
FILES_MARKED_TRASH));
|
||||
@@ -1450,9 +1396,7 @@ TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFiles) {
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFilesSubjectToMemoryLimit) {
|
||||
for (CacheEntryRoleOptions::Decision charge_table_reader :
|
||||
{CacheEntryRoleOptions::Decision::kEnabled,
|
||||
CacheEntryRoleOptions::Decision::kDisabled}) {
|
||||
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
|
||||
@@ -1492,9 +1436,7 @@ TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFilesSubjectToMemoryLimit) {
|
||||
}
|
||||
Close();
|
||||
|
||||
table_options.cache_usage_options.options_overrides.insert(
|
||||
{CacheEntryRole::kBlockBasedTableReader,
|
||||
{/*.charged = */ charge_table_reader}});
|
||||
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 */);
|
||||
@@ -1503,13 +1445,8 @@ TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFilesSubjectToMemoryLimit) {
|
||||
// Reopening the DB will try to load all existing files, conditionally
|
||||
// subject to memory limit
|
||||
Status s = TryReopen(options);
|
||||
|
||||
if (charge_table_reader == CacheEntryRoleOptions::Decision::kEnabled) {
|
||||
if (table_options.reserve_table_reader_memory) {
|
||||
EXPECT_TRUE(s.IsMemoryLimit());
|
||||
EXPECT_TRUE(s.ToString().find(
|
||||
kCacheEntryRoleToCamelString[static_cast<std::uint32_t>(
|
||||
CacheEntryRole::kBlockBasedTableReader)]) !=
|
||||
std::string::npos);
|
||||
EXPECT_TRUE(s.ToString().find("memory limit based on cache capacity") !=
|
||||
std::string::npos);
|
||||
|
||||
@@ -1541,7 +1478,7 @@ TEST_F(DBSSTTest, GetTotalSstFilesSize) {
|
||||
// Generate 5 files in L0
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
std::string val = "val_file_" + std::to_string(i);
|
||||
std::string val = "val_file_" + ToString(i);
|
||||
ASSERT_OK(Put(Key(j), val));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
@@ -1631,45 +1568,6 @@ TEST_F(DBSSTTest, GetTotalSstFilesSize) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, OpenDBWithoutGetFileSizeInvocations) {
|
||||
Options options = CurrentOptions();
|
||||
std::unique_ptr<MockEnv> env{MockEnv::Create(Env::Default())};
|
||||
options.env = env.get();
|
||||
options.disable_auto_compactions = true;
|
||||
options.compression = kNoCompression;
|
||||
options.enable_blob_files = true;
|
||||
options.blob_file_size = 32; // create one blob per file
|
||||
options.skip_checking_sst_file_sizes_on_db_open = true;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// Generate 5 files in L0
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
std::string val = "val_file_" + std::to_string(i);
|
||||
ASSERT_OK(Put(Key(j), val));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
Close();
|
||||
|
||||
bool is_get_file_size_called = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"MockFileSystem::GetFileSize:CheckFileType", [&](void* arg) {
|
||||
std::string* filename = reinterpret_cast<std::string*>(arg);
|
||||
if (filename->find(".blob") != std::string::npos) {
|
||||
is_get_file_size_called = true;
|
||||
}
|
||||
});
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_FALSE(is_get_file_size_called);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, GetTotalSstFilesSizeVersionsFilesShared) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
@@ -84,8 +84,7 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfAllTablesTest) {
|
||||
}
|
||||
// Build file
|
||||
for (int i = 0; i < 10 + table; ++i) {
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), std::to_string(table * 100 + i), "val"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), ToString(table * 100 + i), "val"));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
@@ -114,7 +113,7 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfAllTablesTest) {
|
||||
// fetch key from 1st and 2nd table, which will internally place that table to
|
||||
// the table cache.
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Get(std::to_string(i * 100 + 0));
|
||||
Get(ToString(i * 100 + 0));
|
||||
}
|
||||
|
||||
VerifyTableProperties(db_, 10 + 11 + 12 + 13);
|
||||
@@ -123,7 +122,7 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfAllTablesTest) {
|
||||
Reopen(options);
|
||||
// fetch key from all tables, which will place them in table cache.
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
Get(std::to_string(i * 100 + 0));
|
||||
Get(ToString(i * 100 + 0));
|
||||
}
|
||||
VerifyTableProperties(db_, 10 + 11 + 12 + 13);
|
||||
|
||||
@@ -157,7 +156,7 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfAllTablesTest) {
|
||||
} else {
|
||||
bool found_corruption = false;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
std::string result = Get(std::to_string(i * 100 + 0));
|
||||
std::string result = Get(ToString(i * 100 + 0));
|
||||
if (result.find_first_of("Corruption: block checksum mismatch") !=
|
||||
std::string::npos) {
|
||||
found_corruption = true;
|
||||
@@ -188,7 +187,7 @@ TEST_F(DBTablePropertiesTest, InvalidIgnored) {
|
||||
|
||||
// Build file
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), std::to_string(i), "val"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), ToString(i), "val"));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
|
||||
|
||||
+71
-206
@@ -321,7 +321,7 @@ TEST_F(DBTest, MixedSlowdownOptions) {
|
||||
// We need the 2nd write to trigger delay. This is because delay is
|
||||
// estimated based on the last write size which is 0 for the first write.
|
||||
ASSERT_OK(dbfull()->Put(wo, "foo2", "bar2"));
|
||||
token.reset();
|
||||
token.reset();
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
@@ -379,7 +379,7 @@ TEST_F(DBTest, MixedSlowdownOptionsInQueue) {
|
||||
// We need the 2nd write to trigger delay. This is because delay is
|
||||
// estimated based on the last write size which is 0 for the first write.
|
||||
ASSERT_OK(dbfull()->Put(wo, "foo2", "bar2"));
|
||||
token.reset();
|
||||
token.reset();
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
@@ -448,7 +448,7 @@ TEST_F(DBTest, MixedSlowdownOptionsStop) {
|
||||
// We need the 2nd write to trigger delay. This is because delay is
|
||||
// estimated based on the last write size which is 0 for the first write.
|
||||
ASSERT_OK(dbfull()->Put(wo, "foo2", "bar2"));
|
||||
token.reset();
|
||||
token.reset();
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
@@ -483,6 +483,7 @@ TEST_F(DBTest, LevelLimitReopen) {
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
|
||||
TEST_F(DBTest, PutSingleDeleteGet) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
@@ -781,6 +782,7 @@ TEST_F(DBTest, GetFromImmutableLayer) {
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBTest, GetLevel0Ordering) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
@@ -1842,8 +1844,6 @@ TEST_F(DBTest, Snapshot) {
|
||||
uint64_t time_snap1 = GetTimeOldestSnapshots();
|
||||
ASSERT_GT(time_snap1, 0U);
|
||||
ASSERT_EQ(GetSequenceOldestSnapshots(), s1->GetSequenceNumber());
|
||||
ASSERT_EQ(GetTimeOldestSnapshots(),
|
||||
static_cast<uint64_t>(s1->GetUnixTime()));
|
||||
ASSERT_OK(Put(0, "foo", "0v2"));
|
||||
ASSERT_OK(Put(1, "foo", "1v2"));
|
||||
|
||||
@@ -1853,8 +1853,6 @@ TEST_F(DBTest, Snapshot) {
|
||||
ASSERT_EQ(2U, GetNumSnapshots());
|
||||
ASSERT_EQ(time_snap1, GetTimeOldestSnapshots());
|
||||
ASSERT_EQ(GetSequenceOldestSnapshots(), s1->GetSequenceNumber());
|
||||
ASSERT_EQ(GetTimeOldestSnapshots(),
|
||||
static_cast<uint64_t>(s1->GetUnixTime()));
|
||||
ASSERT_OK(Put(0, "foo", "0v3"));
|
||||
ASSERT_OK(Put(1, "foo", "1v3"));
|
||||
|
||||
@@ -1863,8 +1861,6 @@ TEST_F(DBTest, Snapshot) {
|
||||
ASSERT_EQ(3U, GetNumSnapshots());
|
||||
ASSERT_EQ(time_snap1, GetTimeOldestSnapshots());
|
||||
ASSERT_EQ(GetSequenceOldestSnapshots(), s1->GetSequenceNumber());
|
||||
ASSERT_EQ(GetTimeOldestSnapshots(),
|
||||
static_cast<uint64_t>(s1->GetUnixTime()));
|
||||
|
||||
ASSERT_OK(Put(0, "foo", "0v4"));
|
||||
ASSERT_OK(Put(1, "foo", "1v4"));
|
||||
@@ -1881,8 +1877,6 @@ TEST_F(DBTest, Snapshot) {
|
||||
ASSERT_EQ(2U, GetNumSnapshots());
|
||||
ASSERT_EQ(time_snap1, GetTimeOldestSnapshots());
|
||||
ASSERT_EQ(GetSequenceOldestSnapshots(), s1->GetSequenceNumber());
|
||||
ASSERT_EQ(GetTimeOldestSnapshots(),
|
||||
static_cast<uint64_t>(s1->GetUnixTime()));
|
||||
ASSERT_EQ("0v1", Get(0, "foo", s1));
|
||||
ASSERT_EQ("1v1", Get(1, "foo", s1));
|
||||
ASSERT_EQ("0v2", Get(0, "foo", s2));
|
||||
@@ -1898,8 +1892,6 @@ TEST_F(DBTest, Snapshot) {
|
||||
ASSERT_EQ(1U, GetNumSnapshots());
|
||||
ASSERT_LT(time_snap1, GetTimeOldestSnapshots());
|
||||
ASSERT_EQ(GetSequenceOldestSnapshots(), s2->GetSequenceNumber());
|
||||
ASSERT_EQ(GetTimeOldestSnapshots(),
|
||||
static_cast<uint64_t>(s2->GetUnixTime()));
|
||||
|
||||
db_->ReleaseSnapshot(s2);
|
||||
ASSERT_EQ(0U, GetNumSnapshots());
|
||||
@@ -2435,8 +2427,8 @@ TEST_F(DBTest, SnapshotFiles) {
|
||||
|
||||
// Also test GetLiveFilesStorageInfo
|
||||
std::vector<LiveFileStorageInfo> new_infos;
|
||||
ASSERT_OK(db_->GetLiveFilesStorageInfo(LiveFilesStorageInfoOptions(),
|
||||
&new_infos));
|
||||
ASSERT_OK(dbfull()->GetLiveFilesStorageInfo(LiveFilesStorageInfoOptions(),
|
||||
&new_infos));
|
||||
|
||||
// Close DB (while deletions disabled)
|
||||
Close();
|
||||
@@ -2742,7 +2734,7 @@ TEST_P(MultiThreadedDBTest, MultiThreaded) {
|
||||
Options options = CurrentOptions(options_override);
|
||||
std::vector<std::string> cfs;
|
||||
for (int i = 1; i < kColumnFamilies; ++i) {
|
||||
cfs.push_back(std::to_string(i));
|
||||
cfs.push_back(ToString(i));
|
||||
}
|
||||
Reopen(options);
|
||||
CreateAndReopenWithCF(cfs, options);
|
||||
@@ -2794,7 +2786,7 @@ static void GCThreadBody(void* arg) {
|
||||
WriteOptions wo;
|
||||
|
||||
for (int i = 0; i < kGCNumKeys; ++i) {
|
||||
std::string kv(std::to_string(i + id * kGCNumKeys));
|
||||
std::string kv(ToString(i + id * kGCNumKeys));
|
||||
ASSERT_OK(db->Put(wo, kv, kv));
|
||||
}
|
||||
t->done = true;
|
||||
@@ -2830,7 +2822,7 @@ TEST_F(DBTest, GroupCommitTest) {
|
||||
|
||||
std::vector<std::string> expected_db;
|
||||
for (int i = 0; i < kGCNumThreads * kGCNumKeys; ++i) {
|
||||
expected_db.push_back(std::to_string(i));
|
||||
expected_db.push_back(ToString(i));
|
||||
}
|
||||
std::sort(expected_db.begin(), expected_db.end());
|
||||
|
||||
@@ -2867,18 +2859,6 @@ class ModelDB : public DB {
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t GetUnixTime() const override {
|
||||
// no need to call this
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t GetTimestamp() const override {
|
||||
// no need to call this
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
explicit ModelDB(const Options& options) : options_(options) {}
|
||||
@@ -3471,40 +3451,7 @@ TEST_F(DBTest, BlockBasedTablePrefixIndexTest) {
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k2", "v2"));
|
||||
|
||||
// Reopen with different prefix extractor, make sure everything still works.
|
||||
// RocksDB should just fall back to the binary index.
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(2));
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_EQ("v1", Get("k1"));
|
||||
ASSERT_EQ("v2", Get("k2"));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Back to original
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "fixed:1"}}));
|
||||
ASSERT_EQ("v1", Get("k1"));
|
||||
ASSERT_EQ("v2", Get("k2"));
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Same if there's a problem initally loading prefix transform
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTable::Open::ForceNullTablePrefixExtractor",
|
||||
[&](void* arg) { *static_cast<bool*>(arg) = true; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_EQ("v1", Get("k1"));
|
||||
ASSERT_EQ("v2", Get("k2"));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Change again
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "fixed:2"}}));
|
||||
ASSERT_EQ("v1", Get("k1"));
|
||||
ASSERT_EQ("v2", Get("k2"));
|
||||
#endif // !ROCKSDB_LITE
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// Reopen with no prefix extractor, make sure everything still works.
|
||||
// Reopen it without prefix extractor, make sure everything still works.
|
||||
// RocksDB should just fall back to the binary index.
|
||||
table_options.index_type = BlockBasedTableOptions::kBinarySearch;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
@@ -3514,7 +3461,6 @@ TEST_F(DBTest, BlockBasedTablePrefixIndexTest) {
|
||||
ASSERT_EQ("v1", Get("k1"));
|
||||
ASSERT_EQ("v2", Get("k2"));
|
||||
}
|
||||
|
||||
TEST_F(DBTest, BlockBasedTablePrefixHashIndexTest) {
|
||||
// create a DB with block prefix index
|
||||
BlockBasedTableOptions table_options;
|
||||
@@ -3645,7 +3591,7 @@ TEST_P(DBTestWithParam, FIFOCompactionTest) {
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
for (int j = 0; j < 110; ++j) {
|
||||
ASSERT_OK(Put(std::to_string(i * 100 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 100 + j), rnd.RandomString(980)));
|
||||
}
|
||||
// flush should happen here
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
@@ -3661,7 +3607,7 @@ TEST_P(DBTestWithParam, FIFOCompactionTest) {
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 5);
|
||||
for (int i = 0; i < 50; ++i) {
|
||||
// these keys should be deleted in previous compaction
|
||||
ASSERT_EQ("NOT_FOUND", Get(std::to_string(i)));
|
||||
ASSERT_EQ("NOT_FOUND", Get(ToString(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3683,7 +3629,7 @@ TEST_F(DBTest, FIFOCompactionTestWithCompaction) {
|
||||
for (int i = 0; i < 60; i++) {
|
||||
// Generate and flush a file about 20KB.
|
||||
for (int j = 0; j < 20; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -3694,7 +3640,7 @@ TEST_F(DBTest, FIFOCompactionTestWithCompaction) {
|
||||
for (int i = 0; i < 60; i++) {
|
||||
// Generate and flush a file about 20KB.
|
||||
for (int j = 0; j < 20; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j + 2000), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j + 2000), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -3724,27 +3670,27 @@ TEST_F(DBTest, FIFOCompactionStyleWithCompactionAndDelete) {
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Each file contains a different key which will be dropped later.
|
||||
ASSERT_OK(Put("a" + std::to_string(i), rnd.RandomString(500)));
|
||||
ASSERT_OK(Put("key" + std::to_string(i), ""));
|
||||
ASSERT_OK(Put("z" + std::to_string(i), rnd.RandomString(500)));
|
||||
ASSERT_OK(Put("a" + ToString(i), rnd.RandomString(500)));
|
||||
ASSERT_OK(Put("key" + ToString(i), ""));
|
||||
ASSERT_OK(Put("z" + ToString(i), rnd.RandomString(500)));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ASSERT_EQ("", Get("key" + std::to_string(i)));
|
||||
ASSERT_EQ("", Get("key" + ToString(i)));
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Each file contains a different key which will be dropped later.
|
||||
ASSERT_OK(Put("a" + std::to_string(i), rnd.RandomString(500)));
|
||||
ASSERT_OK(Delete("key" + std::to_string(i)));
|
||||
ASSERT_OK(Put("z" + std::to_string(i), rnd.RandomString(500)));
|
||||
ASSERT_OK(Put("a" + ToString(i), rnd.RandomString(500)));
|
||||
ASSERT_OK(Delete("key" + ToString(i)));
|
||||
ASSERT_OK(Put("z" + ToString(i), rnd.RandomString(500)));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 2);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ASSERT_EQ("NOT_FOUND", Get("key" + std::to_string(i)));
|
||||
ASSERT_EQ("NOT_FOUND", Get("key" + ToString(i)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3805,7 +3751,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
|
||||
options.compaction_options_fifo.max_table_files_size = 150 << 10; // 150KB
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.ttl = 1 * 60 * 60; // 1 hour
|
||||
options.ttl = 1 * 60 * 60 ; // 1 hour
|
||||
options = CurrentOptions(options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -3813,7 +3759,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -3845,7 +3791,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -3861,7 +3807,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
// Create 1 more file to trigger TTL compaction. The old files are dropped.
|
||||
for (int i = 0; i < 1; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -3879,7 +3825,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
options.write_buffer_size = 10 << 10; // 10KB
|
||||
options.compaction_options_fifo.max_table_files_size = 150 << 10; // 150KB
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.ttl = 1 * 60 * 60; // 1 hour
|
||||
options.ttl = 1 * 60 * 60; // 1 hour
|
||||
options = CurrentOptions(options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -3887,7 +3833,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -3902,7 +3848,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int j = 0; j < 140; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -3925,7 +3871,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -3944,7 +3890,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
// Create 10 more files. The old 5 files are dropped as their ttl expired.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -3969,7 +3915,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
for (int i = 0; i < 60; i++) {
|
||||
// Generate and flush a file about 20KB.
|
||||
for (int j = 0; j < 20; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -3980,8 +3926,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
for (int i = 0; i < 60; i++) {
|
||||
// Generate and flush a file about 20KB.
|
||||
for (int j = 0; j < 20; j++) {
|
||||
ASSERT_OK(
|
||||
Put(std::to_string(i * 20 + j + 2000), rnd.RandomString(980)));
|
||||
ASSERT_OK(Put(ToString(i * 20 + j + 2000), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -4092,6 +4037,9 @@ class MockedRateLimiterWithNoOptionalAPIImpl : public RateLimiter {
|
||||
|
||||
~MockedRateLimiterWithNoOptionalAPIImpl() override {}
|
||||
|
||||
const char* Name() const override {
|
||||
return "MockedRateLimiterWithNoOptionalAPI";
|
||||
}
|
||||
void SetBytesPerSecond(int64_t bytes_per_second) override {
|
||||
(void)bytes_per_second;
|
||||
}
|
||||
@@ -4259,7 +4207,7 @@ TEST_F(DBTest, ConcurrentFlushWAL) {
|
||||
std::vector<port::Thread> threads;
|
||||
threads.emplace_back([&] {
|
||||
for (size_t i = 0; i < cnt; i++) {
|
||||
auto istr = std::to_string(i);
|
||||
auto istr = ToString(i);
|
||||
ASSERT_OK(db_->Put(wopt, db_->DefaultColumnFamily(), "a" + istr,
|
||||
"b" + istr));
|
||||
}
|
||||
@@ -4267,10 +4215,8 @@ TEST_F(DBTest, ConcurrentFlushWAL) {
|
||||
if (two_write_queues) {
|
||||
threads.emplace_back([&] {
|
||||
for (size_t i = cnt; i < 2 * cnt; i++) {
|
||||
auto istr = std::to_string(i);
|
||||
WriteBatch batch(0 /* reserved_bytes */, 0 /* max_bytes */,
|
||||
wopt.protection_bytes_per_key,
|
||||
0 /* default_cf_ts_sz */);
|
||||
auto istr = ToString(i);
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(batch.Put("a" + istr, "b" + istr));
|
||||
ASSERT_OK(
|
||||
dbfull()->WriteImpl(wopt, &batch, nullptr, nullptr, 0, true));
|
||||
@@ -4290,7 +4236,7 @@ TEST_F(DBTest, ConcurrentFlushWAL) {
|
||||
Reopen(options);
|
||||
for (size_t i = 0; i < cnt; i++) {
|
||||
PinnableSlice pval;
|
||||
auto istr = std::to_string(i);
|
||||
auto istr = ToString(i);
|
||||
ASSERT_OK(
|
||||
db_->Get(ropt, db_->DefaultColumnFamily(), "a" + istr, &pval));
|
||||
ASSERT_TRUE(pval == ("b" + istr));
|
||||
@@ -4313,7 +4259,7 @@ TEST_F(DBTest, ManualFlushWalAndWriteRace) {
|
||||
|
||||
port::Thread writeThread([&]() {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
auto istr = std::to_string(i);
|
||||
auto istr = ToString(i);
|
||||
ASSERT_OK(dbfull()->Put(wopts, "key_" + istr, "value_" + istr));
|
||||
}
|
||||
});
|
||||
@@ -4661,7 +4607,7 @@ TEST_P(DBTestWithParam, ThreadStatusSingleCompaction) {
|
||||
// The Put Phase.
|
||||
for (int file = 0; file < kNumL0Files; ++file) {
|
||||
for (int key = 0; key < kEntriesPerBuffer; ++key) {
|
||||
ASSERT_OK(Put(std::to_string(key + file * kEntriesPerBuffer),
|
||||
ASSERT_OK(Put(ToString(key + file * kEntriesPerBuffer),
|
||||
rnd.RandomString(kTestValueSize)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
@@ -4812,7 +4758,7 @@ TEST_P(DBTestWithParam, PreShutdownMultipleCompaction) {
|
||||
int operation_count[ThreadStatus::NUM_OP_TYPES] = {0};
|
||||
for (int file = 0; file < 16 * kNumL0Files; ++file) {
|
||||
for (int k = 0; k < kEntriesPerBuffer; ++k) {
|
||||
ASSERT_OK(Put(std::to_string(key++), rnd.RandomString(kTestValueSize)));
|
||||
ASSERT_OK(Put(ToString(key++), rnd.RandomString(kTestValueSize)));
|
||||
}
|
||||
|
||||
ASSERT_OK(env_->GetThreadList(&thread_list));
|
||||
@@ -4899,7 +4845,7 @@ TEST_P(DBTestWithParam, PreShutdownCompactionMiddle) {
|
||||
int operation_count[ThreadStatus::NUM_OP_TYPES] = {0};
|
||||
for (int file = 0; file < 16 * kNumL0Files; ++file) {
|
||||
for (int k = 0; k < kEntriesPerBuffer; ++k) {
|
||||
ASSERT_OK(Put(std::to_string(key++), rnd.RandomString(kTestValueSize)));
|
||||
ASSERT_OK(Put(ToString(key++), rnd.RandomString(kTestValueSize)));
|
||||
}
|
||||
|
||||
ASSERT_OK(env_->GetThreadList(&thread_list));
|
||||
@@ -5210,9 +5156,8 @@ TEST_F(DBTest, DynamicCompactionOptions) {
|
||||
// Writing to 64KB L0 files should trigger a compaction. Since these
|
||||
// 2 L0 files have the same key range, compaction merge them and should
|
||||
// result in 2 32KB L1 files.
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "2"},
|
||||
{"target_file_size_base", std::to_string(k32KB)}}));
|
||||
ASSERT_OK(dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "2"},
|
||||
{"target_file_size_base", ToString(k32KB)}}));
|
||||
|
||||
gen_l0_kb(0, 64, 1);
|
||||
ASSERT_EQ("1,1", FilesPerLevel());
|
||||
@@ -5231,8 +5176,8 @@ TEST_F(DBTest, DynamicCompactionOptions) {
|
||||
// Increase level base size to 256KB and write enough data that will
|
||||
// fill L1 and L2. L1 size should be around 256KB while L2 size should be
|
||||
// around 256KB x 4.
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"max_bytes_for_level_base", std::to_string(k1MB)}}));
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions({{"max_bytes_for_level_base", ToString(k1MB)}}));
|
||||
|
||||
// writing 96 x 64KB => 6 * 1024KB
|
||||
// (L1 + L2) = (1 + 4) * 1024KB
|
||||
@@ -5251,9 +5196,9 @@ TEST_F(DBTest, DynamicCompactionOptions) {
|
||||
// max_bytes_for_level_base. Now, reduce both mulitplier and level base,
|
||||
// After filling enough data that can fit in L1 - L3, we should see L1 size
|
||||
// reduces to 128KB from 256KB which was asserted previously. Same for L2.
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"max_bytes_for_level_multiplier", "2"},
|
||||
{"max_bytes_for_level_base", std::to_string(k128KB)}}));
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions({{"max_bytes_for_level_multiplier", "2"},
|
||||
{"max_bytes_for_level_base", ToString(k128KB)}}));
|
||||
|
||||
// writing 20 x 64KB = 10 x 128KB
|
||||
// (L1 + L2 + L3) = (1 + 2 + 4) * 128KB
|
||||
@@ -5909,7 +5854,7 @@ TEST_P(DBTestWithParam, FilterCompactionTimeTest) {
|
||||
// put some data
|
||||
for (int table = 0; table < 4; ++table) {
|
||||
for (int i = 0; i < 10 + table; ++i) {
|
||||
ASSERT_OK(Put(std::to_string(table * 100 + i), "val"));
|
||||
ASSERT_OK(Put(ToString(table * 100 + i), "val"));
|
||||
++n;
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
@@ -6067,6 +6012,7 @@ TEST_F(DBTest, DISABLED_SuggestCompactRangeTest) {
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBTest, PromoteL0) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -6247,12 +6193,13 @@ TEST_F(DBTest, CompactFilesShouldTriggerAutoCompaction) {
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
port::Thread manual_compaction_thread([&]() {
|
||||
auto s = db_->CompactFiles(CompactionOptions(), db_->DefaultColumnFamily(),
|
||||
input_files, 0);
|
||||
ASSERT_OK(s);
|
||||
auto s = db_->CompactFiles(CompactionOptions(),
|
||||
db_->DefaultColumnFamily(), input_files, 0);
|
||||
ASSERT_OK(s);
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT("DBTest::CompactFilesShouldTriggerAutoCompaction:Begin");
|
||||
TEST_SYNC_POINT(
|
||||
"DBTest::CompactFilesShouldTriggerAutoCompaction:Begin");
|
||||
// generate enough files to trigger compaction
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
@@ -6262,15 +6209,16 @@ TEST_F(DBTest, CompactFilesShouldTriggerAutoCompaction) {
|
||||
}
|
||||
db_->GetColumnFamilyMetaData(db_->DefaultColumnFamily(), &cf_meta_data);
|
||||
ASSERT_GT(cf_meta_data.levels[0].files.size(),
|
||||
options.level0_file_num_compaction_trigger);
|
||||
TEST_SYNC_POINT("DBTest::CompactFilesShouldTriggerAutoCompaction:End");
|
||||
options.level0_file_num_compaction_trigger);
|
||||
TEST_SYNC_POINT(
|
||||
"DBTest::CompactFilesShouldTriggerAutoCompaction:End");
|
||||
|
||||
manual_compaction_thread.join();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
db_->GetColumnFamilyMetaData(db_->DefaultColumnFamily(), &cf_meta_data);
|
||||
ASSERT_LE(cf_meta_data.levels[0].files.size(),
|
||||
options.level0_file_num_compaction_trigger);
|
||||
options.level0_file_num_compaction_trigger);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -6290,7 +6238,7 @@ TEST_F(DBTest, LargeBatchWithColumnFamilies) {
|
||||
(write_size / 1024 / 1024), pass);
|
||||
for (;;) {
|
||||
std::string data(3000, j++ % 127 + 20);
|
||||
data += std::to_string(j);
|
||||
data += ToString(j);
|
||||
ASSERT_OK(batch.Put(handles_[0], Slice(data), Slice(data)));
|
||||
if (batch.GetDataSize() > write_size) {
|
||||
break;
|
||||
@@ -6495,9 +6443,8 @@ class WriteStallListener : public EventListener {
|
||||
MutexLock l(&mutex_);
|
||||
return expected == condition_;
|
||||
}
|
||||
|
||||
private:
|
||||
port::Mutex mutex_;
|
||||
port::Mutex mutex_;
|
||||
WriteStallCondition condition_;
|
||||
};
|
||||
|
||||
@@ -6725,8 +6672,7 @@ TEST_F(DBTest, LastWriteBufferDelay) {
|
||||
sleeping_task.WakeUp();
|
||||
sleeping_task.WaitUntilDone();
|
||||
}
|
||||
#endif // !defined(ROCKSDB_LITE) &&
|
||||
// !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
|
||||
#endif // !defined(ROCKSDB_LITE) && !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
|
||||
|
||||
TEST_F(DBTest, FailWhenCompressionNotSupportedTest) {
|
||||
CompressionType compressions[] = {kZlibCompression, kBZip2Compression,
|
||||
@@ -6811,89 +6757,6 @@ TEST_F(DBTest, PinnableSliceAndRowCache) {
|
||||
1);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, ReusePinnableSlice) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.row_cache = NewLRUCache(8192);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_EQ(Get("foo"), "bar");
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
|
||||
{
|
||||
PinnableSlice pin_slice;
|
||||
ASSERT_EQ(Get("foo", &pin_slice), Status::OK());
|
||||
ASSERT_EQ(Get("foo", &pin_slice), Status::OK());
|
||||
ASSERT_EQ(pin_slice.ToString(), "bar");
|
||||
|
||||
// Entry is already in cache, lookup will remove the element from lru
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
0);
|
||||
}
|
||||
// After PinnableSlice destruction element is added back in LRU
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
|
||||
{
|
||||
std::vector<Slice> multiget_keys;
|
||||
multiget_keys.push_back("foo");
|
||||
std::vector<PinnableSlice> multiget_values(1);
|
||||
std::vector<Status> statuses({Status::NotFound()});
|
||||
ReadOptions ropt;
|
||||
dbfull()->MultiGet(ropt, dbfull()->DefaultColumnFamily(),
|
||||
multiget_keys.size(), multiget_keys.data(),
|
||||
multiget_values.data(), statuses.data());
|
||||
ASSERT_EQ(Status::OK(), statuses[0]);
|
||||
dbfull()->MultiGet(ropt, dbfull()->DefaultColumnFamily(),
|
||||
multiget_keys.size(), multiget_keys.data(),
|
||||
multiget_values.data(), statuses.data());
|
||||
ASSERT_EQ(Status::OK(), statuses[0]);
|
||||
|
||||
// Entry is already in cache, lookup will remove the element from lru
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
0);
|
||||
}
|
||||
// After PinnableSlice destruction element is added back in LRU
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
|
||||
{
|
||||
std::vector<ColumnFamilyHandle*> multiget_cfs;
|
||||
multiget_cfs.push_back(dbfull()->DefaultColumnFamily());
|
||||
std::vector<Slice> multiget_keys;
|
||||
multiget_keys.push_back("foo");
|
||||
std::vector<PinnableSlice> multiget_values(1);
|
||||
std::vector<Status> statuses({Status::NotFound()});
|
||||
ReadOptions ropt;
|
||||
dbfull()->MultiGet(ropt, multiget_keys.size(), multiget_cfs.data(),
|
||||
multiget_keys.data(), multiget_values.data(),
|
||||
statuses.data());
|
||||
ASSERT_EQ(Status::OK(), statuses[0]);
|
||||
dbfull()->MultiGet(ropt, multiget_keys.size(), multiget_cfs.data(),
|
||||
multiget_keys.data(), multiget_values.data(),
|
||||
statuses.data());
|
||||
ASSERT_EQ(Status::OK(), statuses[0]);
|
||||
|
||||
// Entry is already in cache, lookup will remove the element from lru
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
0);
|
||||
}
|
||||
// After PinnableSlice destruction element is added back in LRU
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest, DeletingOldWalAfterDrop) {
|
||||
@@ -6973,7 +6836,9 @@ TEST_F(DBTest, PauseBackgroundWorkTest) {
|
||||
TEST_F(DBTest, ThreadLocalPtrDeadlock) {
|
||||
std::atomic<int> flushes_done{0};
|
||||
std::atomic<int> threads_destroyed{0};
|
||||
auto done = [&] { return flushes_done.load() > 10; };
|
||||
auto done = [&] {
|
||||
return flushes_done.load() > 10;
|
||||
};
|
||||
|
||||
port::Thread flushing_thread([&] {
|
||||
for (int i = 0; !done(); ++i) {
|
||||
@@ -6986,7 +6851,7 @@ TEST_F(DBTest, ThreadLocalPtrDeadlock) {
|
||||
});
|
||||
|
||||
std::vector<port::Thread> thread_spawning_threads(10);
|
||||
for (auto& t : thread_spawning_threads) {
|
||||
for (auto& t: thread_spawning_threads) {
|
||||
t = port::Thread([&] {
|
||||
while (!done()) {
|
||||
{
|
||||
@@ -7002,7 +6867,7 @@ TEST_F(DBTest, ThreadLocalPtrDeadlock) {
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& t : thread_spawning_threads) {
|
||||
for (auto& t: thread_spawning_threads) {
|
||||
t.join();
|
||||
}
|
||||
flushing_thread.join();
|
||||
|
||||
+124
-415
@@ -38,10 +38,10 @@ class DBTest2 : public DBTestBase {
|
||||
#ifndef ROCKSDB_LITE
|
||||
uint64_t GetSstSizeHelper(Temperature temperature) {
|
||||
std::string prop;
|
||||
EXPECT_TRUE(dbfull()->GetProperty(
|
||||
DB::Properties::kLiveSstFilesSizeAtTemperature +
|
||||
std::to_string(static_cast<uint8_t>(temperature)),
|
||||
&prop));
|
||||
EXPECT_TRUE(
|
||||
dbfull()->GetProperty(DB::Properties::kLiveSstFilesSizeAtTemperature +
|
||||
ToString(static_cast<uint8_t>(temperature)),
|
||||
&prop));
|
||||
return static_cast<uint64_t>(std::atoi(prop.c_str()));
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -1296,7 +1296,6 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
enum DictionaryTypes : int {
|
||||
kWithoutDict,
|
||||
kWithDict,
|
||||
kWithZSTDfinalizeDict,
|
||||
kWithZSTDTrainedDict,
|
||||
kDictEnd,
|
||||
};
|
||||
@@ -1305,7 +1304,6 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
options.compression = compression_type;
|
||||
size_t bytes_without_dict = 0;
|
||||
size_t bytes_with_dict = 0;
|
||||
size_t bytes_with_zstd_finalize_dict = 0;
|
||||
size_t bytes_with_zstd_trained_dict = 0;
|
||||
for (int i = kWithoutDict; i < kDictEnd; i++) {
|
||||
// First iteration: compress without preset dictionary
|
||||
@@ -1325,22 +1323,12 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
|
||||
options.compression_opts.zstd_max_train_bytes = 0;
|
||||
break;
|
||||
case kWithZSTDfinalizeDict:
|
||||
if (compression_type != kZSTD ||
|
||||
!ZSTD_FinalizeDictionarySupported()) {
|
||||
continue;
|
||||
}
|
||||
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
|
||||
options.compression_opts.zstd_max_train_bytes = kL0FileBytes;
|
||||
options.compression_opts.use_zstd_dict_trainer = false;
|
||||
break;
|
||||
case kWithZSTDTrainedDict:
|
||||
if (compression_type != kZSTD || !ZSTD_TrainDictionarySupported()) {
|
||||
if (compression_type != kZSTD) {
|
||||
continue;
|
||||
}
|
||||
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
|
||||
options.compression_opts.zstd_max_train_bytes = kL0FileBytes;
|
||||
options.compression_opts.use_zstd_dict_trainer = true;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
@@ -1377,8 +1365,6 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
bytes_without_dict = total_sst_bytes;
|
||||
} else if (i == kWithDict) {
|
||||
bytes_with_dict = total_sst_bytes;
|
||||
} else if (i == kWithZSTDfinalizeDict) {
|
||||
bytes_with_zstd_finalize_dict = total_sst_bytes;
|
||||
} else if (i == kWithZSTDTrainedDict) {
|
||||
bytes_with_zstd_trained_dict = total_sst_bytes;
|
||||
}
|
||||
@@ -1389,13 +1375,6 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
}
|
||||
if (i == kWithDict) {
|
||||
ASSERT_GT(bytes_without_dict, bytes_with_dict);
|
||||
} else if (i == kWithZSTDTrainedDict) {
|
||||
// In zstd compression, it is sometimes possible that using a finalized
|
||||
// dictionary does not get as good a compression ratio as raw content
|
||||
// dictionary. But using a dictionary should always get better
|
||||
// compression ratio than not using one.
|
||||
ASSERT_TRUE(bytes_with_dict > bytes_with_zstd_finalize_dict ||
|
||||
bytes_without_dict > bytes_with_zstd_finalize_dict);
|
||||
} else if (i == kWithZSTDTrainedDict) {
|
||||
// In zstd compression, it is sometimes possible that using a trained
|
||||
// dictionary does not get as good a compression ratio as without
|
||||
@@ -1715,9 +1694,9 @@ class CompactionCompressionListener : public EventListener {
|
||||
int bottommost_level = 0;
|
||||
for (int level = 0; level < db->NumberLevels(); level++) {
|
||||
std::string files_at_level;
|
||||
ASSERT_TRUE(
|
||||
db->GetProperty("rocksdb.num-files-at-level" + std::to_string(level),
|
||||
&files_at_level));
|
||||
ASSERT_TRUE(db->GetProperty(
|
||||
"rocksdb.num-files-at-level" + ROCKSDB_NAMESPACE::ToString(level),
|
||||
&files_at_level));
|
||||
if (files_at_level != "0") {
|
||||
bottommost_level = level;
|
||||
}
|
||||
@@ -2513,14 +2492,14 @@ TEST_F(DBTest2, TestPerfContextIterCpuTime) {
|
||||
|
||||
const size_t kNumEntries = 10;
|
||||
for (size_t i = 0; i < kNumEntries; ++i) {
|
||||
ASSERT_OK(Put("k" + std::to_string(i), "v" + std::to_string(i)));
|
||||
ASSERT_OK(Put("k" + ToString(i), "v" + ToString(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
for (size_t i = 0; i < kNumEntries; ++i) {
|
||||
ASSERT_EQ("v" + std::to_string(i), Get("k" + std::to_string(i)));
|
||||
ASSERT_EQ("v" + ToString(i), Get("k" + ToString(i)));
|
||||
}
|
||||
std::string last_key = "k" + std::to_string(kNumEntries - 1);
|
||||
std::string last_value = "v" + std::to_string(kNumEntries - 1);
|
||||
std::string last_key = "k" + ToString(kNumEntries - 1);
|
||||
std::string last_value = "v" + ToString(kNumEntries - 1);
|
||||
env_->now_cpu_count_.store(0);
|
||||
env_->SetMockSleep();
|
||||
|
||||
@@ -3081,21 +3060,10 @@ TEST_F(DBTest2, PausingManualCompaction1) {
|
||||
int manual_compactions_paused = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:1", [&](void* arg) {
|
||||
auto canceled = static_cast<std::atomic<bool>*>(arg);
|
||||
// CompactRange triggers manual compaction and cancel the compaction
|
||||
// by set *canceled as true
|
||||
if (canceled != nullptr) {
|
||||
canceled->store(true, std::memory_order_release);
|
||||
}
|
||||
manual_compactions_paused += 1;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"TestCompactFiles:PausingManualCompaction:3", [&](void* arg) {
|
||||
auto paused = static_cast<std::atomic<int>*>(arg);
|
||||
// CompactFiles() relies on manual_compactions_paused to
|
||||
// determine if thie compaction should be paused or not
|
||||
ASSERT_EQ(0, paused->load(std::memory_order_acquire));
|
||||
paused->fetch_add(1, std::memory_order_release);
|
||||
manual_compactions_paused += 1;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -3160,7 +3128,7 @@ TEST_F(DBTest2, PausingManualCompaction2) {
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// Generate a file containing 100 keys.
|
||||
// Generate a file containing 10 keys.
|
||||
for (int j = 0; j < 100; j++) {
|
||||
ASSERT_OK(Put(Key(j), rnd.RandomString(50)));
|
||||
}
|
||||
@@ -3259,21 +3227,10 @@ TEST_F(DBTest2, PausingManualCompaction4) {
|
||||
int run_manual_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:2", [&](void* arg) {
|
||||
auto canceled = static_cast<std::atomic<bool>*>(arg);
|
||||
// CompactRange triggers manual compaction and cancel the compaction
|
||||
// by set *canceled as true
|
||||
if (canceled != nullptr) {
|
||||
canceled->store(true, std::memory_order_release);
|
||||
}
|
||||
run_manual_compactions++;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"TestCompactFiles:PausingManualCompaction:3", [&](void* arg) {
|
||||
auto paused = static_cast<std::atomic<int>*>(arg);
|
||||
// CompactFiles() relies on manual_compactions_paused to
|
||||
// determine if thie compaction should be paused or not
|
||||
ASSERT_EQ(0, paused->load(std::memory_order_acquire));
|
||||
paused->fetch_add(1, std::memory_order_release);
|
||||
run_manual_compactions++;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -3288,6 +3245,7 @@ TEST_F(DBTest2, PausingManualCompaction4) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:2");
|
||||
dbfull()->EnableManualCompaction();
|
||||
ASSERT_OK(dbfull()->CompactRange(compact_options, nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -3536,9 +3494,8 @@ TEST_F(DBTest2, CancelManualCompactionWithListener) {
|
||||
"CompactionJob::FinishCompactionOutputFile1",
|
||||
[&](void* /*arg*/) { running_compaction++; });
|
||||
|
||||
// Case I: 1 Notify begin compaction, 2 Set *canceled as true to disable
|
||||
// manual compaction in the callback function, 3 Compaction not run,
|
||||
// 4 Notify compaction end.
|
||||
// Case I: 1 Notify begin compaction, 2 DisableManualCompaction, 3 Compaction
|
||||
// not run, 4 Notify compaction end.
|
||||
listener->code_ = Status::kIncomplete;
|
||||
listener->subcode_ = Status::SubCode::kManualCompactionPaused;
|
||||
|
||||
@@ -3555,9 +3512,8 @@ TEST_F(DBTest2, CancelManualCompactionWithListener) {
|
||||
listener->num_compaction_started_ = 0;
|
||||
listener->num_compaction_ended_ = 0;
|
||||
|
||||
// Case II: 1 Set *canceled as true in the callback function to disable manual
|
||||
// compaction, 2 Notify begin compaction (return without notifying), 3 Notify
|
||||
// compaction end (return without notifying).
|
||||
// Case II: 1 DisableManualCompaction, 2 Notify begin compaction (return
|
||||
// without notifying), 3 Notify compaction end (return without notifying).
|
||||
ASSERT_TRUE(dbfull()
|
||||
->CompactRange(compact_options, nullptr, nullptr)
|
||||
.IsManualCompactionPaused());
|
||||
@@ -3568,8 +3524,8 @@ TEST_F(DBTest2, CancelManualCompactionWithListener) {
|
||||
ASSERT_EQ(running_compaction, 0);
|
||||
|
||||
// Case III: 1 Notify begin compaction, 2 Compaction in between
|
||||
// 3. Set *canceled as true in the callback function to disable manual
|
||||
// compaction, 4 Notify compaction end.
|
||||
// 3. DisableManualCompaction, , 4 Notify compaction end.
|
||||
// compact_options.canceled->store(false, std::memory_order_release);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"CompactionIterator:ProcessKV");
|
||||
|
||||
@@ -4031,14 +3987,12 @@ TEST_F(DBTest2, RateLimitedCompactionReads) {
|
||||
|
||||
// should be slightly above 512KB due to non-data blocks read. Arbitrarily
|
||||
// chose 1MB as the upper bound on the total bytes read.
|
||||
size_t rate_limited_bytes = static_cast<size_t>(
|
||||
options.rate_limiter->GetTotalBytesThrough(Env::IO_TOTAL));
|
||||
// The charges can exist for `IO_LOW` and `IO_USER` priorities.
|
||||
size_t rate_limited_bytes_by_pri =
|
||||
options.rate_limiter->GetTotalBytesThrough(Env::IO_LOW) +
|
||||
options.rate_limiter->GetTotalBytesThrough(Env::IO_USER);
|
||||
size_t rate_limited_bytes =
|
||||
options.rate_limiter->GetTotalBytesThrough(Env::IO_TOTAL);
|
||||
// There must be no charges at non-`IO_LOW` priorities.
|
||||
ASSERT_EQ(rate_limited_bytes,
|
||||
static_cast<size_t>(rate_limited_bytes_by_pri));
|
||||
static_cast<size_t>(
|
||||
options.rate_limiter->GetTotalBytesThrough(Env::IO_LOW)));
|
||||
// Include the explicit prefetch of the footer in direct I/O case.
|
||||
size_t direct_io_extra = use_direct_io ? 512 * 1024 : 0;
|
||||
ASSERT_GE(
|
||||
@@ -4056,11 +4010,9 @@ TEST_F(DBTest2, RateLimitedCompactionReads) {
|
||||
}
|
||||
delete iter;
|
||||
// bytes read for user iterator shouldn't count against the rate limit.
|
||||
rate_limited_bytes_by_pri =
|
||||
options.rate_limiter->GetTotalBytesThrough(Env::IO_LOW) +
|
||||
options.rate_limiter->GetTotalBytesThrough(Env::IO_USER);
|
||||
ASSERT_EQ(rate_limited_bytes,
|
||||
static_cast<size_t>(rate_limited_bytes_by_pri));
|
||||
static_cast<size_t>(
|
||||
options.rate_limiter->GetTotalBytesThrough(Env::IO_LOW)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5601,7 +5553,7 @@ TEST_F(DBTest2, MultiDBParallelOpenTest) {
|
||||
Options options = CurrentOptions();
|
||||
std::vector<std::string> dbnames;
|
||||
for (int i = 0; i < kNumDbs; ++i) {
|
||||
dbnames.emplace_back(test::PerThreadDBPath(env_, "db" + std::to_string(i)));
|
||||
dbnames.emplace_back(test::PerThreadDBPath(env_, "db" + ToString(i)));
|
||||
ASSERT_OK(DestroyDB(dbnames.back(), options));
|
||||
}
|
||||
|
||||
@@ -6353,247 +6305,115 @@ TEST_F(DBTest2, BlockBasedTablePrefixGetIndexNotFound) {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
do {
|
||||
// create a DB with block prefix index
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options =
|
||||
*options.table_factory->GetOptions<BlockBasedTableOptions>();
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.statistics = CreateDBStatistics();
|
||||
// create a DB with block prefix index
|
||||
BlockBasedTableOptions table_options;
|
||||
Options options = CurrentOptions();
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
Reopen(options);
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
std::string large_value = rnd.RandomString(500);
|
||||
Random rnd(301);
|
||||
std::string large_value = rnd.RandomString(500);
|
||||
|
||||
ASSERT_OK(Put("a1", large_value));
|
||||
ASSERT_OK(Put("x1", large_value));
|
||||
ASSERT_OK(Put("y1", large_value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("a1", large_value));
|
||||
ASSERT_OK(Put("x1", large_value));
|
||||
ASSERT_OK(Put("y1", large_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = false;
|
||||
ro.auto_prefix_mode = true;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = false;
|
||||
ro.auto_prefix_mode = true;
|
||||
{
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("x1", iterator->key().ToString());
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
|
||||
const auto stat = BLOOM_FILTER_PREFIX_CHECKED;
|
||||
{
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("x1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
std::string ub_str = "b9";
|
||||
Slice ub(ub_str);
|
||||
ro.iterate_upper_bound = &ub;
|
||||
|
||||
Slice ub;
|
||||
{
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
|
||||
ub_str = "z";
|
||||
ub = Slice(ub_str);
|
||||
{
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("x1", iterator->key().ToString());
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
|
||||
ub_str = "c";
|
||||
ub = Slice(ub_str);
|
||||
{
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
|
||||
// The same queries without recreating iterator
|
||||
{
|
||||
ub_str = "b9";
|
||||
ub = Slice(ub_str);
|
||||
ro.iterate_upper_bound = &ub;
|
||||
|
||||
ub = "b9";
|
||||
{
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
ASSERT_EQ(3, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
ASSERT_OK(iterator->status());
|
||||
|
||||
ub = "z";
|
||||
{
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("x1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
ub_str = "z";
|
||||
ub = Slice(ub_str);
|
||||
|
||||
ub = "c";
|
||||
{
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
iterator->Seek("b1");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("x1", iterator->key().ToString());
|
||||
ASSERT_EQ(3, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
|
||||
ub = "c1";
|
||||
{
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
ub_str = "c";
|
||||
ub = Slice(ub_str);
|
||||
|
||||
// The same queries without recreating iterator
|
||||
{
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
ASSERT_EQ(4, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
|
||||
ub = "b9";
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
ub_str = "b9";
|
||||
ub = Slice(ub_str);
|
||||
ro.iterate_upper_bound = &ub;
|
||||
iterator->SeekForPrev("b1");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("a1", iterator->key().ToString());
|
||||
ASSERT_EQ(4, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
|
||||
ub = "z";
|
||||
iterator->Seek("b1");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("x1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
ub_str = "zz";
|
||||
ub = Slice(ub_str);
|
||||
ro.iterate_upper_bound = &ub;
|
||||
iterator->SeekToLast();
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("y1", iterator->key().ToString());
|
||||
|
||||
ub = "c";
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
|
||||
ub = "b9";
|
||||
iterator->SeekForPrev("b1");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("a1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
|
||||
ub = "zz";
|
||||
iterator->SeekToLast();
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("y1", iterator->key().ToString());
|
||||
|
||||
iterator->SeekToFirst();
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("a1", iterator->key().ToString());
|
||||
}
|
||||
|
||||
// Similar, now with reverse comparator
|
||||
// Technically, we are violating axiom 2 of prefix_extractors, but
|
||||
// it should be revised because of major use-cases using
|
||||
// ReverseBytewiseComparator with capped/fixed prefix Seek. (FIXME)
|
||||
options.comparator = ReverseBytewiseComparator();
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("a1", large_value));
|
||||
ASSERT_OK(Put("x1", large_value));
|
||||
ASSERT_OK(Put("y1", large_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
{
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
|
||||
ub = "b1";
|
||||
iterator->Seek("b9");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
|
||||
ub = "b1";
|
||||
iterator->Seek("z");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("y1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
|
||||
ub = "b1";
|
||||
iterator->Seek("c");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
|
||||
ub = "b";
|
||||
iterator->Seek("c9");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
// Fails if ReverseBytewiseComparator::IsSameLengthImmediateSuccessor
|
||||
// is "correctly" implemented.
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
|
||||
ub = "a";
|
||||
iterator->Seek("b9");
|
||||
// Fails if ReverseBytewiseComparator::IsSameLengthImmediateSuccessor
|
||||
// is "correctly" implemented.
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("a1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
|
||||
ub = "b";
|
||||
iterator->Seek("a");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
// Fails if ReverseBytewiseComparator::IsSameLengthImmediateSuccessor
|
||||
// matches BytewiseComparator::IsSameLengthImmediateSuccessor. Upper
|
||||
// comparing before seek key prevents a real bug from surfacing.
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
|
||||
ub = "b1";
|
||||
iterator->SeekForPrev("b9");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
// Fails if ReverseBytewiseComparator::IsSameLengthImmediateSuccessor
|
||||
// is "correctly" implemented.
|
||||
ASSERT_EQ("x1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
|
||||
ub = "a";
|
||||
iterator->SeekToLast();
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("a1", iterator->key().ToString());
|
||||
|
||||
iterator->SeekToFirst();
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("y1", iterator->key().ToString());
|
||||
}
|
||||
|
||||
// Now something a bit different, related to "short" keys that
|
||||
// auto_prefix_mode can omit. See "BUG" section of auto_prefix_mode.
|
||||
options.comparator = BytewiseComparator();
|
||||
for (const auto config : {"fixed:2", "capped:2"}) {
|
||||
ASSERT_OK(SliceTransform::CreateFromString(ConfigOptions(), config,
|
||||
&options.prefix_extractor));
|
||||
|
||||
// FIXME: kHashSearch, etc. requires all keys be InDomain
|
||||
if (StartsWith(config, "fixed") &&
|
||||
(table_options.index_type == BlockBasedTableOptions::kHashSearch ||
|
||||
StartsWith(options.memtable_factory->Name(), "Hash"))) {
|
||||
continue;
|
||||
}
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const char* a_end_stuff = "a\xffXYZ";
|
||||
const char* b_begin_stuff = "b\x00XYZ";
|
||||
ASSERT_OK(Put("a", large_value));
|
||||
ASSERT_OK(Put("b", large_value));
|
||||
ASSERT_OK(Put(Slice(b_begin_stuff, 3), large_value));
|
||||
ASSERT_OK(Put("c", large_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// control showing valid optimization with auto_prefix mode
|
||||
ub = Slice(a_end_stuff, 4);
|
||||
ro.iterate_upper_bound = &ub;
|
||||
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek(Slice(a_end_stuff, 2));
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
|
||||
// test, cannot be validly optimized with auto_prefix_mode
|
||||
ub = Slice(b_begin_stuff, 2);
|
||||
ro.iterate_upper_bound = &ub;
|
||||
|
||||
iterator->Seek(Slice(a_end_stuff, 2));
|
||||
// !!! BUG !!! See "BUG" section of auto_prefix_mode.
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
|
||||
// To prove that is the wrong result, now use total order seek
|
||||
ReadOptions tos_ro = ro;
|
||||
tos_ro.total_order_seek = true;
|
||||
tos_ro.auto_prefix_mode = false;
|
||||
iterator.reset(db_->NewIterator(tos_ro));
|
||||
iterator->Seek(Slice(a_end_stuff, 2));
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("b", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
} while (ChangeOptions(kSkipPlainTable));
|
||||
iterator->SeekToFirst();
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("a1", iterator->key().ToString());
|
||||
}
|
||||
}
|
||||
|
||||
class RenameCurrentTest : public DBTestBase,
|
||||
@@ -7035,11 +6855,6 @@ TEST_F(DBTest2, BottommostTemperatureUniversal) {
|
||||
ASSERT_EQ(size, 0);
|
||||
size = GetSstSizeHelper(Temperature::kCold);
|
||||
ASSERT_GT(size, 0);
|
||||
|
||||
// kLastTemperature is an invalid temperature
|
||||
options.bottommost_temperature = Temperature::kLastTemperature;
|
||||
s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, LastLevelStatistics) {
|
||||
@@ -7330,112 +7145,6 @@ TEST_F(DBTest2, RenameDirectory) {
|
||||
dbname_ = old_dbname;
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, SstUniqueIdVerifyBackwardCompatible) {
|
||||
const int kNumSst = 3;
|
||||
const int kLevel0Trigger = 4;
|
||||
auto options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kLevel0Trigger;
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
// Existing manifest doesn't have unique id
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionEdit::EncodeTo:UniqueId", [&](void* arg) {
|
||||
auto unique_id = static_cast<UniqueId64x2*>(arg);
|
||||
// remove id before writing it to manifest
|
||||
(*unique_id)[0] = 0;
|
||||
(*unique_id)[1] = 0;
|
||||
});
|
||||
std::atomic_int skipped = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack("Version::VerifySstUniqueIds::Skipped",
|
||||
[&](void* /*arg*/) { skipped++; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// generate a few SSTs
|
||||
for (int i = 0; i < kNumSst; i++) {
|
||||
for (int j = 0; j < 100; j++) {
|
||||
ASSERT_OK(Put(Key(i * 10 + j), "value"));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
// Reopen without verification
|
||||
Reopen(options);
|
||||
|
||||
// Reopen with verification, but it's skipped because manifest doesn't have id
|
||||
options.verify_sst_unique_id_in_manifest = true;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(skipped, kNumSst);
|
||||
|
||||
// test compaction generated Sst
|
||||
for (int i = kNumSst; i < kLevel0Trigger; i++) {
|
||||
for (int j = 0; j < 100; j++) {
|
||||
ASSERT_OK(Put(Key(i * 10 + j), "value"));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Reopen with verification should fail
|
||||
options.verify_sst_unique_id_in_manifest = true;
|
||||
skipped = 0;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(skipped, 1);
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, SstUniqueIdVerify) {
|
||||
const int kNumSst = 3;
|
||||
const int kLevel0Trigger = 4;
|
||||
auto options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kLevel0Trigger;
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"PropertyBlockBuilder::AddTableProperty:Start", [&](void* props_vs) {
|
||||
auto props = static_cast<TableProperties*>(props_vs);
|
||||
// update table property session_id to a different one
|
||||
props->db_session_id = DBImpl::GenerateDbSessionId(nullptr);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// generate a few SSTs
|
||||
for (int i = 0; i < kNumSst; i++) {
|
||||
for (int j = 0; j < 100; j++) {
|
||||
ASSERT_OK(Put(Key(i * 10 + j), "value"));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
// Reopen with verification should report corruption
|
||||
options.verify_sst_unique_id_in_manifest = true;
|
||||
auto s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
|
||||
// Reopen without verification should be fine
|
||||
options.verify_sst_unique_id_in_manifest = false;
|
||||
Reopen(options);
|
||||
|
||||
// test compaction generated Sst
|
||||
for (int i = kNumSst; i < kLevel0Trigger; i++) {
|
||||
for (int j = 0; j < 100; j++) {
|
||||
ASSERT_OK(Put(Key(i * 10 + j), "value"));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Reopen with verification should fail
|
||||
options.verify_sst_unique_id_in_manifest = true;
|
||||
s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest2, GetLatestSeqAndTsForKey) {
|
||||
Destroy(last_options_);
|
||||
|
||||
Loaded 100 of 498 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user