mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 03:22:34 -05:00
Compare commits
47
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
110285073c | ||
|
|
59d972f58a | ||
|
|
565fcead22 | ||
|
|
3b6dc049f7 | ||
|
|
ca0ef54f16 | ||
|
|
27d6ef8e60 | ||
|
|
c967436453 | ||
|
|
4a9ae4f713 | ||
|
|
36aec94d85 | ||
|
|
9b8b8b1504 | ||
|
|
785b804a9a | ||
|
|
7574841aac | ||
|
|
dab19afe56 | ||
|
|
f20b674796 | ||
|
|
ce60d0cbe5 | ||
|
|
fc61e98ae6 | ||
|
|
4a776d81cc | ||
|
|
3362a730dc | ||
|
|
67542bfab5 | ||
|
|
659a16d52b | ||
|
|
73fd589b1a | ||
|
|
926ee13811 | ||
|
|
db8647969d | ||
|
|
d74468e348 | ||
|
|
ff8763c187 | ||
|
|
af6cb50bc4 | ||
|
|
33742c2a9f | ||
|
|
3edbeeaa50 | ||
|
|
9e9e3d16b9 | ||
|
|
d95e13e9cc | ||
|
|
e3ef41b02f | ||
|
|
06c8afeff5 | ||
|
|
7d7e88c7d1 | ||
|
|
0a89cea5f5 | ||
|
|
ddb7620a61 | ||
|
|
9983eecdfb | ||
|
|
9ed96703d1 | ||
|
|
87a8b3c8af | ||
|
|
21345d2823 | ||
|
|
f706a9c199 | ||
|
|
6f12599863 | ||
|
|
8ca433f912 | ||
|
|
96978e4d96 | ||
|
|
d795a730be | ||
|
|
ba65cfff63 | ||
|
|
3379d1466f | ||
|
|
7ae4da924a |
No files matched your search
+87
-71
@@ -87,7 +87,6 @@ commands:
|
||||
- run:
|
||||
name: Install Clang 13
|
||||
command: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
echo "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main" | sudo tee -a /etc/apt/sources.list
|
||||
echo "APT::Acquire::Retries \"10\";" | sudo tee -a /etc/apt/apt.conf.d/80-retries # llvm.org unreliable
|
||||
@@ -171,19 +170,42 @@ jobs:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-gflags-on-macos
|
||||
- pre-steps-macos
|
||||
- run: ulimit -S -n 1048576 && OPT=-DCIRCLECI make V=1 J=32 -j32 check
|
||||
- run: ulimit -S -n 1048576 && OPT=-DCIRCLECI make V=1 J=32 -j32 all
|
||||
- post-steps
|
||||
|
||||
build-macos-cmake:
|
||||
macos:
|
||||
xcode: 12.5.1
|
||||
resource_class: large
|
||||
parameters:
|
||||
run_even_tests:
|
||||
description: run even or odd tests, used to split tests to 2 groups
|
||||
type: boolean
|
||||
default: true
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-cmake-on-macos
|
||||
- install-gflags-on-macos
|
||||
- pre-steps-macos
|
||||
- run: ulimit -S -n 1048576 && (mkdir build && cd build && cmake -DWITH_GFLAGS=1 .. && make V=1 -j32 && ctest -j10)
|
||||
- run:
|
||||
name: "cmake generate project file"
|
||||
command: ulimit -S -n 1048576 && mkdir build && cd build && cmake -DWITH_GFLAGS=1 ..
|
||||
- run:
|
||||
name: "Build tests"
|
||||
command: cd build && make V=1 -j32
|
||||
- when:
|
||||
condition: << parameters.run_even_tests >>
|
||||
steps:
|
||||
- run:
|
||||
name: "Run even tests"
|
||||
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 1048576 && cd build && ctest -j32 -I 1,,2
|
||||
- post-steps
|
||||
|
||||
build-linux:
|
||||
@@ -219,38 +241,38 @@ jobs:
|
||||
build-linux-release:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: large
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: make V=1 -j8 release
|
||||
- run: make V=1 -j32 release
|
||||
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
|
||||
- install-gflags
|
||||
- run: make V=1 -j8 release
|
||||
- run: make V=1 -j32 release
|
||||
- run: ./db_stress --version # ensure with gflags
|
||||
- post-steps
|
||||
|
||||
build-linux-release-rtti:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: large
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: make clean
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j8 static_lib tools db_bench
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j16 static_lib tools db_bench
|
||||
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
|
||||
- run: sudo apt-get update -y && sudo apt-get install -y libgflags-dev
|
||||
- run: make clean
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j8 static_lib tools db_bench
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j16 static_lib tools db_bench
|
||||
- run: ./db_stress --version # ensure with gflags
|
||||
|
||||
build-linux-lite:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: 2xlarge
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: LITE=1 make V=1 J=32 -j32 check
|
||||
- run: LITE=1 make V=1 J=8 -j8 check
|
||||
- post-steps
|
||||
|
||||
build-linux-lite-release:
|
||||
@@ -291,11 +313,30 @@ jobs:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: 2xlarge
|
||||
# find test list by `make list_all_tests`
|
||||
parameters:
|
||||
start_test:
|
||||
default: ""
|
||||
type: string
|
||||
end_test:
|
||||
default: ""
|
||||
type: string
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-clang-10
|
||||
- run: COMPILE_WITH_TSAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check # aligned new doesn't work for reason we haven't figured out.
|
||||
- install-gtest-parallel
|
||||
- run:
|
||||
name: "Build unit tests"
|
||||
command: |
|
||||
echo "env: $(env)"
|
||||
ROCKSDBTESTS_START=<<parameters.start_test>> ROCKSDBTESTS_END=<<parameters.end_test>> ROCKSDBTESTS_SUBSET_TESTS_TO_FILE=/tmp/test_list COMPILE_WITH_TSAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 --output-sync=target build_subset_tests
|
||||
- run:
|
||||
name: "Run unit tests in parallel"
|
||||
command: |
|
||||
sed -i 's/[[:space:]]*$//; s/ / \.\//g; s/.*/.\/&/' /tmp/test_list
|
||||
cat /tmp/test_list
|
||||
gtest-parallel $(</tmp/test_list) --output_dir=/tmp | cat # pipe to cat to continuously output status on circleci UI. Otherwise, no status will be printed while the job is running.
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-ubsan:
|
||||
@@ -367,11 +408,11 @@ jobs:
|
||||
build-linux-gcc-8-no_test_run:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: large
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test && sudo apt-get update -y && sudo apt-get install gcc-8 g++-8 libgflags-dev
|
||||
- run: CC=gcc-8 CXX=g++-8 V=1 make -j8 all
|
||||
- run: CC=gcc-8 CXX=g++-8 V=1 make -j16 all
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-10-cxx20-no_test_run:
|
||||
@@ -381,7 +422,7 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: sudo apt-get update -y && sudo apt-get install gcc-10 g++-10 libgflags-dev
|
||||
- run: CC=gcc-10 CXX=g++-10 V=1 SKIP_LINK=1 ROCKSDB_CXX_STANDARD=c++20 make -j16 all # Linking broken because libgflags compiled with newer ABI
|
||||
- run: CC=gcc-10 CXX=g++-10 V=1 ROCKSDB_CXX_STANDARD=c++20 make -j16 all
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-11-no_test_run:
|
||||
@@ -390,8 +431,9 @@ jobs:
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-benchmark
|
||||
- run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test && sudo apt-get update -y && sudo apt-get install gcc-11 g++-11 libgflags-dev
|
||||
- run: CC=gcc-11 CXX=g++-11 V=1 SKIP_LINK=1 make -j16 all # Linking broken because libgflags compiled with newer ABI
|
||||
- run: CC=gcc-11 CXX=g++-11 V=1 make -j16 all microbench
|
||||
- post-steps
|
||||
|
||||
build-linux-clang-13-no_test_run:
|
||||
@@ -401,18 +443,19 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-clang-13
|
||||
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j16 all
|
||||
- install-benchmark
|
||||
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j16 all microbench
|
||||
- post-steps
|
||||
|
||||
# This job is only to make sure the microbench tests are able to run, the benchmark result is not meaningful as the CI host is changing.
|
||||
build-linux-microbench:
|
||||
build-linux-run-microbench:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: xlarge
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-benchmark
|
||||
- run: DEBUG_LEVEL=0 make microbench
|
||||
- run: DEBUG_LEVEL=0 make -j32 run_microbench
|
||||
- post-steps
|
||||
|
||||
build-windows:
|
||||
@@ -476,7 +519,7 @@ jobs:
|
||||
${CMAKE_BIN} -G "${CMAKE_GENERATOR}" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DJNI=1 << parameters.extra_cmake_opt >> ..
|
||||
cd ..
|
||||
echo "Building with VS version: ${CMAKE_GENERATOR}"
|
||||
msbuild.exe build/rocksdb.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
msbuild.exe build/rocksdb.sln -maxCpuCount:64 -property:Configuration=Debug -property:Platform=x64
|
||||
- run:
|
||||
name: "Test RocksDB"
|
||||
shell: powershell.exe
|
||||
@@ -531,7 +574,7 @@ jobs:
|
||||
build-macos-java:
|
||||
macos:
|
||||
xcode: 12.5.1
|
||||
resource_class: medium
|
||||
resource_class: large
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1 # jemalloc causes java 8 crash
|
||||
@@ -549,16 +592,16 @@ jobs:
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Shared Library"
|
||||
command: make V=1 J=8 -j8 rocksdbjava
|
||||
command: make V=1 J=16 -j16 rocksdbjava
|
||||
- run:
|
||||
name: "Test RocksDBJava"
|
||||
command: make V=1 J=8 -j8 jtest
|
||||
command: make V=1 J=16 -j16 jtest
|
||||
- post-steps
|
||||
|
||||
build-macos-java-static:
|
||||
macos:
|
||||
xcode: 12.5.1
|
||||
resource_class: medium
|
||||
resource_class: large
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
|
||||
steps:
|
||||
@@ -576,13 +619,13 @@ jobs:
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava x86 and ARM Static Libraries"
|
||||
command: make V=1 J=8 -j8 rocksdbjavastaticosx
|
||||
command: make V=1 J=16 -j16 rocksdbjavastaticosx
|
||||
- post-steps
|
||||
|
||||
build-macos-java-static-universal:
|
||||
macos:
|
||||
xcode: 12.5.1
|
||||
resource_class: medium
|
||||
resource_class: large
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
|
||||
steps:
|
||||
@@ -600,7 +643,7 @@ jobs:
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Universal Binary Static Library"
|
||||
command: make V=1 J=8 -j8 rocksdbjavastaticosx_ub
|
||||
command: make V=1 J=16 -j16 rocksdbjavastaticosx_ub
|
||||
- post-steps
|
||||
|
||||
build-examples:
|
||||
@@ -619,7 +662,7 @@ jobs:
|
||||
build-cmake-mingw:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: 2xlarge
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
@@ -643,30 +686,12 @@ jobs:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: 2xlarge
|
||||
parameters:
|
||||
start_test:
|
||||
default: ""
|
||||
type: string
|
||||
end_test:
|
||||
default: ""
|
||||
type: string
|
||||
environment:
|
||||
TEST_TMPDIR: /tmp/rocksdb_test_tmp
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-gtest-parallel
|
||||
- run:
|
||||
name: "Build unit tests"
|
||||
command: |
|
||||
echo "env: $(env)"
|
||||
echo "** done env"
|
||||
ROCKSDBTESTS_START=<<parameters.start_test>> ROCKSDBTESTS_END=<<parameters.end_test>> ROCKSDBTESTS_SUBSET_TESTS_TO_FILE=/tmp/test_list make V=1 -j32 --output-sync=target build_subset_tests
|
||||
- run:
|
||||
name: "Run unit tests in parallel"
|
||||
command: |
|
||||
sed -i 's/[[:space:]]*$//; s/ / \.\//g; s/.*/.\/&/' /tmp/test_list
|
||||
cat /tmp/test_list
|
||||
export TEST_TMPDIR=/tmp/rocksdb_test_tmp
|
||||
gtest-parallel $(</tmp/test_list) --output_dir=/tmp | cat # pipe to cat to continuously output status on circleci UI. Otherwise, no status will be printed while the job is running.
|
||||
- run: make V=1 -j32 check
|
||||
- post-steps
|
||||
|
||||
build-linux-arm-test-full:
|
||||
@@ -788,7 +813,12 @@ workflows:
|
||||
- build-linux-clang10-asan
|
||||
build-linux-clang10-mini-tsan:
|
||||
jobs:
|
||||
- build-linux-clang10-mini-tsan
|
||||
- build-linux-clang10-mini-tsan:
|
||||
start_test: ""
|
||||
end_test: "env_test"
|
||||
- build-linux-clang10-mini-tsan:
|
||||
start_test: "env_test"
|
||||
end_test: ""
|
||||
build-linux-clang10-ubsan:
|
||||
jobs:
|
||||
- build-linux-clang10-ubsan
|
||||
@@ -823,20 +853,6 @@ workflows:
|
||||
build-examples:
|
||||
jobs:
|
||||
- build-examples
|
||||
build-linux-non-shm:
|
||||
jobs:
|
||||
- build-linux-non-shm:
|
||||
start_test: ""
|
||||
end_test: "db_options_test" # make sure unique in src.mk
|
||||
- build-linux-non-shm:
|
||||
start_test: "db_options_test" # make sure unique in src.mk
|
||||
end_test: "filename_test" # make sure unique in src.mk
|
||||
- build-linux-non-shm:
|
||||
start_test: "filename_test" # make sure unique in src.mk
|
||||
end_test: "statistics_test" # make sure unique in src.mk
|
||||
- build-linux-non-shm:
|
||||
start_test: "statistics_test" # make sure unique in src.mk
|
||||
end_test: ""
|
||||
build-linux-compilers-no_test_run:
|
||||
jobs:
|
||||
- build-linux-clang-no_test_run
|
||||
@@ -849,18 +865,16 @@ workflows:
|
||||
build-macos:
|
||||
jobs:
|
||||
- build-macos
|
||||
build-macos-cmake:
|
||||
jobs:
|
||||
- build-macos-cmake
|
||||
- build-macos-cmake:
|
||||
run_even_tests: true
|
||||
- build-macos-cmake:
|
||||
run_even_tests: false
|
||||
build-cmake-mingw:
|
||||
jobs:
|
||||
- build-cmake-mingw
|
||||
build-linux-arm:
|
||||
jobs:
|
||||
- build-linux-arm
|
||||
build-microbench:
|
||||
jobs:
|
||||
- build-linux-microbench
|
||||
build-fuzzers:
|
||||
jobs:
|
||||
- build-fuzzers
|
||||
@@ -875,3 +889,5 @@ workflows:
|
||||
jobs:
|
||||
- build-format-compatible
|
||||
- build-linux-arm-test-full
|
||||
- build-linux-run-microbench
|
||||
- build-linux-non-shm
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
- name: Download clang-format-diff.py
|
||||
uses: wei/wget@v1
|
||||
with:
|
||||
args: https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py
|
||||
args: https://raw.githubusercontent.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
|
||||
|
||||
- name: Check format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
|
||||
+10
-8
@@ -321,8 +321,7 @@ if(NOT MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
|
||||
if (NOT PORTABLE OR FORCE_SSE42)
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
#include <wmmintrin.h>
|
||||
@@ -334,12 +333,11 @@ int main() {
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
}
|
||||
" HAVE_SSE42)
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
add_definitions(-DHAVE_PCLMUL)
|
||||
elseif(FORCE_SSE42)
|
||||
message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
|
||||
endif()
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
add_definitions(-DHAVE_PCLMUL)
|
||||
elseif(FORCE_SSE42)
|
||||
message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
|
||||
endif()
|
||||
|
||||
# Check if -latomic is required or not
|
||||
@@ -629,6 +627,7 @@ set(SOURCES
|
||||
cache/cache_reservation_manager.cc
|
||||
cache/clock_cache.cc
|
||||
cache/lru_cache.cc
|
||||
cache/lru_secondary_cache.cc
|
||||
cache/sharded_cache.cc
|
||||
db/arena_wrapped_db_iter.cc
|
||||
db/blob/blob_fetcher.cc
|
||||
@@ -831,6 +830,7 @@ set(SOURCES
|
||||
util/coding.cc
|
||||
util/compaction_job_stats_impl.cc
|
||||
util/comparator.cc
|
||||
util/compression.cc
|
||||
util/compression_context_cache.cc
|
||||
util/concurrent_task_limiter_impl.cc
|
||||
util/crc32c.cc
|
||||
@@ -1156,6 +1156,7 @@ if(WITH_TESTS)
|
||||
cache/cache_reservation_manager_test.cc
|
||||
cache/cache_test.cc
|
||||
cache/lru_cache_test.cc
|
||||
cache/lru_secondary_cache_test.cc
|
||||
db/blob/blob_counting_iterator_test.cc
|
||||
db/blob/blob_file_addition_test.cc
|
||||
db/blob/blob_file_builder_test.cc
|
||||
@@ -1333,6 +1334,7 @@ if(WITH_TESTS)
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
utilities/transactions/transaction_test.cc
|
||||
utilities/transactions/lock/point/point_lock_manager_test.cc
|
||||
utilities/transactions/write_committed_transaction_ts_test.cc
|
||||
utilities/transactions/write_prepared_transaction_test.cc
|
||||
utilities/transactions/write_unprepared_transaction_test.cc
|
||||
utilities/transactions/lock/range/range_locking_test.cc
|
||||
|
||||
+16
-22
@@ -1,29 +1,23 @@
|
||||
# Rocksdb Change Log
|
||||
## 7.0.4 (03/29/2022)
|
||||
## Unreleased
|
||||
### New Features
|
||||
* Allow WriteBatchWithIndex to index a WriteBatch that includes keys with user-defined timestamps. The index itself does not have timestamp.
|
||||
* Add support for user-defined timestamps to write-committed transaction without API change. The `TransactionDB` layer APIs do not allow timestamps because we require that all user-defined-timestamps-aware operations go through the `Transaction` APIs.
|
||||
* Added BlobDB options to `ldb`
|
||||
* `BlockBasedTableOptions::detect_filter_construct_corruption` can now be dynamically configured using `DB::SetOptions`.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a race condition when disable and re-enable manual compaction.
|
||||
* Fixed a race condition for `alive_log_files_` in non-two-write-queues mode. The race is between the write_thread_ in WriteToWAL() and another thread executing `FindObsoleteFiles()`. The race condition will be caught if `__glibcxx_requires_nonempty` is enabled.
|
||||
* Fixed a race condition when mmaping a WritableFile on POSIX.
|
||||
* Fixed a race condition when 2PC is disabled and WAL tracking in the MANIFEST is enabled. The race condition is between two background flush threads trying to install flush results, causing a WAL deletion not tracked in the MANIFEST. A future DB open may fail.
|
||||
* Fixed a heap use-after-free race with DropColumnFamily.
|
||||
* Fixed a bug that `rocksdb.read.block.compaction.micros` cannot track compaction stats (#9722).
|
||||
|
||||
## 7.0.3 (03/23/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a major performance bug in which Bloom filters generated by pre-7.0 releases are not read by early 7.0.x releases (and vice-versa) due to changes to FilterPolicy::Name() in #9590. This can severely impact read performance and read I/O on upgrade or downgrade with existing DB, but not data correctness.
|
||||
* Fixed a bug that `Iterator::Refresh()` reads stale keys after DeleteRange() performed.
|
||||
|
||||
### Public API changes
|
||||
* Added pure virtual FilterPolicy::CompatibilityName(), which is needed for fixing major performance bug involving FilterPolicy naming in SST metadata without affecting Customizable aspect of FilterPolicy. For source code, this change only affects those with their own custom or wrapper FilterPolicy classes, but does break compiled library binary compatibility in a patch release.
|
||||
|
||||
## 7.0.2 (03/12/2022)
|
||||
* Fixed a bug that DisableManualCompaction may assert when disable an unscheduled manual compaction.
|
||||
|
||||
## 7.0.1 (03/02/2022)
|
||||
### Bug Fixes
|
||||
* Fix a race condition when cancel manual compaction with `DisableManualCompaction`. Also DB close can cancel the manual compaction thread.
|
||||
* Fixed a data race on `versions_` between `DBImpl::ResumeImpl()` and threads waiting for recovery to complete (#9496)
|
||||
* Fixed a bug caused by race among flush, incoming writes and taking snapshots. Queries to snapshots created with these race condition can return incorrect result, e.g. resurfacing deleted data.
|
||||
* Fixed a bug that DB flush uses `options.compression` even `options.compression_per_level` is set.
|
||||
|
||||
### Public API changes
|
||||
* Remove BlockBasedTableOptions.hash_index_allow_collision which already takes no effect.
|
||||
* `options.compression_per_level` is dynamically changeable with `SetOptions()`.
|
||||
* Added `WriteOptions::rate_limiter_priority`. When set to something other than `Env::IO_TOTAL`, the internal rate limiter (`DBOptions::rate_limiter`) will be charged at the specified priority for writes associated with the API to which the `WriteOptions` was provided. Currently the support covers automatic WAL flushes, which happen during live updates (`Put()`, `Write()`, `Delete()`, etc.) when `WriteOptions::disableWAL == false` and `DBOptions::manual_wal_flush == false`.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a race condition when cancel manual compaction with `DisableManualCompaction`. Also DB close can cancel the manual compaction thread.
|
||||
|
||||
## 7.0.0 (02/20/2022)
|
||||
### Bug Fixes
|
||||
|
||||
@@ -815,6 +815,8 @@ test_libs: $(TEST_LIBS)
|
||||
benchmarks: $(BENCHMARKS)
|
||||
|
||||
microbench: $(MICROBENCHS)
|
||||
|
||||
run_microbench: $(MICROBENCHS)
|
||||
for t in $(MICROBENCHS); do echo "===== Running benchmark $$t (`date`)"; ./$$t || exit 1; done;
|
||||
|
||||
dbg: $(LIBRARY) $(BENCHMARKS) tools $(TESTS)
|
||||
@@ -1864,6 +1866,9 @@ point_lock_manager_test: utilities/transactions/lock/point/point_lock_manager_te
|
||||
transaction_test: $(OBJ_DIR)/utilities/transactions/transaction_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
write_committed_transaction_ts_test: $(OBJ_DIR)/utilities/transactions/write_committed_transaction_ts_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
write_prepared_transaction_test: $(OBJ_DIR)/utilities/transactions/write_prepared_transaction_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1897,6 +1902,9 @@ statistics_test: $(OBJ_DIR)/monitoring/statistics_test.o $(TEST_LIBRARY) $(LIBRA
|
||||
stats_history_test: $(OBJ_DIR)/monitoring/stats_history_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
lru_secondary_cache_test: $(OBJ_DIR)/cache/lru_secondary_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
lru_cache_test: $(OBJ_DIR)/cache/lru_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -2098,7 +2106,7 @@ SHA256_CMD = sha256sum
|
||||
|
||||
ZLIB_VER ?= 1.2.11
|
||||
ZLIB_SHA256 ?= c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
ZLIB_DOWNLOAD_BASE ?= https://zlib.net/fossils
|
||||
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.8
|
||||
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
|
||||
BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
|
||||
@@ -2524,6 +2532,9 @@ endif
|
||||
build_subset_tests: $(ROCKSDBTESTS_SUBSET)
|
||||
$(AM_V_GEN)if [ -n "$${ROCKSDBTESTS_SUBSET_TESTS_TO_FILE}" ]; then echo "$(ROCKSDBTESTS_SUBSET)" > "$${ROCKSDBTESTS_SUBSET_TESTS_TO_FILE}"; else echo "$(ROCKSDBTESTS_SUBSET)"; fi
|
||||
|
||||
list_all_tests:
|
||||
echo "$(ROCKSDBTESTS_SUBSET)"
|
||||
|
||||
# Remove the rules for which dependencies should not be generated and see if any are left.
|
||||
#If so, include the dependencies; if not, do not include the dependency files
|
||||
ROCKS_DEP_RULES=$(filter-out clean format check-format check-buck-targets check-headers check-sources jclean jtest package analyze tags rocksdbjavastatic% unity.% unity_test, $(MAKECMDGOALS))
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -210,14 +210,34 @@ def generate_targets(repo_path, deps_map):
|
||||
with open(f"{repo_path}/buckifier/bench.json") as json_file:
|
||||
fast_fancy_bench_config_list = json.load(json_file)
|
||||
for config_dict in fast_fancy_bench_config_list:
|
||||
TARGETS.add_fancy_bench_config(config_dict['name'],config_dict['benchmarks'], False, config_dict['expected_runtime'])
|
||||
clean_benchmarks = {}
|
||||
benchmarks = config_dict['benchmarks']
|
||||
for binary, benchmark_dict in benchmarks.items():
|
||||
clean_benchmarks[binary] = {}
|
||||
for benchmark, overloaded_metric_list in benchmark_dict.items():
|
||||
clean_benchmarks[binary][benchmark] = []
|
||||
for metric in overloaded_metric_list:
|
||||
if not isinstance(metric, dict):
|
||||
clean_benchmarks[binary][benchmark].append(metric)
|
||||
TARGETS.add_fancy_bench_config(config_dict['name'], clean_benchmarks, False, config_dict['expected_runtime_one_iter'], config_dict['sl_iterations'], config_dict['regression_threshold'])
|
||||
|
||||
with open(f"{repo_path}/buckifier/bench-slow.json") as json_file:
|
||||
slow_fancy_bench_config_list = json.load(json_file)
|
||||
for config_dict in slow_fancy_bench_config_list:
|
||||
TARGETS.add_fancy_bench_config(config_dict['name']+"_slow",config_dict['benchmarks'], True, config_dict['expected_runtime'])
|
||||
|
||||
except (FileNotFoundError, KeyError):
|
||||
clean_benchmarks = {}
|
||||
benchmarks = config_dict['benchmarks']
|
||||
for binary, benchmark_dict in benchmarks.items():
|
||||
clean_benchmarks[binary] = {}
|
||||
for benchmark, overloaded_metric_list in benchmark_dict.items():
|
||||
clean_benchmarks[binary][benchmark] = []
|
||||
for metric in overloaded_metric_list:
|
||||
if not isinstance(metric, dict):
|
||||
clean_benchmarks[binary][benchmark].append(metric)
|
||||
for config_dict in slow_fancy_bench_config_list:
|
||||
TARGETS.add_fancy_bench_config(config_dict['name']+"_slow", clean_benchmarks, True, config_dict['expected_runtime_one_iter'], config_dict['sl_iterations'], config_dict['regression_threshold'])
|
||||
# it is better servicelab experiments break
|
||||
# than rocksdb github ci
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
TARGETS.add_test_header()
|
||||
|
||||
@@ -92,12 +92,14 @@ add_c_test_wrapper()
|
||||
# will not be included.
|
||||
""")
|
||||
|
||||
def add_fancy_bench_config(self, name, bench_config, slow, expected_runtime):
|
||||
def add_fancy_bench_config(self, name, bench_config, slow, expected_runtime, sl_iterations, regression_threshold):
|
||||
self.targets_file.write(targets_cfg.fancy_bench_template.format(
|
||||
name=name,
|
||||
bench_config=pprint.pformat(bench_config),
|
||||
slow=slow,
|
||||
expected_runtime=expected_runtime,
|
||||
sl_iterations=sl_iterations,
|
||||
regression_threshold=regression_threshold
|
||||
).encode("utf-8"))
|
||||
|
||||
def register_test(self,
|
||||
|
||||
@@ -41,6 +41,6 @@ cpp_unittest_wrapper(name="{test_name}",
|
||||
"""
|
||||
|
||||
fancy_bench_template = """
|
||||
fancy_bench_wrapper(suite_name="{name}", binary_to_bench_to_metric_list_map={bench_config}, slow={slow}, expected_runtime={expected_runtime})
|
||||
fancy_bench_wrapper(suite_name="{name}", binary_to_bench_to_metric_list_map={bench_config}, slow={slow}, expected_runtime={expected_runtime}, sl_iterations={sl_iterations}, regression_threshold={regression_threshold})
|
||||
|
||||
"""
|
||||
@@ -638,6 +638,9 @@ if test "0$PORTABLE" -eq 0; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
|
||||
fi
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
|
||||
RISC_ISA=$(cat /proc/cpuinfo | grep isa | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
|
||||
elif [ "$TARGET_OS" == "IOS" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif [ "$TARGET_OS" == "AIX" ] || [ "$TARGET_OS" == "SunOS" ]; then
|
||||
@@ -658,6 +661,11 @@ else
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
|
||||
fi
|
||||
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
|
||||
RISC_ISA=$(cat /proc/cpuinfo | grep isa | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
|
||||
fi
|
||||
|
||||
if [[ "${PLATFORM}" == "OS_MACOSX" ]]; then
|
||||
# For portability compile for macOS 10.12 (2016) or newer
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mmacosx-version-min=10.12"
|
||||
|
||||
@@ -110,8 +110,9 @@ CLANG="USE_CLANG=1"
|
||||
TSAN="COMPILE_WITH_TSAN=1"
|
||||
UBSAN="COMPILE_WITH_UBSAN=1"
|
||||
ASAN_CRASH="ASAN_OPTIONS=disable_coredump=0"
|
||||
TSAN_CRASH="CRASH_TEST_EXT_ARGS=\\\"--compression_type=zstd --log2_keys_per_lock=22\\\""
|
||||
TSAN_CRASH="TSAN_OPTIONS=disable_coredump=0 CRASH_TEST_EXT_ARGS=\\\"--compression_type=zstd --log2_keys_per_lock=22\\\""
|
||||
NON_TSAN_CRASH="CRASH_TEST_EXT_ARGS=--compression_type=zstd"
|
||||
UBSAN_CRASH="UBSAN_OPTIONS=disable_coredump=0"
|
||||
DISABLE_JEMALLOC="DISABLE_JEMALLOC=1"
|
||||
HTTP_PROXY="https_proxy=http://fwdproxy.29.prn1:8080 http_proxy=http://fwdproxy.29.prn1:8080 ftp_proxy=http://fwdproxy.29.prn1:8080"
|
||||
SETUP_JAVA_ENV="export $HTTP_PROXY; export JAVA_HOME=/usr/local/jdk-8u60-64/; export PATH=\$JAVA_HOME/bin:\$PATH"
|
||||
@@ -760,7 +761,7 @@ UBSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
\"name\":\"Build and run RocksDB debug ubsan_crash_test\",
|
||||
\"timeout\": 86400,
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make $PARALLELISM ubsan_crash_test || $CONTRUN_NAME=ubsan_crash_test $TASK_CREATION_TOOL\",
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $UBSAN_CRASH $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make $PARALLELISM ubsan_crash_test || $CONTRUN_NAME=ubsan_crash_test $TASK_CREATION_TOOL\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
@@ -784,7 +785,7 @@ UBSAN_BLACKBOX_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
\"name\":\"Build and run RocksDB debug blackbox ubsan_crash_test\",
|
||||
\"timeout\": 86400,
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make $PARALLELISM blackbox_ubsan_crash_test || $CONTRUN_NAME=blackbox_ubsan_crash_test $TASK_CREATION_TOOL\",
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $UBSAN_CRASH $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make $PARALLELISM blackbox_ubsan_crash_test || $CONTRUN_NAME=blackbox_ubsan_crash_test $TASK_CREATION_TOOL\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
@@ -808,7 +809,7 @@ UBSAN_WHITEBOX_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
\"name\":\"Build and run RocksDB debug whitebox ubsan_crash_test\",
|
||||
\"timeout\": 86400,
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make $PARALLELISM whitebox_ubsan_crash_test || $CONTRUN_NAME=whitebox_ubsan_crash_test $TASK_CREATION_TOOL\",
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $UBSAN_CRASH $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make $PARALLELISM whitebox_ubsan_crash_test || $CONTRUN_NAME=whitebox_ubsan_crash_test $TASK_CREATION_TOOL\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
@@ -832,7 +833,7 @@ UBSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
\"name\":\"Build and run RocksDB debug ubsan_crash_test_with_atomic_flush\",
|
||||
\"timeout\": 86400,
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make $PARALLELISM ubsan_crash_test_with_atomic_flush || $CONTRUN_NAME=ubsan_crash_test_with_atomic_flush $TASK_CREATION_TOOL\",
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $UBSAN_CRASH $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make $PARALLELISM ubsan_crash_test_with_atomic_flush || $CONTRUN_NAME=ubsan_crash_test_with_atomic_flush $TASK_CREATION_TOOL\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
@@ -856,7 +857,7 @@ UBSAN_CRASH_TEST_WITH_TXN_COMMANDS="[
|
||||
{
|
||||
\"name\":\"Build and run RocksDB debug ubsan_crash_test_with_txn\",
|
||||
\"timeout\": 86400,
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make $PARALLELISM ubsan_crash_test_with_txn || $CONTRUN_NAME=ubsan_crash_test_with_txn $TASK_CREATION_TOOL\",
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $UBSAN_CRASH $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make $PARALLELISM ubsan_crash_test_with_txn || $CONTRUN_NAME=ubsan_crash_test_with_txn $TASK_CREATION_TOOL\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
|
||||
Vendored
+1
-1
@@ -687,7 +687,7 @@ Status ClockCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
Status s;
|
||||
if (out_handle != nullptr) {
|
||||
if (handle == nullptr) {
|
||||
s = Status::Incomplete("Insert failed due to LRU cache being full.");
|
||||
s = Status::Incomplete("Insert failed due to CLOCK cache being full.");
|
||||
} else {
|
||||
*out_handle = reinterpret_cast<Cache::Handle*>(handle);
|
||||
}
|
||||
|
||||
Vendored
+168
@@ -0,0 +1,168 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "cache/lru_secondary_cache.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "memory/memory_allocator.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
|
||||
void DeletionCallback(const Slice& /*key*/, void* obj) {
|
||||
delete reinterpret_cast<CacheAllocationPtr*>(obj);
|
||||
obj = nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
LRUSecondaryCache::LRUSecondaryCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
CompressionType compression_type, uint32_t compress_format_version)
|
||||
: cache_options_(capacity, num_shard_bits, strict_capacity_limit,
|
||||
high_pri_pool_ratio, memory_allocator, use_adaptive_mutex,
|
||||
metadata_charge_policy, compression_type,
|
||||
compress_format_version) {
|
||||
cache_ = NewLRUCache(capacity, num_shard_bits, strict_capacity_limit,
|
||||
high_pri_pool_ratio, memory_allocator,
|
||||
use_adaptive_mutex, metadata_charge_policy);
|
||||
}
|
||||
|
||||
LRUSecondaryCache::~LRUSecondaryCache() { cache_.reset(); }
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> LRUSecondaryCache::Lookup(
|
||||
const Slice& key, const Cache::CreateCallback& create_cb, bool /*wait*/) {
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle;
|
||||
Cache::Handle* lru_handle = cache_->Lookup(key);
|
||||
if (lru_handle == nullptr) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
CacheAllocationPtr* ptr =
|
||||
reinterpret_cast<CacheAllocationPtr*>(cache_->Value(lru_handle));
|
||||
void* value = nullptr;
|
||||
size_t charge = 0;
|
||||
Status s;
|
||||
|
||||
if (cache_options_.compression_type == kNoCompression) {
|
||||
s = create_cb(ptr->get(), cache_->GetCharge(lru_handle), &value, &charge);
|
||||
} else {
|
||||
UncompressionContext uncompression_context(cache_options_.compression_type);
|
||||
UncompressionInfo uncompression_info(uncompression_context,
|
||||
UncompressionDict::GetEmptyDict(),
|
||||
cache_options_.compression_type);
|
||||
|
||||
size_t uncompressed_size = 0;
|
||||
CacheAllocationPtr uncompressed;
|
||||
uncompressed = UncompressData(
|
||||
uncompression_info, (char*)ptr->get(), cache_->GetCharge(lru_handle),
|
||||
&uncompressed_size, cache_options_.compress_format_version,
|
||||
cache_options_.memory_allocator.get());
|
||||
|
||||
if (!uncompressed) {
|
||||
cache_->Release(lru_handle, true);
|
||||
return handle;
|
||||
}
|
||||
s = create_cb(uncompressed.get(), uncompressed_size, &value, &charge);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
cache_->Release(lru_handle, true);
|
||||
return handle;
|
||||
}
|
||||
|
||||
handle.reset(new LRUSecondaryCacheResultHandle(value, charge));
|
||||
cache_->Release(lru_handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
Status LRUSecondaryCache::Insert(const Slice& key, void* value,
|
||||
const Cache::CacheItemHelper* helper) {
|
||||
size_t size = (*helper->size_cb)(value);
|
||||
CacheAllocationPtr ptr =
|
||||
AllocateBlock(size, cache_options_.memory_allocator.get());
|
||||
|
||||
Status s = (*helper->saveto_cb)(value, 0, size, ptr.get());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
Slice val(ptr.get(), size);
|
||||
|
||||
std::string compressed_val;
|
||||
if (cache_options_.compression_type != kNoCompression) {
|
||||
CompressionOptions compression_opts;
|
||||
CompressionContext compression_context(cache_options_.compression_type);
|
||||
uint64_t sample_for_compression = 0;
|
||||
CompressionInfo compression_info(
|
||||
compression_opts, compression_context, CompressionDict::GetEmptyDict(),
|
||||
cache_options_.compression_type, sample_for_compression);
|
||||
|
||||
bool success =
|
||||
CompressData(val, compression_info,
|
||||
cache_options_.compress_format_version, &compressed_val);
|
||||
|
||||
if (!success) {
|
||||
return Status::Corruption("Error compressing value.");
|
||||
}
|
||||
|
||||
val = Slice(compressed_val);
|
||||
size = compressed_val.size();
|
||||
ptr = AllocateBlock(size, cache_options_.memory_allocator.get());
|
||||
memcpy(ptr.get(), compressed_val.data(), size);
|
||||
}
|
||||
|
||||
CacheAllocationPtr* buf = new CacheAllocationPtr(std::move(ptr));
|
||||
|
||||
return cache_->Insert(key, buf, size, DeletionCallback);
|
||||
}
|
||||
|
||||
void LRUSecondaryCache::Erase(const Slice& key) { cache_->Erase(key); }
|
||||
|
||||
std::string LRUSecondaryCache::GetPrintableOptions() const {
|
||||
std::string ret;
|
||||
ret.reserve(20000);
|
||||
const int kBufferSize = 200;
|
||||
char buffer[kBufferSize];
|
||||
ret.append(cache_->GetPrintableOptions());
|
||||
snprintf(buffer, kBufferSize, " compression_type : %s\n",
|
||||
CompressionTypeToString(cache_options_.compression_type).c_str());
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " compression_type : %d\n",
|
||||
cache_options_.compress_format_version);
|
||||
ret.append(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache> NewLRUSecondaryCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
CompressionType compression_type, uint32_t compress_format_version) {
|
||||
return std::make_shared<LRUSecondaryCache>(
|
||||
capacity, num_shard_bits, strict_capacity_limit, high_pri_pool_ratio,
|
||||
memory_allocator, use_adaptive_mutex, metadata_charge_policy,
|
||||
compression_type, compress_format_version);
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache> NewLRUSecondaryCache(
|
||||
const LRUSecondaryCacheOptions& opts) {
|
||||
// The secondary_cache is disabled for this LRUCache instance.
|
||||
assert(opts.secondary_cache == nullptr);
|
||||
return NewLRUSecondaryCache(
|
||||
opts.capacity, opts.num_shard_bits, opts.strict_capacity_limit,
|
||||
opts.high_pri_pool_ratio, opts.memory_allocator, opts.use_adaptive_mutex,
|
||||
opts.metadata_charge_policy, opts.compression_type,
|
||||
opts.compress_format_version);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "cache/lru_cache.h"
|
||||
#include "memory/memory_allocator.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "util/compression.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class LRUSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
|
||||
public:
|
||||
LRUSecondaryCacheResultHandle(void* value, size_t size)
|
||||
: value_(value), size_(size) {}
|
||||
virtual ~LRUSecondaryCacheResultHandle() override = default;
|
||||
|
||||
LRUSecondaryCacheResultHandle(const LRUSecondaryCacheResultHandle&) = delete;
|
||||
LRUSecondaryCacheResultHandle& operator=(
|
||||
const LRUSecondaryCacheResultHandle&) = delete;
|
||||
|
||||
bool IsReady() override { return true; }
|
||||
|
||||
void Wait() override {}
|
||||
|
||||
void* Value() override { return value_; }
|
||||
|
||||
size_t Size() override { return size_; }
|
||||
|
||||
private:
|
||||
void* value_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
// The LRUSecondaryCache is a concrete implementation of
|
||||
// rocksdb::SecondaryCache.
|
||||
//
|
||||
// Users can also cast a pointer to it and call methods on
|
||||
// it directly, especially custom methods that may be added
|
||||
// in the future. For example -
|
||||
// std::unique_ptr<rocksdb::SecondaryCache> cache =
|
||||
// NewLRUSecondaryCache(opts);
|
||||
// static_cast<LRUSecondaryCache*>(cache.get())->Erase(key);
|
||||
|
||||
class LRUSecondaryCache : public SecondaryCache {
|
||||
public:
|
||||
LRUSecondaryCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator = nullptr,
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy =
|
||||
kDontChargeCacheMetadata,
|
||||
CompressionType compression_type = CompressionType::kLZ4Compression,
|
||||
uint32_t compress_format_version = 2);
|
||||
virtual ~LRUSecondaryCache() override;
|
||||
|
||||
const char* Name() const override { return "LRUSecondaryCache"; }
|
||||
|
||||
Status Insert(const Slice& key, void* value,
|
||||
const Cache::CacheItemHelper* helper) override;
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CreateCallback& create_cb,
|
||||
bool /*wait*/) override;
|
||||
|
||||
void Erase(const Slice& key) override;
|
||||
|
||||
void WaitAll(std::vector<SecondaryCacheResultHandle*> /*handles*/) override {}
|
||||
|
||||
std::string GetPrintableOptions() const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Cache> cache_;
|
||||
LRUSecondaryCacheOptions cache_options_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+597
@@ -0,0 +1,597 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "cache/lru_secondary_cache.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
#include "memory/jemalloc_nodump_allocator.h"
|
||||
#include "memory/memory_allocator.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/random.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class LRUSecondaryCacheTest : public testing::Test {
|
||||
public:
|
||||
LRUSecondaryCacheTest() : fail_create_(false) {}
|
||||
~LRUSecondaryCacheTest() {}
|
||||
|
||||
protected:
|
||||
class TestItem {
|
||||
public:
|
||||
TestItem(const char* buf, size_t size) : buf_(new char[size]), size_(size) {
|
||||
memcpy(buf_.get(), buf, size);
|
||||
}
|
||||
~TestItem() {}
|
||||
|
||||
char* Buf() { return buf_.get(); }
|
||||
size_t Size() { return size_; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<char[]> buf_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
static size_t SizeCallback(void* obj) {
|
||||
return reinterpret_cast<TestItem*>(obj)->Size();
|
||||
}
|
||||
|
||||
static Status SaveToCallback(void* from_obj, size_t from_offset,
|
||||
size_t length, void* out) {
|
||||
TestItem* item = reinterpret_cast<TestItem*>(from_obj);
|
||||
const char* buf = item->Buf();
|
||||
EXPECT_EQ(length, item->Size());
|
||||
EXPECT_EQ(from_offset, 0);
|
||||
memcpy(out, buf, length);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static void DeletionCallback(const Slice& /*key*/, void* obj) {
|
||||
delete reinterpret_cast<TestItem*>(obj);
|
||||
obj = nullptr;
|
||||
}
|
||||
|
||||
static Cache::CacheItemHelper helper_;
|
||||
|
||||
static Status SaveToCallbackFail(void* /*obj*/, size_t /*offset*/,
|
||||
size_t /*size*/, void* /*out*/) {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
static Cache::CacheItemHelper helper_fail_;
|
||||
|
||||
Cache::CreateCallback test_item_creator = [&](const void* buf, size_t size,
|
||||
void** out_obj,
|
||||
size_t* charge) -> Status {
|
||||
if (fail_create_) {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
*out_obj = reinterpret_cast<void*>(new TestItem((char*)buf, size));
|
||||
*charge = size;
|
||||
return Status::OK();
|
||||
};
|
||||
|
||||
void SetFailCreate(bool fail) { fail_create_ = fail; }
|
||||
|
||||
void BasicTest(bool sec_cache_is_compressed, bool use_jemalloc) {
|
||||
LRUSecondaryCacheOptions 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> cache = NewLRUSecondaryCache(opts);
|
||||
|
||||
// Lookup an non-existent key.
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle0 =
|
||||
cache->Lookup("k0", test_item_creator, true);
|
||||
ASSERT_EQ(handle0, nullptr);
|
||||
|
||||
Random rnd(301);
|
||||
// Insert and Lookup the first item.
|
||||
std::string str1;
|
||||
test::CompressibleString(&rnd, 0.25, 1000, &str1);
|
||||
TestItem item1(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", &item1, &LRUSecondaryCacheTest::helper_));
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1 =
|
||||
cache->Lookup("k1", test_item_creator, true);
|
||||
ASSERT_NE(handle1, nullptr);
|
||||
// delete reinterpret_cast<TestItem*>(handle1->Value());
|
||||
std::unique_ptr<TestItem> val1 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle1->Value()));
|
||||
ASSERT_NE(val1, nullptr);
|
||||
ASSERT_EQ(memcmp(val1->Buf(), item1.Buf(), item1.Size()), 0);
|
||||
|
||||
// Insert and Lookup the second item.
|
||||
std::string str2;
|
||||
test::CompressibleString(&rnd, 0.5, 1000, &str2);
|
||||
TestItem item2(str2.data(), str2.length());
|
||||
ASSERT_OK(cache->Insert("k2", &item2, &LRUSecondaryCacheTest::helper_));
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle2 =
|
||||
cache->Lookup("k2", test_item_creator, true);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
std::unique_ptr<TestItem> val2 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle2->Value()));
|
||||
ASSERT_NE(val2, nullptr);
|
||||
ASSERT_EQ(memcmp(val2->Buf(), item2.Buf(), item2.Size()), 0);
|
||||
|
||||
// Lookup the first item again to make sure it is still in the cache.
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
|
||||
cache->Lookup("k1", test_item_creator, true);
|
||||
ASSERT_NE(handle1_1, nullptr);
|
||||
std::unique_ptr<TestItem> val1_1 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle1_1->Value()));
|
||||
ASSERT_NE(val1_1, nullptr);
|
||||
ASSERT_EQ(memcmp(val1_1->Buf(), item1.Buf(), item1.Size()), 0);
|
||||
|
||||
std::vector<SecondaryCacheResultHandle*> handles = {handle1.get(),
|
||||
handle2.get()};
|
||||
cache->WaitAll(handles);
|
||||
|
||||
cache->Erase("k1");
|
||||
handle1 = cache->Lookup("k1", test_item_creator, true);
|
||||
ASSERT_EQ(handle1, nullptr);
|
||||
|
||||
cache.reset();
|
||||
}
|
||||
|
||||
void FailsTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
secondary_cache_opts.capacity = 1100;
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
std::shared_ptr<SecondaryCache> cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
|
||||
// Insert and Lookup the first item.
|
||||
Random rnd(301);
|
||||
std::string str1(rnd.RandomString(1000));
|
||||
TestItem item1(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", &item1, &LRUSecondaryCacheTest::helper_));
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1 =
|
||||
cache->Lookup("k1", test_item_creator, true);
|
||||
ASSERT_NE(handle1, nullptr);
|
||||
std::unique_ptr<TestItem> val1 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle1->Value()));
|
||||
ASSERT_NE(val1, nullptr);
|
||||
ASSERT_EQ(memcmp(val1->Buf(), item1.Buf(), item1.Size()), 0);
|
||||
|
||||
// Insert and Lookup the second item.
|
||||
std::string str2(rnd.RandomString(200));
|
||||
TestItem item2(str2.data(), str2.length());
|
||||
// k1 is evicted.
|
||||
ASSERT_OK(cache->Insert("k2", &item2, &LRUSecondaryCacheTest::helper_));
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
|
||||
cache->Lookup("k1", test_item_creator, true);
|
||||
ASSERT_EQ(handle1_1, nullptr);
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle2 =
|
||||
cache->Lookup("k2", test_item_creator, true);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
std::unique_ptr<TestItem> val2 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle2->Value()));
|
||||
ASSERT_NE(val2, nullptr);
|
||||
ASSERT_EQ(memcmp(val2->Buf(), item2.Buf(), item2.Size()), 0);
|
||||
|
||||
// Create Fails.
|
||||
SetFailCreate(true);
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle2_1 =
|
||||
cache->Lookup("k2", test_item_creator, true);
|
||||
ASSERT_EQ(handle2_1, nullptr);
|
||||
|
||||
// Save Fails.
|
||||
std::string str3 = rnd.RandomString(10);
|
||||
TestItem item3(str3.data(), str3.length());
|
||||
ASSERT_NOK(
|
||||
cache->Insert("k3", &item3, &LRUSecondaryCacheTest::helper_fail_));
|
||||
|
||||
cache.reset();
|
||||
}
|
||||
|
||||
void BasicIntegrationTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
secondary_cache_opts.capacity = 2300;
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
LRUCacheOptions lru_cache_opts(1024, 0, false, 0.5, nullptr,
|
||||
kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
lru_cache_opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lru_cache_opts);
|
||||
std::shared_ptr<Statistics> stats = CreateDBStatistics();
|
||||
|
||||
Random rnd(301);
|
||||
|
||||
std::string str1 = rnd.RandomString(1010);
|
||||
std::string str1_clone{str1};
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// After Insert, lru cache contains k2 and secondary cache contains k1.
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_,
|
||||
str2.length()));
|
||||
|
||||
std::string str3 = rnd.RandomString(1020);
|
||||
TestItem* item3 = new TestItem(str3.data(), str3.length());
|
||||
// After Insert, lru cache contains k3 and secondary cache contains k1 and
|
||||
// k2
|
||||
ASSERT_OK(cache->Insert("k3", item3, &LRUSecondaryCacheTest::helper_,
|
||||
str3.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle =
|
||||
cache->Lookup("k3", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_NE(handle, nullptr);
|
||||
TestItem* val3 = static_cast<TestItem*>(cache->Value(handle));
|
||||
ASSERT_NE(val3, nullptr);
|
||||
ASSERT_EQ(memcmp(val3->Buf(), item3->Buf(), item3->Size()), 0);
|
||||
cache->Release(handle);
|
||||
|
||||
// Lookup an non-existent key.
|
||||
handle =
|
||||
cache->Lookup("k0", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
|
||||
// This Lookup should promote k1 and demote k3, so k2 is evicted from the
|
||||
// secondary cache. The lru cache contains k1 and secondary cache contains
|
||||
// k3. item1 was Free(), so it cannot be compared against the item1.
|
||||
handle =
|
||||
cache->Lookup("k1", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_NE(handle, nullptr);
|
||||
TestItem* val1_1 = static_cast<TestItem*>(cache->Value(handle));
|
||||
ASSERT_NE(val1_1, nullptr);
|
||||
ASSERT_EQ(memcmp(val1_1->Buf(), str1_clone.data(), str1_clone.size()), 0);
|
||||
cache->Release(handle);
|
||||
|
||||
handle =
|
||||
cache->Lookup("k2", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
void BasicIntegrationFailTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
secondary_cache_opts.capacity = 2048;
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
|
||||
LRUCacheOptions opts(1024, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(opts);
|
||||
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
auto item1 =
|
||||
std::unique_ptr<TestItem>(new TestItem(str1.data(), str1.length()));
|
||||
ASSERT_NOK(cache->Insert("k1", item1.get(), nullptr, str1.length()));
|
||||
ASSERT_OK(cache->Insert("k1", item1.get(), &LRUSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
item1.release(); // Appease clang-analyze "potential memory leak"
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup("k2", nullptr, test_item_creator,
|
||||
Cache::Priority::LOW, true);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, false);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
void IntegrationSaveFailTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
secondary_cache_opts.capacity = 2048;
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
|
||||
LRUCacheOptions opts(1024, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(opts);
|
||||
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_fail_,
|
||||
str1.length()));
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k1 should be demoted to the secondary cache.
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_fail_,
|
||||
str2.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_fail_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
// This lookup should fail, since k1 demotion would have failed
|
||||
handle = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_fail_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
// Since k1 didn't get promoted, k2 should still be in cache
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_fail_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
void IntegrationCreateFailTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
secondary_cache_opts.capacity = 2048;
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
|
||||
LRUCacheOptions opts(1024, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(opts);
|
||||
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k1 should be demoted to the secondary cache.
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_,
|
||||
str2.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
SetFailCreate(true);
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
// This lookup should fail, since k1 creation would have failed
|
||||
handle = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
// Since k1 didn't get promoted, k2 should still be in cache
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
void IntegrationFullCapacityTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
secondary_cache_opts.capacity = 2048;
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
|
||||
LRUCacheOptions opts(1024, 0, /*_strict_capacity_limit=*/true, 0.5, nullptr,
|
||||
kDefaultToAdaptiveMutex, kDontChargeCacheMetadata);
|
||||
opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(opts);
|
||||
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k1 should be demoted to the secondary cache.
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_,
|
||||
str2.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
// k1 promotion should fail due to the block cache being at capacity,
|
||||
// but the lookup should still succeed
|
||||
Cache::Handle* handle2;
|
||||
handle2 = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
// Since k1 didn't get inserted, k2 should still be in cache
|
||||
cache->Release(handle);
|
||||
cache->Release(handle2);
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
bool fail_create_;
|
||||
};
|
||||
|
||||
Cache::CacheItemHelper LRUSecondaryCacheTest::helper_(
|
||||
LRUSecondaryCacheTest::SizeCallback, LRUSecondaryCacheTest::SaveToCallback,
|
||||
LRUSecondaryCacheTest::DeletionCallback);
|
||||
|
||||
Cache::CacheItemHelper LRUSecondaryCacheTest::helper_fail_(
|
||||
LRUSecondaryCacheTest::SizeCallback,
|
||||
LRUSecondaryCacheTest::SaveToCallbackFail,
|
||||
LRUSecondaryCacheTest::DeletionCallback);
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicTestWithNoCompression) {
|
||||
BasicTest(false, false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicTestWithMemoryAllocatorAndNoCompression) {
|
||||
BasicTest(false, true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicTestWithCompression) {
|
||||
BasicTest(true, false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicTestWithMemoryAllocatorAndCompression) {
|
||||
BasicTest(true, true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, FailsTestWithNoCompression) { FailsTest(false); }
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, FailsTestWithCompression) { FailsTest(true); }
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicIntegrationTestWithNoCompression) {
|
||||
BasicIntegrationTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicIntegrationTestWithCompression) {
|
||||
BasicIntegrationTest(true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicIntegrationFailTestWithNoCompression) {
|
||||
BasicIntegrationFailTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicIntegrationFailTestWithCompression) {
|
||||
BasicIntegrationFailTest(true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationSaveFailTestWithNoCompression) {
|
||||
IntegrationSaveFailTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationSaveFailTestWithCompression) {
|
||||
IntegrationSaveFailTest(true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationCreateFailTestWithNoCompression) {
|
||||
IntegrationCreateFailTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationCreateFailTestWithCompression) {
|
||||
IntegrationCreateFailTest(true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationFullCapacityTestWithNoCompression) {
|
||||
IntegrationFullCapacityTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationFullCapacityTestWithCompression) {
|
||||
IntegrationFullCapacityTest(true);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+22
-47
@@ -58,55 +58,30 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
uint64_t cur_sv_number = cfd_->GetSuperVersionNumber();
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:1");
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:2");
|
||||
while (true) {
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(latest_seq);
|
||||
}
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
sv->current, latest_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, read_callback_, db_impl_, cfd_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator(),
|
||||
latest_seq, /* allow_unprepared_value */ true);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
break;
|
||||
} else {
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
// Refresh range-tombstones in MemTable
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
SuperVersion* sv = cfd_->GetThreadLocalSuperVersion(db_impl_);
|
||||
ReadRangeDelAggregator* range_del_agg =
|
||||
db_iter_->GetRangeDelAggregator();
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter;
|
||||
range_del_iter.reset(
|
||||
sv->mem->NewRangeTombstoneIterator(read_options_, latest_seq));
|
||||
range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
cfd_->ReturnThreadLocalSuperVersion(sv);
|
||||
}
|
||||
// Refresh latest sequence number
|
||||
db_iter_->set_sequence(latest_seq);
|
||||
db_iter_->set_valid(false);
|
||||
// Check again if the latest super version number is changed
|
||||
uint64_t latest_sv_number = cfd_->GetSuperVersionNumber();
|
||||
if (latest_sv_number != cur_sv_number) {
|
||||
// If the super version number is changed after refreshing,
|
||||
// fallback to Re-Init the InternalIterator
|
||||
cur_sv_number = latest_sv_number;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(latest_seq);
|
||||
}
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
sv->current, latest_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, read_callback_, db_impl_, cfd_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator(),
|
||||
latest_seq, /* allow_unprepared_value */ true);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
} else {
|
||||
db_iter_->set_sequence(db_impl_->GetLatestSequenceNumber());
|
||||
db_iter_->set_valid(false);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -2297,11 +2297,6 @@ void rocksdb_block_based_options_set_data_block_hash_ratio(
|
||||
options->rep.data_block_hash_table_util_ratio = v;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_hash_index_allow_collision(
|
||||
rocksdb_block_based_table_options_t* options, unsigned char v) {
|
||||
options->rep.hash_index_allow_collision = v;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_cache_index_and_filter_blocks(
|
||||
rocksdb_block_based_table_options_t* options, unsigned char v) {
|
||||
options->rep.cache_index_and_filter_blocks = v;
|
||||
@@ -3757,9 +3752,6 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom_format(
|
||||
const FilterPolicy* rep_;
|
||||
~Wrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
const char* CompatibilityName() const override {
|
||||
return rep_->CompatibilityName();
|
||||
}
|
||||
// No need to override GetFilterBitsBuilder if this one is overridden
|
||||
ROCKSDB_NAMESPACE::FilterBitsBuilder* GetBuilderWithContext(
|
||||
const ROCKSDB_NAMESPACE::FilterBuildingContext& context)
|
||||
@@ -3797,9 +3789,6 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_ribbon_format(
|
||||
const FilterPolicy* rep_;
|
||||
~Wrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
const char* CompatibilityName() const override {
|
||||
return rep_->CompatibilityName();
|
||||
}
|
||||
ROCKSDB_NAMESPACE::FilterBitsBuilder* GetBuilderWithContext(
|
||||
const ROCKSDB_NAMESPACE::FilterBuildingContext& context)
|
||||
const override {
|
||||
|
||||
+7
-35
@@ -7,13 +7,12 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE // Lite does not support C API
|
||||
|
||||
#include <assert.h>
|
||||
#include "rocksdb/c.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "rocksdb/c.h"
|
||||
#ifndef OS_WIN
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@@ -90,8 +89,10 @@ static void CheckEqual(const char* expected, const char* v, size_t n) {
|
||||
// ok
|
||||
return;
|
||||
} else {
|
||||
fprintf(stderr, "%s: expected '%s', got '%s'\n", phase,
|
||||
(expected ? expected : "(null)"), (v ? v : "(null)"));
|
||||
fprintf(stderr, "%s: expected '%s', got '%s'\n",
|
||||
phase,
|
||||
(expected ? expected : "(null)"),
|
||||
(v ? v : "(null"));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@@ -988,36 +989,7 @@ int main(int argc, char** argv) {
|
||||
CheckGet(db, roptions, "foo", NULL);
|
||||
rocksdb_release_snapshot(db, snap);
|
||||
}
|
||||
StartPhase("snapshot_with_memtable_inplace_update");
|
||||
{
|
||||
rocksdb_close(db);
|
||||
const rocksdb_snapshot_t* snap = NULL;
|
||||
const char* s_key = "foo_snap";
|
||||
const char* value1 = "hello_s1";
|
||||
const char* value2 = "hello_s2";
|
||||
rocksdb_options_set_allow_concurrent_memtable_write(options, 0);
|
||||
rocksdb_options_set_inplace_update_support(options, 1);
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_put(db, woptions, s_key, 8, value1, 8, &err);
|
||||
snap = rocksdb_create_snapshot(db);
|
||||
assert(snap != NULL);
|
||||
rocksdb_put(db, woptions, s_key, 8, value2, 8, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_readoptions_set_snapshot(roptions, snap);
|
||||
CheckGet(db, roptions, "foo", NULL);
|
||||
// snapshot syntax is invalid, because of inplace update supported is set
|
||||
CheckGet(db, roptions, s_key, value2);
|
||||
// restore the data and options
|
||||
rocksdb_delete(db, woptions, s_key, 8, &err);
|
||||
CheckGet(db, roptions, s_key, NULL);
|
||||
rocksdb_release_snapshot(db, snap);
|
||||
rocksdb_readoptions_set_snapshot(roptions, NULL);
|
||||
rocksdb_options_set_inplace_update_support(options, 0);
|
||||
rocksdb_options_set_allow_concurrent_memtable_write(options, 1);
|
||||
rocksdb_options_set_error_if_exists(options, 1);
|
||||
}
|
||||
|
||||
StartPhase("repair");
|
||||
{
|
||||
// If we do not compact here, then the lazy deletion of
|
||||
|
||||
@@ -1562,6 +1562,20 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
return new_cfd;
|
||||
}
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
void ColumnFamilySet::FreeDeadColumnFamilies() {
|
||||
autovector<ColumnFamilyData*> to_delete;
|
||||
for (auto cfd = dummy_cfd_->next_; cfd != dummy_cfd_; cfd = cfd->next_) {
|
||||
if (cfd->refs_.load(std::memory_order_relaxed) == 0) {
|
||||
to_delete.push_back(cfd);
|
||||
}
|
||||
}
|
||||
for (auto cfd : to_delete) {
|
||||
// this is very rare, so it's not a problem that we do it under a mutex
|
||||
delete cfd;
|
||||
}
|
||||
}
|
||||
|
||||
// under a DB mutex AND from a write thread
|
||||
void ColumnFamilySet::RemoveColumnFamily(ColumnFamilyData* cfd) {
|
||||
auto cfd_iter = column_family_data_.find(cfd->GetID());
|
||||
|
||||
+17
-56
@@ -519,10 +519,9 @@ class ColumnFamilyData {
|
||||
ThreadLocalPtr* TEST_GetLocalSV() { return local_sv_.get(); }
|
||||
WriteBufferManager* write_buffer_mgr() { return write_buffer_manager_; }
|
||||
|
||||
static const uint32_t kDummyColumnFamilyDataId;
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
static const uint32_t kDummyColumnFamilyDataId;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
Version* dummy_versions, Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
@@ -628,8 +627,10 @@ class ColumnFamilyData {
|
||||
// held and it needs to be executed from the write thread. SetDropped() also
|
||||
// guarantees that it will be called only from single-threaded LogAndApply(),
|
||||
// but this condition is not that important.
|
||||
// * Iteration -- hold DB mutex. If you want to release the DB mutex in the
|
||||
// body of the iteration, wrap in a RefedColumnFamilySet.
|
||||
// * Iteration -- hold DB mutex, but you can release it in the body of
|
||||
// iteration. If you release DB mutex in body, reference the column
|
||||
// family before the mutex and unreference after you unlock, since the column
|
||||
// family might get dropped when the DB mutex is released
|
||||
// * GetDefault() -- thread safe
|
||||
// * GetColumnFamily() -- either inside of DB mutex or from a write thread
|
||||
// * GetNextColumnFamilyID(), GetMaxColumnFamily(), UpdateMaxColumnFamily(),
|
||||
@@ -641,12 +642,17 @@ class ColumnFamilySet {
|
||||
public:
|
||||
explicit iterator(ColumnFamilyData* cfd)
|
||||
: current_(cfd) {}
|
||||
// NOTE: minimum operators for for-loop iteration
|
||||
iterator& operator++() {
|
||||
current_ = current_->next_;
|
||||
// dropped column families might still be included in this iteration
|
||||
// (we're only removing them when client drops the last reference to the
|
||||
// column family).
|
||||
// dummy is never dead, so this will never be infinite
|
||||
do {
|
||||
current_ = current_->next_;
|
||||
} while (current_->refs_.load(std::memory_order_relaxed) == 0);
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(const iterator& other) const {
|
||||
bool operator!=(const iterator& other) {
|
||||
return this->current_ != other.current_;
|
||||
}
|
||||
ColumnFamilyData* operator*() { return current_; }
|
||||
@@ -685,6 +691,10 @@ class ColumnFamilySet {
|
||||
iterator begin() { return iterator(dummy_cfd_->next_); }
|
||||
iterator end() { return iterator(dummy_cfd_); }
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
// Don't call while iterating over ColumnFamilySet
|
||||
void FreeDeadColumnFamilies();
|
||||
|
||||
Cache* get_table_cache() { return table_cache_; }
|
||||
|
||||
WriteBufferManager* write_buffer_manager() { return write_buffer_manager_; }
|
||||
@@ -727,55 +737,6 @@ class ColumnFamilySet {
|
||||
std::string db_session_id_;
|
||||
};
|
||||
|
||||
// A wrapper for ColumnFamilySet that supports releasing DB mutex during each
|
||||
// iteration over the iterator, because the cfd is Refed and Unrefed during
|
||||
// each iteration to prevent concurrent CF drop from destroying it (until
|
||||
// Unref).
|
||||
class RefedColumnFamilySet {
|
||||
public:
|
||||
explicit RefedColumnFamilySet(ColumnFamilySet* cfs) : wrapped_(cfs) {}
|
||||
|
||||
class iterator {
|
||||
public:
|
||||
explicit iterator(ColumnFamilySet::iterator wrapped) : wrapped_(wrapped) {
|
||||
MaybeRef(*wrapped_);
|
||||
}
|
||||
~iterator() { MaybeUnref(*wrapped_); }
|
||||
inline void MaybeRef(ColumnFamilyData* cfd) {
|
||||
if (cfd->GetID() != ColumnFamilyData::kDummyColumnFamilyDataId) {
|
||||
cfd->Ref();
|
||||
}
|
||||
}
|
||||
inline void MaybeUnref(ColumnFamilyData* cfd) {
|
||||
if (cfd->GetID() != ColumnFamilyData::kDummyColumnFamilyDataId) {
|
||||
cfd->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
// NOTE: minimum operators for for-loop iteration
|
||||
inline iterator& operator++() {
|
||||
ColumnFamilyData* old = *wrapped_;
|
||||
++wrapped_;
|
||||
// Can only unref & potentially free cfd after accessing its next_
|
||||
MaybeUnref(old);
|
||||
MaybeRef(*wrapped_);
|
||||
return *this;
|
||||
}
|
||||
inline bool operator!=(const iterator& other) const {
|
||||
return this->wrapped_ != other.wrapped_;
|
||||
}
|
||||
inline ColumnFamilyData* operator*() { return *wrapped_; }
|
||||
|
||||
private:
|
||||
ColumnFamilySet::iterator wrapped_;
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(wrapped_->begin()); }
|
||||
iterator end() { return iterator(wrapped_->end()); }
|
||||
|
||||
private:
|
||||
ColumnFamilySet* wrapped_;
|
||||
};
|
||||
|
||||
// We use ColumnFamilyMemTablesImpl to provide WriteBatch a way to access
|
||||
// memtables of different column families (specified by ID in the write batch)
|
||||
class ColumnFamilyMemTablesImpl : public ColumnFamilyMemTables {
|
||||
|
||||
@@ -138,6 +138,9 @@ TEST_F(CompactFilesTest, MultipleLevel) {
|
||||
collector->ClearFlushedFiles();
|
||||
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.
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db)->TEST_WaitForBackgroundWork());
|
||||
auto l0_files = collector->GetFlushedFiles();
|
||||
ASSERT_OK(db->CompactFiles(CompactionOptions(), l0_files, i));
|
||||
|
||||
@@ -296,6 +299,9 @@ TEST_F(CompactFilesTest, CapturingPendingFiles) {
|
||||
ASSERT_OK(db->Flush(FlushOptions()));
|
||||
}
|
||||
|
||||
// Ensure background work is fully finished including listener callbacks
|
||||
// before accessing listener state.
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db)->TEST_WaitForBackgroundWork());
|
||||
auto l0_files = collector->GetFlushedFiles();
|
||||
EXPECT_EQ(5, l0_files.size());
|
||||
|
||||
@@ -414,6 +420,9 @@ TEST_F(CompactFilesTest, SentinelCompressionType) {
|
||||
ASSERT_OK(db->Put(WriteOptions(), "key", "val"));
|
||||
ASSERT_OK(db->Flush(FlushOptions()));
|
||||
|
||||
// Ensure background work is fully finished including listener callbacks
|
||||
// before accessing listener state.
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db)->TEST_WaitForBackgroundWork());
|
||||
auto l0_files = collector->GetFlushedFiles();
|
||||
ASSERT_EQ(1, l0_files.size());
|
||||
|
||||
|
||||
@@ -277,9 +277,9 @@ Compaction::~Compaction() {
|
||||
|
||||
bool Compaction::InputCompressionMatchesOutput() const {
|
||||
int base_level = input_vstorage_->base_level();
|
||||
bool matches = (GetCompressionType(immutable_options_, input_vstorage_,
|
||||
mutable_cf_options_, start_level_,
|
||||
base_level) == output_compression_);
|
||||
bool matches =
|
||||
(GetCompressionType(input_vstorage_, mutable_cf_options_, start_level_,
|
||||
base_level) == output_compression_);
|
||||
if (matches) {
|
||||
TEST_SYNC_POINT("Compaction::InputCompressionMatchesOutput:Matches");
|
||||
return true;
|
||||
|
||||
@@ -1312,7 +1312,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
uint64_t prev_cpu_micros = db_options_.clock->CPUMicros();
|
||||
uint64_t prev_cpu_micros = db_options_.clock->CPUNanos() / 1000;
|
||||
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
|
||||
@@ -1659,7 +1659,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
|
||||
sub_compact->compaction_job_stats.cpu_micros =
|
||||
db_options_.clock->CPUMicros() - prev_cpu_micros;
|
||||
db_options_.clock->CPUNanos() / 1000 - prev_cpu_micros;
|
||||
|
||||
if (measure_io_stats_) {
|
||||
sub_compact->compaction_job_stats.file_write_nanos +=
|
||||
|
||||
@@ -100,8 +100,7 @@ bool FindIntraL0Compaction(const std::vector<FileMetaData*>& level_files,
|
||||
// If enable_compression is false, then compression is always disabled no
|
||||
// matter what the values of the other two parameters are.
|
||||
// Otherwise, the compression type is determined based on options and level.
|
||||
CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
const VersionStorageInfo* vstorage,
|
||||
CompressionType GetCompressionType(const VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int level, int base_level,
|
||||
const bool enable_compression) {
|
||||
@@ -118,17 +117,19 @@ CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
}
|
||||
// If the user has specified a different compression level for each level,
|
||||
// then pick the compression for that level.
|
||||
if (!ioptions.compression_per_level.empty()) {
|
||||
if (!mutable_cf_options.compression_per_level.empty()) {
|
||||
assert(level == 0 || level >= base_level);
|
||||
int idx = (level == 0) ? 0 : level - base_level + 1;
|
||||
|
||||
const int n = static_cast<int>(ioptions.compression_per_level.size()) - 1;
|
||||
const int n =
|
||||
static_cast<int>(mutable_cf_options.compression_per_level.size()) - 1;
|
||||
// It is possible for level_ to be -1; in that case, we use level
|
||||
// 0's compression. This occurs mostly in backwards compatibility
|
||||
// situations when the builder doesn't know what level the file
|
||||
// belongs to. Likewise, if level is beyond the end of the
|
||||
// specified compression levels, use the last value.
|
||||
return ioptions.compression_per_level[std::max(0, std::min(idx, n))];
|
||||
return mutable_cf_options
|
||||
.compression_per_level[std::max(0, std::min(idx, n))];
|
||||
} else {
|
||||
return mutable_cf_options.compression;
|
||||
}
|
||||
@@ -347,9 +348,8 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
} else {
|
||||
base_level = 1;
|
||||
}
|
||||
compression_type =
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, base_level);
|
||||
compression_type = GetCompressionType(vstorage, mutable_cf_options,
|
||||
output_level, base_level);
|
||||
} else {
|
||||
// TODO(ajkr): `CompactionOptions` offers configurable `CompressionType`
|
||||
// without configurable `CompressionOptions`, which is inconsistent.
|
||||
@@ -637,8 +637,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
ioptions_.compaction_style),
|
||||
/* max_compaction_bytes */ LLONG_MAX,
|
||||
compact_range_options.target_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, 1),
|
||||
GetCompressionType(vstorage, mutable_cf_options, output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
/* grandparents */ {},
|
||||
@@ -816,7 +815,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
ioptions_.level_compaction_dynamic_level_bytes),
|
||||
mutable_cf_options.max_compaction_bytes,
|
||||
compact_range_options.target_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, output_level,
|
||||
GetCompressionType(vstorage, mutable_cf_options, output_level,
|
||||
vstorage->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
|
||||
@@ -304,8 +304,7 @@ bool FindIntraL0Compaction(
|
||||
CompactionInputFiles* comp_inputs,
|
||||
SequenceNumber earliest_mem_seqno = kMaxSequenceNumber);
|
||||
|
||||
CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
const VersionStorageInfo* vstorage,
|
||||
CompressionType GetCompressionType(const VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int level, int base_level,
|
||||
const bool enable_compression = true);
|
||||
|
||||
@@ -343,8 +343,8 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
ioptions_.level_compaction_dynamic_level_bytes),
|
||||
mutable_cf_options_.max_compaction_bytes,
|
||||
GetPathId(ioptions_, mutable_cf_options_, output_level_),
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level_, vstorage_->base_level()),
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level_,
|
||||
vstorage_->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level_),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
|
||||
@@ -746,19 +746,19 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
} else {
|
||||
compaction_reason = CompactionReason::kUniversalSortedRunNum;
|
||||
}
|
||||
return new Compaction(
|
||||
vstorage_, ioptions_, mutable_cf_options_, mutable_db_options_,
|
||||
std::move(inputs), output_level,
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level,
|
||||
kCompactionStyleUniversal),
|
||||
GetMaxOverlappingBytes(), path_id,
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_, start_level,
|
||||
1, enable_compression),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, start_level,
|
||||
enable_compression),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, grandparents, /* is manual */ false, score_,
|
||||
false /* deletion_compaction */, compaction_reason);
|
||||
return new Compaction(vstorage_, ioptions_, mutable_cf_options_,
|
||||
mutable_db_options_, std::move(inputs), output_level,
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level,
|
||||
kCompactionStyleUniversal),
|
||||
GetMaxOverlappingBytes(), path_id,
|
||||
GetCompressionType(vstorage_, mutable_cf_options_,
|
||||
start_level, 1, enable_compression),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_,
|
||||
start_level, enable_compression),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, grandparents,
|
||||
/* is manual */ false, score_,
|
||||
false /* deletion_compaction */, compaction_reason);
|
||||
}
|
||||
|
||||
// Look at overall size amplification. If size amplification
|
||||
@@ -1076,8 +1076,8 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level,
|
||||
kCompactionStyleUniversal),
|
||||
GetMaxOverlappingBytes(), path_id,
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level, 1, true /* enable_compression */),
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1,
|
||||
true /* enable_compression */),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
Temperature::kUnknown,
|
||||
@@ -1220,8 +1220,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level,
|
||||
kCompactionStyleUniversal),
|
||||
/* max_grandparent_overlap_bytes */ GetMaxOverlappingBytes(), path_id,
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level, 1),
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, grandparents, /* is manual */ false, score_,
|
||||
@@ -1294,8 +1293,8 @@ Compaction* UniversalCompactionBuilder::PickCompactionToOldest(
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level,
|
||||
kCompactionStyleUniversal),
|
||||
GetMaxOverlappingBytes(), path_id,
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level, 1, true /* enable_compression */),
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1,
|
||||
true /* enable_compression */),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
Temperature::kUnknown,
|
||||
|
||||
@@ -1540,6 +1540,93 @@ TEST_P(DBFilterConstructionCorruptionTestWithParam, DetectCorruption) {
|
||||
"TamperFilter");
|
||||
}
|
||||
|
||||
// RocksDB lite does not support dynamic options
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_P(DBFilterConstructionCorruptionTestWithParam,
|
||||
DynamicallyTurnOnAndOffDetectConstructCorruption) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
|
||||
// We intend to turn on
|
||||
// table_options.detect_filter_construct_corruption dynamically
|
||||
// therefore we override this test parmater's value
|
||||
table_options.detect_filter_construct_corruption = false;
|
||||
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.create_if_missing = true;
|
||||
|
||||
int num_key = static_cast<int>(GetNumKey());
|
||||
Status s;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Case 1: !table_options.detect_filter_construct_corruption
|
||||
for (int i = 0; i < num_key; i++) {
|
||||
ASSERT_OK(Put(Key(i), Key(i)));
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"XXPH3FilterBitsBuilder::Finish::TamperHashEntries", [&](void* arg) {
|
||||
std::deque<uint64_t>* hash_entries_to_corrupt =
|
||||
(std::deque<uint64_t>*)arg;
|
||||
assert(!hash_entries_to_corrupt->empty());
|
||||
*(hash_entries_to_corrupt->begin()) =
|
||||
*(hash_entries_to_corrupt->begin()) ^ uint64_t { 1 };
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
s = Flush();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearCallBack(
|
||||
"XXPH3FilterBitsBuilder::Finish::"
|
||||
"TamperHashEntries");
|
||||
|
||||
ASSERT_FALSE(table_options.detect_filter_construct_corruption);
|
||||
EXPECT_TRUE(s.ok());
|
||||
|
||||
// Case 2: dynamically turn on
|
||||
// table_options.detect_filter_construct_corruption
|
||||
ASSERT_OK(db_->SetOptions({{"block_based_table_factory",
|
||||
"{detect_filter_construct_corruption=true;}"}}));
|
||||
|
||||
for (int i = 0; i < num_key; i++) {
|
||||
ASSERT_OK(Put(Key(i), Key(i)));
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"XXPH3FilterBitsBuilder::Finish::TamperHashEntries", [&](void* arg) {
|
||||
std::deque<uint64_t>* hash_entries_to_corrupt =
|
||||
(std::deque<uint64_t>*)arg;
|
||||
assert(!hash_entries_to_corrupt->empty());
|
||||
*(hash_entries_to_corrupt->begin()) =
|
||||
*(hash_entries_to_corrupt->begin()) ^ uint64_t { 1 };
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
s = Flush();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearCallBack(
|
||||
"XXPH3FilterBitsBuilder::Finish::"
|
||||
"TamperHashEntries");
|
||||
|
||||
auto updated_table_options =
|
||||
db_->GetOptions().table_factory->GetOptions<BlockBasedTableOptions>();
|
||||
EXPECT_TRUE(updated_table_options->detect_filter_construct_corruption);
|
||||
EXPECT_TRUE(s.IsCorruption());
|
||||
EXPECT_TRUE(s.ToString().find("Filter's hash entries checksum mismatched") !=
|
||||
std::string::npos);
|
||||
|
||||
// Case 3: dynamically turn off
|
||||
// table_options.detect_filter_construct_corruption
|
||||
ASSERT_OK(db_->SetOptions({{"block_based_table_factory",
|
||||
"{detect_filter_construct_corruption=false;}"}}));
|
||||
updated_table_options =
|
||||
db_->GetOptions().table_factory->GetOptions<BlockBasedTableOptions>();
|
||||
EXPECT_FALSE(updated_table_options->detect_filter_construct_corruption);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
namespace {
|
||||
// NOTE: This class is referenced by HISTORY.md as a model for a wrapper
|
||||
// FilterPolicy selecting among configurations based on context.
|
||||
@@ -1551,15 +1638,9 @@ class LevelAndStyleCustomFilterPolicy : public FilterPolicy {
|
||||
policy_l0_other_(NewBloomFilterPolicy(bpk_l0_other)),
|
||||
policy_otherwise_(NewBloomFilterPolicy(bpk_otherwise)) {}
|
||||
|
||||
const char* Name() const override {
|
||||
return "LevelAndStyleCustomFilterPolicy";
|
||||
}
|
||||
|
||||
// OK to use built-in policy name because we are deferring to a
|
||||
// built-in builder. We aren't changing the serialized format.
|
||||
const char* CompatibilityName() const override {
|
||||
return policy_fifo_->CompatibilityName();
|
||||
}
|
||||
const char* Name() const override { return policy_fifo_->Name(); }
|
||||
|
||||
FilterBitsBuilder* GetBuilderWithContext(
|
||||
const FilterBuildingContext& context) const override {
|
||||
@@ -2169,7 +2250,6 @@ class BloomStatsTestWithParam
|
||||
options_.table_factory.reset(NewPlainTableFactory(table_options));
|
||||
} else {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.hash_index_allow_collision = false;
|
||||
if (partition_filters_) {
|
||||
assert(bfp_impl_ != kDeprecatedBlock);
|
||||
table_options.partition_filters = partition_filters_;
|
||||
|
||||
@@ -6896,7 +6896,6 @@ TEST_F(DBCompactionTest, DisableMultiManualCompaction) {
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
Reopen(options);
|
||||
|
||||
// Generate 2 levels of file to make sure the manual compaction is not skipped
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put(Key(i), "value"));
|
||||
if (i % 2) {
|
||||
@@ -6940,8 +6939,6 @@ TEST_F(DBCompactionTest, DisableMultiManualCompaction) {
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
});
|
||||
|
||||
// Disable manual compaction should cancel both manual compactions and both
|
||||
// compaction should return incomplete.
|
||||
db_->DisableManualCompaction();
|
||||
|
||||
compact_thread1.join();
|
||||
@@ -6966,14 +6963,12 @@ TEST_F(DBCompactionTest, DisableJustStartedManualCompaction) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
// make sure the manual compaction background is started but not yet set the
|
||||
// status to in_progress, then cancel the manual compaction, which should not
|
||||
// result in segfault
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BGWorkCompaction",
|
||||
"DBCompactionTest::DisableJustStartedManualCompaction:"
|
||||
"PreDisableManualCompaction"},
|
||||
{"DBImpl::RunManualCompaction:Unscheduled",
|
||||
{"DBCompactionTest::DisableJustStartedManualCompaction:"
|
||||
"ManualCompactionReturn",
|
||||
"BackgroundCallCompaction:0"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -6982,6 +6977,9 @@ TEST_F(DBCompactionTest, DisableJustStartedManualCompaction) {
|
||||
cro.exclusive_manual_compaction = true;
|
||||
auto s = db_->CompactRange(cro, nullptr, nullptr);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::DisableJustStartedManualCompaction:"
|
||||
"ManualCompactionReturn");
|
||||
});
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::DisableJustStartedManualCompaction:"
|
||||
@@ -6991,43 +6989,6 @@ TEST_F(DBCompactionTest, DisableJustStartedManualCompaction) {
|
||||
compact_thread.join();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableInProgressManualCompaction) {
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
Reopen(options);
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction:InProgress",
|
||||
"DBCompactionTest::DisableInProgressManualCompaction:"
|
||||
"PreDisableManualCompaction"},
|
||||
{"DBImpl::RunManualCompaction:Unscheduled",
|
||||
"CompactionJob::Run():Start"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// generate files, but avoid trigger auto compaction
|
||||
for (int i = 0; i < kNumL0Files / 2; i++) {
|
||||
ASSERT_OK(Put(Key(1), "value1"));
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
port::Thread compact_thread([&]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = true;
|
||||
auto s = db_->CompactRange(cro, nullptr, nullptr);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::DisableInProgressManualCompaction:"
|
||||
"PreDisableManualCompaction");
|
||||
db_->DisableManualCompaction();
|
||||
|
||||
compact_thread.join();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableManualCompactionThreadQueueFull) {
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
|
||||
@@ -45,15 +45,17 @@ Status DBImpl::FlushForGetLiveFiles() {
|
||||
}
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions(), FlushReason::kGetLiveFiles);
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:1");
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:2");
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (!status.ok() && !status.IsColumnFamilyDropped()) {
|
||||
break;
|
||||
} else if (status.IsColumnFamilyDropped()) {
|
||||
@@ -61,6 +63,7 @@ Status DBImpl::FlushForGetLiveFiles() {
|
||||
}
|
||||
}
|
||||
}
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -1575,6 +1575,9 @@ TEST_F(DBFlushTest, FireOnFlushCompletedAfterCommittedResult) {
|
||||
flush_opts.wait = false;
|
||||
ASSERT_OK(db_->Flush(flush_opts));
|
||||
t1.join();
|
||||
// Ensure background work is fully finished including listener callbacks
|
||||
// before accessing listener state.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
ASSERT_TRUE(listener->completed1);
|
||||
ASSERT_TRUE(listener->completed2);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
@@ -171,9 +171,7 @@ Status CompactedDBImpl::Open(const Options& options,
|
||||
std::unique_ptr<CompactedDBImpl> db(new CompactedDBImpl(db_options, dbname));
|
||||
Status s = db->Init(options);
|
||||
if (s.ok()) {
|
||||
s = db->StartPeriodicWorkScheduler();
|
||||
}
|
||||
if (s.ok()) {
|
||||
db->StartPeriodicWorkScheduler();
|
||||
ROCKS_LOG_INFO(db->immutable_db_options_.info_log,
|
||||
"Opened the db as fully compacted mode");
|
||||
LogFlush(db->immutable_db_options_.info_log);
|
||||
|
||||
+35
-31
@@ -119,18 +119,16 @@ CompressionType GetCompressionFlush(
|
||||
// Compressing memtable flushes might not help unless the sequential load
|
||||
// optimization is used for leveled compaction. Otherwise the CPU and
|
||||
// latency overhead is not offset by saving much space.
|
||||
if (ioptions.compaction_style == kCompactionStyleUniversal) {
|
||||
if (mutable_cf_options.compaction_options_universal
|
||||
.compression_size_percent < 0) {
|
||||
return mutable_cf_options.compression;
|
||||
} else {
|
||||
return kNoCompression;
|
||||
}
|
||||
} else if (!ioptions.compression_per_level.empty()) {
|
||||
// For leveled compress when min_level_to_compress != 0.
|
||||
return ioptions.compression_per_level[0];
|
||||
} else {
|
||||
if (ioptions.compaction_style == kCompactionStyleUniversal &&
|
||||
mutable_cf_options.compaction_options_universal
|
||||
.compression_size_percent >= 0) {
|
||||
return kNoCompression;
|
||||
}
|
||||
if (mutable_cf_options.compression_per_level.empty()) {
|
||||
return mutable_cf_options.compression;
|
||||
} else {
|
||||
// For leveled compress when min_level_to_compress != 0.
|
||||
return mutable_cf_options.compression_per_level[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,12 +375,15 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
s = AtomicFlushMemTables(cfds, flush_opts, context.flush_reason);
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
s = FlushMemTable(cfd, flush_opts, context.flush_reason);
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -494,14 +495,18 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
s.PermitUncheckedError(); //**TODO: What to do on error?
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (!cfd->IsDropped() && cfd->initialized() && !cfd->mem()->IsEmpty()) {
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
Status s = FlushMemTable(cfd, FlushOptions(), FlushReason::kShutDown);
|
||||
s.PermitUncheckedError(); //**TODO: What to do on error?
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
}
|
||||
|
||||
shutting_down_.store(true, std::memory_order_release);
|
||||
@@ -763,7 +768,7 @@ void DBImpl::PrintStatistics() {
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::StartPeriodicWorkScheduler() {
|
||||
void DBImpl::StartPeriodicWorkScheduler() {
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -773,7 +778,7 @@ Status DBImpl::StartPeriodicWorkScheduler() {
|
||||
"DBImpl::StartPeriodicWorkScheduler:DisableScheduler",
|
||||
&disable_scheduler);
|
||||
if (disable_scheduler) {
|
||||
return Status::OK();
|
||||
return;
|
||||
}
|
||||
#endif // !NDEBUG
|
||||
|
||||
@@ -784,11 +789,10 @@ Status DBImpl::StartPeriodicWorkScheduler() {
|
||||
&periodic_work_scheduler_);
|
||||
}
|
||||
|
||||
return periodic_work_scheduler_->Register(
|
||||
periodic_work_scheduler_->Register(
|
||||
this, mutable_db_options_.stats_dump_period_sec,
|
||||
mutable_db_options_.stats_persist_period_sec);
|
||||
#endif // !ROCKSDB_LITE
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// esitmate the total size of stats_history_
|
||||
@@ -964,13 +968,18 @@ void DBImpl::DumpStats() {
|
||||
TEST_SYNC_POINT("DBImpl::DumpStats:StartRunning");
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->initialized()) {
|
||||
// Release DB mutex for gathering cache entry stats. Pass over all
|
||||
// column families for this first so that other stats are dumped
|
||||
// near-atomically.
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
cfd->internal_stats()->CollectCacheEntryStats(/*foreground=*/false);
|
||||
// Get a ref before unlocking
|
||||
cfd->Ref();
|
||||
{
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
cfd->internal_stats()->CollectCacheEntryStats(/*foreground=*/false);
|
||||
}
|
||||
cfd->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1217,7 +1226,7 @@ Status DBImpl::SetDBOptions(
|
||||
mutable_db_options_.stats_persist_period_sec) {
|
||||
mutex_.Unlock();
|
||||
periodic_work_scheduler_->Unregister(this);
|
||||
s = periodic_work_scheduler_->Register(
|
||||
periodic_work_scheduler_->Register(
|
||||
this, new_options.stats_dump_period_sec,
|
||||
new_options.stats_persist_period_sec);
|
||||
mutex_.Lock();
|
||||
@@ -3195,12 +3204,6 @@ bool CfdListContains(const CfdList& list, ColumnFamilyData* cfd) {
|
||||
} // namespace
|
||||
|
||||
void DBImpl::ReleaseSnapshot(const Snapshot* s) {
|
||||
if (s == nullptr) {
|
||||
// DBImpl::GetSnapshot() can return nullptr when snapshot
|
||||
// not supported by specifying the condition:
|
||||
// inplace_update_support enabled.
|
||||
return;
|
||||
}
|
||||
const SnapshotImpl* casted_s = reinterpret_cast<const SnapshotImpl*>(s);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -3480,13 +3483,15 @@ bool DBImpl::GetAggregatedIntProperty(const Slice& property,
|
||||
// Needs mutex to protect the list of column families.
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
uint64_t value;
|
||||
for (auto* cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
for (auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (!cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
ret = GetIntPropertyInternal(cfd, *property_info, true, &value);
|
||||
// GetIntPropertyInternal may release db mutex and re-acquire it.
|
||||
mutex_.AssertHeld();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (ret) {
|
||||
sum += value;
|
||||
} else {
|
||||
@@ -5077,7 +5082,6 @@ Status DBImpl::VerifyChecksumInternal(const ReadOptions& read_options,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: simplify using GetRefedColumnFamilySet?
|
||||
std::vector<ColumnFamilyData*> cfd_list;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
+16
-28
@@ -1514,31 +1514,19 @@ class DBImpl : public DB {
|
||||
|
||||
// Information for a manual compaction
|
||||
struct ManualCompactionState {
|
||||
ManualCompactionState(ColumnFamilyData* _cfd, int _input_level,
|
||||
int _output_level, uint32_t _output_path_id,
|
||||
bool _exclusive, bool _disallow_trivial_move,
|
||||
std::atomic<bool>* _canceled)
|
||||
: cfd(_cfd),
|
||||
input_level(_input_level),
|
||||
output_level(_output_level),
|
||||
output_path_id(_output_path_id),
|
||||
exclusive(_exclusive),
|
||||
disallow_trivial_move(_disallow_trivial_move),
|
||||
canceled(_canceled) {}
|
||||
|
||||
ColumnFamilyData* cfd;
|
||||
int input_level;
|
||||
int output_level;
|
||||
uint32_t output_path_id;
|
||||
Status status;
|
||||
bool done = false;
|
||||
bool in_progress = false; // compaction request being processed?
|
||||
bool incomplete = false; // only part of requested range compacted
|
||||
bool exclusive; // current behavior of only one manual
|
||||
bool disallow_trivial_move; // Force actual compaction to run
|
||||
const InternalKey* begin = nullptr; // nullptr means beginning of key range
|
||||
const InternalKey* end = nullptr; // nullptr means end of key range
|
||||
InternalKey* manual_end = nullptr; // how far we are compacting
|
||||
bool done;
|
||||
bool in_progress; // compaction request being processed?
|
||||
bool incomplete; // only part of requested range compacted
|
||||
bool exclusive; // current behavior of only one manual
|
||||
bool disallow_trivial_move; // Force actual compaction to run
|
||||
const InternalKey* begin; // nullptr means beginning of key range
|
||||
const InternalKey* end; // nullptr means end of key range
|
||||
InternalKey* manual_end; // how far we are compacting
|
||||
InternalKey tmp_storage; // Used to keep track of compaction progress
|
||||
InternalKey tmp_storage1; // Used to keep track of compaction progress
|
||||
std::atomic<bool>* canceled; // Compaction canceled by the user?
|
||||
@@ -1781,9 +1769,12 @@ class DBImpl : public DB {
|
||||
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)
|
||||
// associated with this WriteToWAL
|
||||
IOStatus WriteToWAL(const WriteBatch& merged_batch, log::Writer* log_writer,
|
||||
uint64_t* log_used, uint64_t* log_size,
|
||||
bool with_db_mutex = false, bool with_log_mutex = false);
|
||||
Env::IOPriority rate_limiter_priority);
|
||||
|
||||
IOStatus WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
@@ -1879,7 +1870,7 @@ class DBImpl : public DB {
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Schedule background tasks
|
||||
Status StartPeriodicWorkScheduler();
|
||||
void StartPeriodicWorkScheduler();
|
||||
|
||||
void PrintStatistics();
|
||||
|
||||
@@ -2109,15 +2100,12 @@ class DBImpl : public DB {
|
||||
bool persistent_stats_cfd_exists_ = true;
|
||||
|
||||
// Without two_write_queues, read and writes to alive_log_files_ are
|
||||
// protected by mutex_. With two_write_queues_, writes
|
||||
// protected by mutex_. However since back() is never popped, and push_back()
|
||||
// is done only from write_thread_, the same thread can access the item
|
||||
// reffered by back() without mutex_. With two_write_queues_, writes
|
||||
// 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
|
||||
|
||||
@@ -382,13 +382,14 @@ Status DBImpl::FlushMemTablesToOutputFiles(
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
const auto& bg_flush_arg = bg_flush_args[0];
|
||||
ColumnFamilyData* cfd = bg_flush_arg.cfd_;
|
||||
MutableCFOptions mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
// intentional infrequent copy for each flush
|
||||
MutableCFOptions mutable_cf_options_copy = *cfd->GetLatestMutableCFOptions();
|
||||
SuperVersionContext* superversion_context =
|
||||
bg_flush_arg.superversion_context_;
|
||||
Status s = FlushMemTableToOutputFile(
|
||||
cfd, mutable_cf_options, made_progress, job_context, superversion_context,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
log_buffer, thread_pri);
|
||||
cfd, mutable_cf_options_copy, made_progress, job_context,
|
||||
superversion_context, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, log_buffer, thread_pri);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1789,13 +1790,19 @@ Status DBImpl::RunManualCompaction(
|
||||
CompactionArg* ca = nullptr;
|
||||
|
||||
bool scheduled = false;
|
||||
bool unscheduled = false;
|
||||
Env::Priority thread_pool_priority = Env::Priority::TOTAL;
|
||||
bool manual_conflict = false;
|
||||
|
||||
ManualCompactionState manual(
|
||||
cfd, input_level, output_level, compact_range_options.target_path_id,
|
||||
exclusive, disallow_trivial_move, compact_range_options.canceled);
|
||||
ManualCompactionState manual;
|
||||
manual.cfd = cfd;
|
||||
manual.input_level = input_level;
|
||||
manual.output_level = output_level;
|
||||
manual.output_path_id = compact_range_options.target_path_id;
|
||||
manual.done = false;
|
||||
manual.in_progress = false;
|
||||
manual.incomplete = false;
|
||||
manual.exclusive = exclusive;
|
||||
manual.disallow_trivial_move = disallow_trivial_move;
|
||||
manual.canceled = compact_range_options.canceled;
|
||||
// For universal compaction, we enforce every manual compaction to compact
|
||||
// all files.
|
||||
if (begin == nullptr ||
|
||||
@@ -1900,22 +1907,15 @@ Status DBImpl::RunManualCompaction(
|
||||
assert(!exclusive || !manual_conflict);
|
||||
// Running either this or some other manual compaction
|
||||
bg_cv_.Wait();
|
||||
if (manual_compaction_paused_ > 0 && scheduled && !unscheduled) {
|
||||
if (manual_compaction_paused_ > 0 && !manual.done &&
|
||||
!manual.in_progress) {
|
||||
manual.done = true;
|
||||
manual.status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
assert(thread_pool_priority != Env::Priority::TOTAL);
|
||||
// unschedule all manual compactions
|
||||
auto unscheduled_task_num = env_->UnSchedule(
|
||||
GetTaskTag(TaskType::kManualCompaction), thread_pool_priority);
|
||||
if (unscheduled_task_num > 0) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"[%s] Unscheduled %d number of manual compactions from the "
|
||||
"thread-pool",
|
||||
cfd->GetName().c_str(), unscheduled_task_num);
|
||||
// it may unschedule other manual compactions, notify others.
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
unscheduled = true;
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction:Unscheduled");
|
||||
env_->UnSchedule(GetTaskTag(TaskType::kManualCompaction),
|
||||
thread_pool_priority);
|
||||
break;
|
||||
}
|
||||
if (scheduled && manual.incomplete == true) {
|
||||
assert(!manual.in_progress);
|
||||
@@ -2699,12 +2699,6 @@ void DBImpl::UnscheduleCompactionCallback(void* arg) {
|
||||
CompactionArg ca = *(ca_ptr);
|
||||
delete reinterpret_cast<CompactionArg*>(arg);
|
||||
if (ca.prepicked_compaction != nullptr) {
|
||||
// if it's a manual compaction, set status to ManualCompactionPaused
|
||||
if (ca.prepicked_compaction->manual_compaction_state) {
|
||||
ca.prepicked_compaction->manual_compaction_state->done = true;
|
||||
ca.prepicked_compaction->manual_compaction_state->status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
if (ca.prepicked_compaction->compaction != nullptr) {
|
||||
ca.prepicked_compaction->compaction->ReleaseCompactionFiles(
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused));
|
||||
@@ -2992,6 +2986,8 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
bg_bottom_compaction_scheduled_--;
|
||||
}
|
||||
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
|
||||
// See if there's more work to be done
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
|
||||
@@ -3087,8 +3083,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
manual_compaction->in_progress = true;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:InProgress");
|
||||
|
||||
std::unique_ptr<TaskLimiterToken> task_token;
|
||||
|
||||
// InternalKey manual_end_storage;
|
||||
|
||||
@@ -23,7 +23,11 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
uint64_t DBImpl::MinLogNumberToKeep() {
|
||||
return versions_->min_log_number_to_keep();
|
||||
if (allow_2pc()) {
|
||||
return versions_->min_log_number_to_keep_2pc();
|
||||
} else {
|
||||
return versions_->MinLogNumberWithUnflushedData();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
|
||||
@@ -220,6 +224,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
}
|
||||
|
||||
// Add log files in wal_dir
|
||||
|
||||
if (!immutable_db_options_.IsWalDirSameAsDBPath(dbname_)) {
|
||||
std::vector<std::string> log_files;
|
||||
Status s = env_->GetChildren(immutable_db_options_.wal_dir, &log_files);
|
||||
@@ -229,7 +234,6 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
log_file, immutable_db_options_.wal_dir);
|
||||
}
|
||||
}
|
||||
|
||||
// Add info log files in db_log_dir
|
||||
if (!immutable_db_options_.db_log_dir.empty() &&
|
||||
immutable_db_options_.db_log_dir != dbname_) {
|
||||
|
||||
@@ -866,11 +866,6 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
bool flushed = false;
|
||||
uint64_t corrupted_wal_number = kMaxSequenceNumber;
|
||||
uint64_t min_wal_number = MinLogNumberToKeep();
|
||||
if (!allow_2pc()) {
|
||||
// In non-2pc mode, we skip WALs that do not back unflushed data.
|
||||
min_wal_number =
|
||||
std::max(min_wal_number, versions_->MinLogNumberWithUnflushedData());
|
||||
}
|
||||
for (auto wal_number : wal_numbers) {
|
||||
if (wal_number < min_wal_number) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
@@ -1275,16 +1270,9 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
}
|
||||
|
||||
std::unique_ptr<VersionEdit> wal_deletion;
|
||||
if (flushed) {
|
||||
wal_deletion = std::make_unique<VersionEdit>();
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest) {
|
||||
wal_deletion->DeleteWalsBefore(max_wal_number + 1);
|
||||
}
|
||||
if (!allow_2pc()) {
|
||||
// In non-2pc mode, flushing the memtables of the column families
|
||||
// means we can advance min_log_number_to_keep.
|
||||
wal_deletion->SetMinLogNumberToKeep(max_wal_number + 1);
|
||||
}
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest) {
|
||||
wal_deletion.reset(new VersionEdit);
|
||||
wal_deletion->DeleteWalsBefore(max_wal_number + 1);
|
||||
edit_lists.back().push_back(wal_deletion.get());
|
||||
}
|
||||
|
||||
@@ -1363,14 +1351,7 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& wal_numbers) {
|
||||
// FindObsoleteFiles()
|
||||
total_log_size_ = 0;
|
||||
log_empty_ = false;
|
||||
uint64_t min_wal_with_unflushed_data =
|
||||
versions_->MinLogNumberWithUnflushedData();
|
||||
for (auto wal_number : wal_numbers) {
|
||||
if (!allow_2pc() && wal_number < min_wal_with_unflushed_data) {
|
||||
// In non-2pc mode, the WAL files not backing unflushed data are not
|
||||
// alive, thus should not be added to the alive_log_files_.
|
||||
continue;
|
||||
}
|
||||
// We preallocate space for wals, but then after a crash and restart, those
|
||||
// preallocated space are not needed anymore. It is likely only the last
|
||||
// log has such preallocated space, so we only truncate for the last log.
|
||||
@@ -1383,7 +1364,6 @@ 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();
|
||||
}
|
||||
@@ -1729,7 +1709,6 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
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();
|
||||
}
|
||||
@@ -1751,7 +1730,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
uint64_t log_used, log_size;
|
||||
log::Writer* log_writer = impl->logs_.back().writer;
|
||||
s = impl->WriteToWAL(empty_batch, log_writer, &log_used, &log_size,
|
||||
/*with_db_mutex==*/true);
|
||||
Env::IO_TOTAL);
|
||||
if (s.ok()) {
|
||||
// Need to fsync, otherwise it might get lost after a power reset.
|
||||
s = impl->FlushWAL(false);
|
||||
@@ -1911,9 +1890,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
persist_options_status.ToString().c_str());
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = impl->StartPeriodicWorkScheduler();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
impl->StartPeriodicWorkScheduler();
|
||||
} else {
|
||||
for (auto* h : *handles) {
|
||||
delete h;
|
||||
}
|
||||
|
||||
+32
-23
@@ -130,8 +130,31 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
assert(!seq_per_batch_ || batch_cnt != 0);
|
||||
if (my_batch == nullptr) {
|
||||
return Status::Corruption("Batch is nullptr!");
|
||||
} else if (WriteBatchInternal::TimestampsUpdateNeeded(*my_batch)) {
|
||||
} else if (!disable_memtable &&
|
||||
WriteBatchInternal::TimestampsUpdateNeeded(*my_batch)) {
|
||||
// If writing to memtable, then we require the caller to set/update the
|
||||
// timestamps for the keys in the write batch.
|
||||
// Otherwise, it means we are just writing to the WAL, and we allow
|
||||
// timestamps unset for the keys in the write batch. This can happen if we
|
||||
// use TransactionDB with write-committed policy, and we currently do not
|
||||
// support user-defined timestamp with other policies.
|
||||
// In the prepare phase, a transaction can write the batch to the WAL
|
||||
// without inserting to memtable. The keys in the batch do not have to be
|
||||
// assigned timestamps because they will be used only during recovery if
|
||||
// there is a commit marker which includes their commit timestamp.
|
||||
return Status::InvalidArgument("write batch must have timestamp(s) set");
|
||||
} else if (write_options.rate_limiter_priority != Env::IO_TOTAL &&
|
||||
write_options.rate_limiter_priority != Env::IO_USER) {
|
||||
return Status::InvalidArgument(
|
||||
"WriteOptions::rate_limiter_priority only allows "
|
||||
"Env::IO_TOTAL and Env::IO_USER due to implementation constraints");
|
||||
} else if (write_options.rate_limiter_priority != Env::IO_TOTAL &&
|
||||
(write_options.disableWAL || manual_wal_flush_)) {
|
||||
return Status::InvalidArgument(
|
||||
"WriteOptions::rate_limiter_priority currently only supports "
|
||||
"rate-limiting automatic WAL flush, which requires "
|
||||
"`WriteOptions::disableWAL` and "
|
||||
"`DBOptions::manual_wal_flush` both set to false");
|
||||
}
|
||||
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
|
||||
// grabs but does not seem thread-safe.
|
||||
@@ -1148,17 +1171,8 @@ WriteBatch* DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
uint64_t* log_size,
|
||||
bool with_db_mutex, bool with_log_mutex) {
|
||||
Env::IOPriority rate_limiter_priority) {
|
||||
assert(log_size != nullptr);
|
||||
|
||||
// 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
|
||||
@@ -1172,7 +1186,7 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
IOStatus io_s = log_writer->AddRecord(log_entry);
|
||||
IOStatus io_s = log_writer->AddRecord(log_entry, rate_limiter_priority);
|
||||
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Unlock();
|
||||
@@ -1181,12 +1195,9 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
*log_used = logfile_number_;
|
||||
}
|
||||
total_log_size_ += log_entry.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);
|
||||
// TODO(myabandeh): it might be unsafe to access alive_log_files_.back() here
|
||||
// since alive_log_files_ might be modified concurrently
|
||||
alive_log_files_.back().AddSize(log_entry.size());
|
||||
log_empty_ = false;
|
||||
return io_s;
|
||||
}
|
||||
@@ -1196,7 +1207,6 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
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;
|
||||
@@ -1214,7 +1224,8 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
WriteBatchInternal::SetSequence(merged_batch, sequence);
|
||||
|
||||
uint64_t log_size;
|
||||
io_s = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
io_s = WriteToWAL(*merged_batch, log_writer, log_used, &log_size,
|
||||
write_group.leader->rate_limiter_priority);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
@@ -1284,7 +1295,6 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
SequenceNumber* last_sequence, size_t seq_inc) {
|
||||
IOStatus io_s;
|
||||
|
||||
assert(two_write_queues_ || immutable_db_options_.unordered_write);
|
||||
assert(!write_group.leader->disable_wal);
|
||||
// Same holds for all in the batch group
|
||||
WriteBatch tmp_batch;
|
||||
@@ -1310,7 +1320,7 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
uint64_t log_size;
|
||||
io_s = WriteToWAL(*merged_batch, log_writer, log_used, &log_size,
|
||||
/*with_db_mutex=*/false, /*with_log_mutex=*/true);
|
||||
write_group.leader->rate_limiter_priority);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
@@ -1964,7 +1974,6 @@ 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();
|
||||
}
|
||||
|
||||
@@ -169,36 +169,6 @@ TEST_F(DBTestInPlaceUpdate, InPlaceUpdateCallbackNoAction) {
|
||||
ASSERT_EQ(Get(1, "key"), "NOT_FOUND");
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBTestInPlaceUpdate, InPlaceUpdateAndSnapshot) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.inplace_update_support = true;
|
||||
options.env = env_;
|
||||
options.write_buffer_size = 100000;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
Reopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Update key with values of smaller size, and
|
||||
// run GetSnapshot and ReleaseSnapshot
|
||||
int numValues = 2;
|
||||
for (int i = numValues; i > 0; i--) {
|
||||
const Snapshot* s = db_->GetSnapshot();
|
||||
ASSERT_EQ(nullptr, s);
|
||||
std::string value = DummyString(i, 'a');
|
||||
ASSERT_OK(Put(1, "key", value));
|
||||
ASSERT_EQ(value, Get(1, "key"));
|
||||
// release s (nullptr)
|
||||
db_->ReleaseSnapshot(s);
|
||||
}
|
||||
|
||||
// Only 1 instance for that key.
|
||||
validateNumberOfEntries(1, 1);
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -167,9 +167,14 @@ TEST_F(DBLogicalBlockSizeCacheTest, CreateColumnFamily) {
|
||||
}
|
||||
|
||||
TEST_F(DBLogicalBlockSizeCacheTest, CreateColumnFamilies) {
|
||||
// Tests that CreateColumnFamilies will cache the cf_paths,
|
||||
// drop the column family handle won't drop the cache,
|
||||
// drop and then delete the column family handle will drop the cache.
|
||||
// To test:
|
||||
// (1) CreateColumnFamilies will cache the cf_paths in
|
||||
// DBLogicalBlockSizeCache
|
||||
// (2) Dropping column family handles associated with
|
||||
// that cf_paths won't drop the cached cf_paths
|
||||
// (3) Deleting all the column family handles associated
|
||||
// with that cf_paths will drop the cached cf_paths
|
||||
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_.get();
|
||||
@@ -190,7 +195,7 @@ TEST_F(DBLogicalBlockSizeCacheTest, CreateColumnFamilies) {
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(2, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
// Drop column family does not drop cache.
|
||||
// Drop column family does not drop cf_path_0_'s entry from cache
|
||||
for (ColumnFamilyHandle* cf : cfs) {
|
||||
ASSERT_OK(db->DropColumnFamily(cf));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
@@ -200,22 +205,24 @@ TEST_F(DBLogicalBlockSizeCacheTest, CreateColumnFamilies) {
|
||||
ASSERT_EQ(2, cache_->GetRefCount(cf_path_0_));
|
||||
}
|
||||
|
||||
// Delete one handle will not drop cache because another handle is still
|
||||
// referencing cf_path_0_.
|
||||
// Delete one cf handle will not drop cf_path_0_'s entry from cache because
|
||||
// another handle is still referencing cf_path_0_.
|
||||
ASSERT_OK(db->DestroyColumnFamilyHandle(cfs[0]));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
// Delete the last handle will drop cache.
|
||||
// Delete all cf handles and ensure the ref count of cf_path_0_ in cache_
|
||||
// can be properly decreased by releasing any background reference to the
|
||||
// ColumnFamilyData during db deletion
|
||||
ASSERT_OK(db->DestroyColumnFamilyHandle(cfs[1]));
|
||||
ASSERT_EQ(1, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
|
||||
delete db;
|
||||
|
||||
// Now cf_path_0_ in cache_ has been properly decreased and cf_path_0_'s entry
|
||||
// is dropped from cache
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
ASSERT_OK(DestroyDB(dbname_, options,
|
||||
{{"cf1", cf_options}, {"cf2", cf_options}}));
|
||||
|
||||
@@ -1067,11 +1067,17 @@ TEST_F(DBPropertiesTest, EstimateCompressionRatio) {
|
||||
const int kNumEntriesPerFile = 1000;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression_per_level = {kNoCompression, kSnappyCompression};
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 2;
|
||||
options.num_levels = 3;
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(db_->SetOptions(
|
||||
{{"compression_per_level", "kNoCompression:kSnappyCompression"}}));
|
||||
auto opts = db_->GetOptions();
|
||||
ASSERT_EQ(opts.compression_per_level.size(), 2);
|
||||
ASSERT_EQ(opts.compression_per_level[0], kNoCompression);
|
||||
ASSERT_EQ(opts.compression_per_level[1], kSnappyCompression);
|
||||
|
||||
// compression ratio is -1.0 when no open files at level
|
||||
ASSERT_EQ(CompressionRatioAtLevel(0), -1.0);
|
||||
|
||||
|
||||
@@ -1724,34 +1724,6 @@ TEST_F(DBRangeDelTest, OverlappedKeys) {
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(1));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, IteratorRefresh) {
|
||||
// Refreshing an iterator after a range tombstone is added should cause the
|
||||
// deleted range of keys to disappear.
|
||||
for (bool sv_changed : {false, true}) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key1", "value1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key2", "value2"));
|
||||
|
||||
auto* iter = db_->NewIterator(ReadOptions());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
"key2", "key3"));
|
||||
|
||||
if (sv_changed) {
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
|
||||
ASSERT_OK(iter->Refresh());
|
||||
ASSERT_OK(iter->status());
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ("key1", iter->key());
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+195
-11
@@ -3,18 +3,26 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/file_checksum_helper.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBRateLimiterTest
|
||||
class DBRateLimiterOnReadTest
|
||||
: public DBTestBase,
|
||||
public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> {
|
||||
public:
|
||||
DBRateLimiterTest()
|
||||
: DBTestBase("db_rate_limiter_test", /*env_do_fsync=*/false),
|
||||
explicit DBRateLimiterOnReadTest()
|
||||
: DBTestBase("db_rate_limiter_on_read_test", /*env_do_fsync=*/false),
|
||||
use_direct_io_(std::get<0>(GetParam())),
|
||||
use_block_cache_(std::get<1>(GetParam())),
|
||||
use_readahead_(std::get<2>(GetParam())) {}
|
||||
@@ -89,20 +97,20 @@ std::string GetTestNameSuffix(
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
INSTANTIATE_TEST_CASE_P(DBRateLimiterTest, DBRateLimiterTest,
|
||||
INSTANTIATE_TEST_CASE_P(DBRateLimiterOnReadTest, DBRateLimiterOnReadTest,
|
||||
::testing::Combine(::testing::Bool(), ::testing::Bool(),
|
||||
::testing::Bool()),
|
||||
GetTestNameSuffix);
|
||||
#else // ROCKSDB_LITE
|
||||
// Cannot use direct I/O in lite mode.
|
||||
INSTANTIATE_TEST_CASE_P(DBRateLimiterTest, DBRateLimiterTest,
|
||||
INSTANTIATE_TEST_CASE_P(DBRateLimiterOnReadTest, DBRateLimiterOnReadTest,
|
||||
::testing::Combine(::testing::Values(false),
|
||||
::testing::Bool(),
|
||||
::testing::Bool()),
|
||||
GetTestNameSuffix);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_P(DBRateLimiterTest, Get) {
|
||||
TEST_P(DBRateLimiterOnReadTest, Get) {
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
return;
|
||||
}
|
||||
@@ -130,7 +138,7 @@ TEST_P(DBRateLimiterTest, Get) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBRateLimiterTest, NewMultiGet) {
|
||||
TEST_P(DBRateLimiterOnReadTest, NewMultiGet) {
|
||||
// The new void-returning `MultiGet()` APIs use `MultiRead()`, which does not
|
||||
// yet support rate limiting.
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
@@ -161,7 +169,7 @@ TEST_P(DBRateLimiterTest, NewMultiGet) {
|
||||
ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
}
|
||||
|
||||
TEST_P(DBRateLimiterTest, OldMultiGet) {
|
||||
TEST_P(DBRateLimiterOnReadTest, OldMultiGet) {
|
||||
// The old `vector<Status>`-returning `MultiGet()` APIs use `Read()`, which
|
||||
// supports rate limiting.
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
@@ -193,7 +201,7 @@ TEST_P(DBRateLimiterTest, OldMultiGet) {
|
||||
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
}
|
||||
|
||||
TEST_P(DBRateLimiterTest, Iterator) {
|
||||
TEST_P(DBRateLimiterOnReadTest, Iterator) {
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
return;
|
||||
}
|
||||
@@ -223,7 +231,7 @@ TEST_P(DBRateLimiterTest, Iterator) {
|
||||
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
|
||||
TEST_P(DBRateLimiterTest, VerifyChecksum) {
|
||||
TEST_P(DBRateLimiterOnReadTest, VerifyChecksum) {
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
return;
|
||||
}
|
||||
@@ -237,7 +245,7 @@ TEST_P(DBRateLimiterTest, VerifyChecksum) {
|
||||
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
}
|
||||
|
||||
TEST_P(DBRateLimiterTest, VerifyFileChecksums) {
|
||||
TEST_P(DBRateLimiterOnReadTest, VerifyFileChecksums) {
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
return;
|
||||
}
|
||||
@@ -253,6 +261,182 @@ TEST_P(DBRateLimiterTest, VerifyFileChecksums) {
|
||||
|
||||
#endif // !defined(ROCKSDB_LITE)
|
||||
|
||||
class DBRateLimiterOnWriteTest : public DBTestBase {
|
||||
public:
|
||||
explicit DBRateLimiterOnWriteTest()
|
||||
: DBTestBase("db_rate_limiter_on_write_test", /*env_do_fsync=*/false) {}
|
||||
|
||||
void Init() {
|
||||
options_ = GetOptions();
|
||||
ASSERT_OK(TryReopenWithColumnFamilies({"default"}, options_));
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumFiles; i++) {
|
||||
ASSERT_OK(Put(0, kStartKey, rnd.RandomString(2)));
|
||||
ASSERT_OK(Put(0, kEndKey, rnd.RandomString(2)));
|
||||
ASSERT_OK(Flush(0));
|
||||
}
|
||||
}
|
||||
|
||||
Options GetOptions() {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.rate_limiter.reset(NewGenericRateLimiter(
|
||||
1 << 20 /* rate_bytes_per_sec */, 100 * 1000 /* refill_period_us */,
|
||||
10 /* fairness */, RateLimiter::Mode::kWritesOnly));
|
||||
options.table_factory.reset(
|
||||
NewBlockBasedTableFactory(BlockBasedTableOptions()));
|
||||
return options;
|
||||
}
|
||||
|
||||
protected:
|
||||
inline const static int64_t kNumFiles = 3;
|
||||
inline const static std::string kStartKey = "a";
|
||||
inline const static std::string kEndKey = "b";
|
||||
Options options_;
|
||||
};
|
||||
|
||||
TEST_F(DBRateLimiterOnWriteTest, Flush) {
|
||||
std::int64_t prev_total_request = 0;
|
||||
|
||||
Init();
|
||||
|
||||
std::int64_t actual_flush_request =
|
||||
options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL) -
|
||||
prev_total_request;
|
||||
std::int64_t exepcted_flush_request = kNumFiles;
|
||||
EXPECT_EQ(actual_flush_request, exepcted_flush_request);
|
||||
EXPECT_EQ(actual_flush_request,
|
||||
options_.rate_limiter->GetTotalRequests(Env::IO_HIGH));
|
||||
}
|
||||
|
||||
TEST_F(DBRateLimiterOnWriteTest, Compact) {
|
||||
Init();
|
||||
|
||||
// Pre-comaction:
|
||||
// level-0 : `kNumFiles` SST files overlapping on [kStartKey, kEndKey]
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::string files_per_level_pre_compaction = std::to_string(kNumFiles);
|
||||
ASSERT_EQ(files_per_level_pre_compaction, FilesPerLevel(0 /* cf */));
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
std::int64_t prev_total_request =
|
||||
options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL);
|
||||
ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_LOW));
|
||||
|
||||
Compact(kStartKey, kEndKey);
|
||||
|
||||
std::int64_t actual_compaction_request =
|
||||
options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL) -
|
||||
prev_total_request;
|
||||
|
||||
// Post-comaction:
|
||||
// level-0 : 0 SST file
|
||||
// level-1 : 1 SST file
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::string files_per_level_post_compaction = "0,1";
|
||||
ASSERT_EQ(files_per_level_post_compaction, FilesPerLevel(0 /* cf */));
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
std::int64_t exepcted_compaction_request = 1;
|
||||
EXPECT_EQ(actual_compaction_request, exepcted_compaction_request);
|
||||
EXPECT_EQ(actual_compaction_request,
|
||||
options_.rate_limiter->GetTotalRequests(Env::IO_LOW));
|
||||
}
|
||||
|
||||
class DBRateLimiterOnWriteWALTest
|
||||
: public DBRateLimiterOnWriteTest,
|
||||
public ::testing::WithParamInterface<std::tuple<
|
||||
bool /* WriteOptions::disableWal */,
|
||||
bool /* Options::manual_wal_flush */,
|
||||
Env::IOPriority /* WriteOptions::rate_limiter_priority */>> {
|
||||
public:
|
||||
static std::string GetTestNameSuffix(
|
||||
::testing::TestParamInfo<std::tuple<bool, bool, Env::IOPriority>> info) {
|
||||
std::ostringstream oss;
|
||||
if (std::get<0>(info.param)) {
|
||||
oss << "DisableWAL";
|
||||
} else {
|
||||
oss << "EnableWAL";
|
||||
}
|
||||
if (std::get<1>(info.param)) {
|
||||
oss << "_ManualWALFlush";
|
||||
} else {
|
||||
oss << "_AutoWALFlush";
|
||||
}
|
||||
if (std::get<2>(info.param) == Env::IO_USER) {
|
||||
oss << "_RateLimitAutoWALFlush";
|
||||
} else if (std::get<2>(info.param) == Env::IO_TOTAL) {
|
||||
oss << "_NoRateLimitAutoWALFlush";
|
||||
} else {
|
||||
oss << "_RateLimitAutoWALFlushWithIncorrectPriority";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
explicit DBRateLimiterOnWriteWALTest()
|
||||
: disable_wal_(std::get<0>(GetParam())),
|
||||
manual_wal_flush_(std::get<1>(GetParam())),
|
||||
rate_limiter_priority_(std::get<2>(GetParam())) {}
|
||||
|
||||
void Init() {
|
||||
options_ = GetOptions();
|
||||
options_.manual_wal_flush = manual_wal_flush_;
|
||||
Reopen(options_);
|
||||
}
|
||||
|
||||
WriteOptions GetWriteOptions() {
|
||||
WriteOptions write_options;
|
||||
write_options.disableWAL = disable_wal_;
|
||||
write_options.rate_limiter_priority = rate_limiter_priority_;
|
||||
return write_options;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool disable_wal_;
|
||||
bool manual_wal_flush_;
|
||||
Env::IOPriority rate_limiter_priority_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBRateLimiterOnWriteWALTest, DBRateLimiterOnWriteWALTest,
|
||||
::testing::Values(std::make_tuple(false, false, Env::IO_TOTAL),
|
||||
std::make_tuple(false, false, Env::IO_USER),
|
||||
std::make_tuple(false, false, Env::IO_HIGH),
|
||||
std::make_tuple(false, true, Env::IO_USER),
|
||||
std::make_tuple(true, false, Env::IO_USER)),
|
||||
DBRateLimiterOnWriteWALTest::GetTestNameSuffix);
|
||||
|
||||
TEST_P(DBRateLimiterOnWriteWALTest, AutoWalFlush) {
|
||||
Init();
|
||||
|
||||
const bool no_rate_limit_auto_wal_flush =
|
||||
(rate_limiter_priority_ == Env::IO_TOTAL);
|
||||
const bool valid_arg = (rate_limiter_priority_ == Env::IO_USER &&
|
||||
!disable_wal_ && !manual_wal_flush_);
|
||||
|
||||
std::int64_t prev_total_request =
|
||||
options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL);
|
||||
ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
|
||||
Status s = Put("foo", "v1", GetWriteOptions());
|
||||
|
||||
if (no_rate_limit_auto_wal_flush || valid_arg) {
|
||||
EXPECT_TRUE(s.ok());
|
||||
} else {
|
||||
EXPECT_TRUE(s.IsInvalidArgument());
|
||||
EXPECT_TRUE(s.ToString().find("WriteOptions::rate_limiter_priority") !=
|
||||
std::string::npos);
|
||||
}
|
||||
|
||||
std::int64_t actual_auto_wal_flush_request =
|
||||
options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL) -
|
||||
prev_total_request;
|
||||
std::int64_t expected_auto_wal_flush_request = valid_arg ? 1 : 0;
|
||||
|
||||
EXPECT_EQ(actual_auto_wal_flush_request, expected_auto_wal_flush_request);
|
||||
EXPECT_EQ(actual_auto_wal_flush_request,
|
||||
options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+17
-114
@@ -21,7 +21,6 @@
|
||||
#include "rocksdb/persistent_cache.h"
|
||||
#include "rocksdb/trace_record.h"
|
||||
#include "rocksdb/trace_record_result.h"
|
||||
#include "rocksdb/utilities/backup_engine.h"
|
||||
#include "rocksdb/utilities/replayer.h"
|
||||
#include "rocksdb/wal_filter.h"
|
||||
#include "test_util/testutil.h"
|
||||
@@ -410,6 +409,9 @@ TEST_P(DBTestSharedWriteBufferAcrossCFs, SharedWriteBufferAcrossCFs) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[1]));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[2]));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[3]));
|
||||
// Ensure background work is fully finished including listener callbacks
|
||||
// before accessing listener state.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
};
|
||||
|
||||
// Create some data and flush "default" and "nikitich" so that they
|
||||
@@ -588,6 +590,11 @@ TEST_F(DBTest2, SharedWriteBufferLimitAcrossDB) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[1]));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[2]));
|
||||
ASSERT_OK(static_cast<DBImpl*>(db2)->TEST_WaitForFlushMemTable());
|
||||
// Ensure background work is fully finished including listener callbacks
|
||||
// before accessing listener state.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
ASSERT_OK(
|
||||
static_cast_with_check<DBImpl>(db2)->TEST_WaitForBackgroundWork());
|
||||
};
|
||||
|
||||
// Trigger a flush on cf2
|
||||
@@ -6901,122 +6908,18 @@ TEST_F(DBTest2, LastLevelStatistics) {
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_COUNT));
|
||||
}
|
||||
|
||||
class FileTemperatureTestFS : public FileSystemWrapper {
|
||||
public:
|
||||
explicit FileTemperatureTestFS(SpecialEnv* env)
|
||||
: FileSystemWrapper(env->GetFileSystem()) {}
|
||||
TEST_F(DBTest2, CheckpointFileTemperature) {
|
||||
class NoLinkTestFS : public FileTemperatureTestFS {
|
||||
using FileTemperatureTestFS::FileTemperatureTestFS;
|
||||
|
||||
static const char* kClassName() { return "TestFileSystem"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
IOStatus NewSequentialFile(const std::string& fname, const FileOptions& opts,
|
||||
std::unique_ptr<FSSequentialFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
auto filename = GetFileName(fname);
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
auto r = ParseFileName(filename, &number, &type);
|
||||
assert(r);
|
||||
if (type == kTableFile) {
|
||||
auto emplaced =
|
||||
requested_sst_file_temperatures_.emplace(number, opts.temperature);
|
||||
assert(emplaced.second); // assume no duplication
|
||||
}
|
||||
return target()->NewSequentialFile(fname, opts, result, dbg);
|
||||
}
|
||||
|
||||
IOStatus LinkFile(const std::string& s, const std::string& t,
|
||||
const IOOptions& options, IODebugContext* dbg) override {
|
||||
auto filename = GetFileName(s);
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
auto r = ParseFileName(filename, &number, &type);
|
||||
assert(r);
|
||||
// return not supported to force checkpoint copy the file instead of just
|
||||
// link
|
||||
if (type == kTableFile) {
|
||||
IOStatus LinkFile(const std::string&, const std::string&, const IOOptions&,
|
||||
IODebugContext*) override {
|
||||
// return not supported to force checkpoint copy the file instead of just
|
||||
// link
|
||||
return IOStatus::NotSupported();
|
||||
}
|
||||
return target()->LinkFile(s, t, options, dbg);
|
||||
}
|
||||
|
||||
const std::map<uint64_t, Temperature>& RequestedSstFileTemperatures() {
|
||||
return requested_sst_file_temperatures_;
|
||||
}
|
||||
|
||||
void ClearRequestedFileTemperatures() {
|
||||
requested_sst_file_temperatures_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<uint64_t, Temperature> requested_sst_file_temperatures_;
|
||||
|
||||
std::string GetFileName(const std::string& fname) {
|
||||
auto filename = fname.substr(fname.find_last_of(kFilePathSeparator) + 1);
|
||||
// workaround only for Windows that the file path could contain both Windows
|
||||
// FilePathSeparator and '/'
|
||||
filename = filename.substr(filename.find_last_of('/') + 1);
|
||||
return filename;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBTest2, BackupFileTemperature) {
|
||||
std::shared_ptr<FileTemperatureTestFS> test_fs =
|
||||
std::make_shared<FileTemperatureTestFS>(env_);
|
||||
std::unique_ptr<Env> backup_env(new CompositeEnvWrapper(env_, test_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.bottommost_temperature = Temperature::kWarm;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
Reopen(options);
|
||||
|
||||
// generate a bottommost file and a non-bottommost file
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put("bar", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put("bar", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put("bar", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
auto size = GetSstSizeHelper(Temperature::kWarm);
|
||||
ASSERT_GT(size, 0);
|
||||
|
||||
std::map<uint64_t, Temperature> temperatures;
|
||||
std::vector<LiveFileStorageInfo> infos;
|
||||
ASSERT_OK(
|
||||
dbfull()->GetLiveFilesStorageInfo(LiveFilesStorageInfoOptions(), &infos));
|
||||
for (auto info : infos) {
|
||||
temperatures.emplace(info.file_number, info.temperature);
|
||||
}
|
||||
BackupEngine* backup_engine;
|
||||
auto backup_options = BackupEngineOptions(
|
||||
dbname_ + kFilePathSeparator + "tempbk", backup_env.get());
|
||||
auto s = BackupEngine::Open(backup_env.get(), backup_options, &backup_engine);
|
||||
ASSERT_OK(s);
|
||||
s = backup_engine->CreateNewBackup(db_);
|
||||
ASSERT_OK(s);
|
||||
|
||||
// checking src file src_temperature hints: 2 sst files: 1 sst is kWarm,
|
||||
// another is kUnknown
|
||||
auto file_temperatures = test_fs->RequestedSstFileTemperatures();
|
||||
ASSERT_EQ(file_temperatures.size(), 2);
|
||||
bool has_only_one_warm_sst = false;
|
||||
for (const auto& file_temperature : file_temperatures) {
|
||||
ASSERT_EQ(temperatures.at(file_temperature.first), file_temperature.second);
|
||||
if (file_temperature.second == Temperature::kWarm) {
|
||||
ASSERT_FALSE(has_only_one_warm_sst);
|
||||
has_only_one_warm_sst = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(has_only_one_warm_sst);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, CheckpointFileTemperature) {
|
||||
std::shared_ptr<FileTemperatureTestFS> test_fs =
|
||||
std::make_shared<FileTemperatureTestFS>(env_);
|
||||
};
|
||||
auto test_fs = std::make_shared<NoLinkTestFS>(env_->GetFileSystem());
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, test_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.bottommost_temperature = Temperature::kWarm;
|
||||
|
||||
@@ -687,6 +687,50 @@ class SpecialEnv : public EnvWrapper {
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class FileTemperatureTestFS : public FileSystemWrapper {
|
||||
public:
|
||||
explicit FileTemperatureTestFS(const std::shared_ptr<FileSystem>& fs)
|
||||
: FileSystemWrapper(fs) {}
|
||||
|
||||
static const char* kClassName() { return "FileTemperatureTestFS"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
IOStatus NewSequentialFile(const std::string& fname, const FileOptions& opts,
|
||||
std::unique_ptr<FSSequentialFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
auto filename = GetFileName(fname);
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
auto r = ParseFileName(filename, &number, &type);
|
||||
assert(r);
|
||||
if (type == kTableFile) {
|
||||
auto emplaced =
|
||||
requested_sst_file_temperatures_.emplace(number, opts.temperature);
|
||||
assert(emplaced.second); // assume no duplication
|
||||
}
|
||||
return target()->NewSequentialFile(fname, opts, result, dbg);
|
||||
}
|
||||
|
||||
const std::map<uint64_t, Temperature>& RequestedSstFileTemperatures() {
|
||||
return requested_sst_file_temperatures_;
|
||||
}
|
||||
|
||||
void ClearRequestedFileTemperatures() {
|
||||
requested_sst_file_temperatures_.clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::map<uint64_t, Temperature> requested_sst_file_temperatures_;
|
||||
|
||||
std::string GetFileName(const std::string& fname) {
|
||||
auto filename = fname.substr(fname.find_last_of(kFilePathSeparator) + 1);
|
||||
// workaround only for Windows that the file path could contain both Windows
|
||||
// FilePathSeparator and '/'
|
||||
filename = filename.substr(filename.find_last_of('/') + 1);
|
||||
return filename;
|
||||
}
|
||||
};
|
||||
|
||||
class OnFileDeletionListener : public EventListener {
|
||||
public:
|
||||
OnFileDeletionListener() : matched_count_(0), expected_file_name_("") {}
|
||||
|
||||
@@ -1491,93 +1491,6 @@ TEST_F(DBWALTest, kPointInTimeRecoveryCFConsistency) {
|
||||
ASSERT_NOK(TryReopenWithColumnFamilies({"default", "one", "two"}, options));
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RaceInstallFlushResultsWithWalObsoletion) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
// The following make sure there are two bg flush threads.
|
||||
options.max_background_jobs = 8;
|
||||
|
||||
const std::string cf1_name("cf1");
|
||||
CreateAndReopenWithCF({cf1_name}, options);
|
||||
assert(handles_.size() == 2);
|
||||
|
||||
{
|
||||
dbfull()->TEST_LockMutex();
|
||||
ASSERT_LE(2, dbfull()->GetBGJobLimits().max_flushes);
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->PauseBackgroundWork());
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles_[1], "foo", "value"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "value"));
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable(false, true, handles_[1]));
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "value"));
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable(false, true, handles_[0]));
|
||||
|
||||
bool called = false;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
// This callback will be called when the first bg flush thread reaches the
|
||||
// point before entering the MANIFEST write queue after flushing the SST
|
||||
// file.
|
||||
// The purpose of the sync points here is to ensure both bg flush threads
|
||||
// finish computing `min_wal_number_to_keep` before any of them updates the
|
||||
// `log_number` for the column family that's being flushed.
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"MemTableList::TryInstallMemtableFlushResults:AfterComputeMinWalToKeep",
|
||||
[&](void* /*arg*/) {
|
||||
dbfull()->mutex()->AssertHeld();
|
||||
if (!called) {
|
||||
// We are the first bg flush thread in the MANIFEST write queue.
|
||||
// We set up the dependency between sync points for two threads that
|
||||
// will be executing the same code.
|
||||
// For the interleaving of events, see
|
||||
// https://github.com/facebook/rocksdb/pull/9715.
|
||||
// bg flush thread1 will release the db mutex while in the MANIFEST
|
||||
// write queue. In the meantime, bg flush thread2 locks db mutex and
|
||||
// computes the min_wal_number_to_keep (before thread1 writes to
|
||||
// MANIFEST thus before cf1->log_number is updated). Bg thread2 joins
|
||||
// the MANIFEST write queue afterwards and bg flush thread1 proceeds
|
||||
// with writing to MANIFEST.
|
||||
called = true;
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"VersionSet::LogAndApply:WriteManifestStart",
|
||||
"DBWALTest::RaceInstallFlushResultsWithWalObsoletion:BgFlush2"},
|
||||
{"DBWALTest::RaceInstallFlushResultsWithWalObsoletion:BgFlush2",
|
||||
"VersionSet::LogAndApply:WriteManifest"},
|
||||
});
|
||||
} else {
|
||||
// The other bg flush thread has already been in the MANIFEST write
|
||||
// queue, and we are after.
|
||||
TEST_SYNC_POINT(
|
||||
"DBWALTest::RaceInstallFlushResultsWithWalObsoletion:BgFlush2");
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(dbfull()->ContinueBackgroundWork());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[0]));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[1]));
|
||||
|
||||
ASSERT_TRUE(called);
|
||||
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
DB* db1 = nullptr;
|
||||
Status s = DB::OpenForReadOnly(options, dbname_, &db1);
|
||||
ASSERT_OK(s);
|
||||
assert(db1);
|
||||
delete db1;
|
||||
}
|
||||
|
||||
// Test scope:
|
||||
// - We expect to open data store under all circumstances
|
||||
// - We expect only data upto the point where the first error was encountered
|
||||
|
||||
+2
-3
@@ -95,9 +95,8 @@ void EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
jwriter << "cf_name" << cf_name << "job" << job_id << "event"
|
||||
<< "table_file_creation"
|
||||
<< "file_number" << fd.GetNumber() << "file_size"
|
||||
<< fd.GetFileSize() << "file_checksum"
|
||||
<< Slice(file_checksum).ToString(true) << "file_checksum_func_name"
|
||||
<< file_checksum_func_name;
|
||||
<< fd.GetFileSize() << "file_checksum" << file_checksum
|
||||
<< "file_checksum_func_name" << file_checksum_func_name;
|
||||
|
||||
// table_properties
|
||||
{
|
||||
|
||||
+4
-4
@@ -354,7 +354,7 @@ Status FlushJob::MemPurge() {
|
||||
|
||||
// Measure purging time.
|
||||
const uint64_t start_micros = clock_->NowMicros();
|
||||
const uint64_t start_cpu_micros = clock_->CPUMicros();
|
||||
const uint64_t start_cpu_micros = clock_->CPUNanos() / 1000;
|
||||
|
||||
MemTable* new_mem = nullptr;
|
||||
// For performance/log investigation purposes:
|
||||
@@ -606,7 +606,7 @@ Status FlushJob::MemPurge() {
|
||||
TEST_SYNC_POINT("DBImpl::FlushJob:MemPurgeUnsuccessful");
|
||||
}
|
||||
const uint64_t micros = clock_->NowMicros() - start_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUMicros() - start_cpu_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUNanos() / 1000 - start_cpu_micros;
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Mempurge lasted %" PRIu64
|
||||
" microseconds, and %" PRIu64
|
||||
@@ -792,7 +792,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
ThreadStatus::STAGE_FLUSH_WRITE_L0);
|
||||
db_mutex_->AssertHeld();
|
||||
const uint64_t start_micros = clock_->NowMicros();
|
||||
const uint64_t start_cpu_micros = clock_->CPUMicros();
|
||||
const uint64_t start_cpu_micros = clock_->CPUNanos() / 1000;
|
||||
Status s;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
@@ -979,7 +979,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
const uint64_t micros = clock_->NowMicros() - start_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUMicros() - start_cpu_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUNanos() / 1000 - start_cpu_micros;
|
||||
stats.micros = micros;
|
||||
stats.cpu_micros = cpu_micros;
|
||||
|
||||
|
||||
+13
-3
@@ -340,6 +340,9 @@ TEST_F(EventListenerTest, OnSingleDBFlushTest) {
|
||||
for (int i = 1; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
// Ensure background work is fully finished including listener callbacks
|
||||
// before accessing listener state.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
ASSERT_EQ(listener->flushed_dbs_.size(), i);
|
||||
ASSERT_EQ(listener->flushed_column_family_names_.size(), i);
|
||||
}
|
||||
@@ -462,6 +465,13 @@ TEST_F(EventListenerTest, MultiDBMultiListeners) {
|
||||
}
|
||||
}
|
||||
|
||||
for (int d = 0; d < kNumDBs; ++d) {
|
||||
// Ensure background work is fully finished including listener callbacks
|
||||
// before accessing listener state.
|
||||
ASSERT_OK(
|
||||
static_cast_with_check<DBImpl>(dbs[d])->TEST_WaitForBackgroundWork());
|
||||
}
|
||||
|
||||
for (auto* listener : listeners) {
|
||||
int pos = 0;
|
||||
for (size_t c = 0; c < cf_names.size(); ++c) {
|
||||
@@ -523,10 +533,10 @@ TEST_F(EventListenerTest, DisableBGCompaction) {
|
||||
ASSERT_OK(db_->Flush(fo, handles_[1]));
|
||||
db_->GetColumnFamilyMetaData(handles_[1], &cf_meta);
|
||||
}
|
||||
ASSERT_GE(listener->slowdown_count, kSlowdownTrigger * 9);
|
||||
// We don't want the listener executing during DBTestBase::Close() due to
|
||||
// race on handles_.
|
||||
// Ensure background work is fully finished including listener callbacks
|
||||
// before accessing listener state.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
ASSERT_GE(listener->slowdown_count, kSlowdownTrigger * 9);
|
||||
}
|
||||
|
||||
class TestCompactionReasonListener : public EventListener {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "util/coding.h"
|
||||
#include "util/crc32c.h"
|
||||
#include "util/random.h"
|
||||
#include "utilities/memory_allocators.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace log {
|
||||
@@ -918,6 +919,73 @@ INSTANTIATE_TEST_CASE_P(
|
||||
::testing::Values(CompressionType::kNoCompression,
|
||||
CompressionType::kZSTD)));
|
||||
|
||||
class StreamingCompressionTest
|
||||
: public ::testing::TestWithParam<std::tuple<int, CompressionType>> {};
|
||||
|
||||
TEST_P(StreamingCompressionTest, Basic) {
|
||||
size_t input_size = std::get<0>(GetParam());
|
||||
CompressionType compression_type = std::get<1>(GetParam());
|
||||
if (!StreamingCompressionTypeSupported(compression_type)) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires support for compression type");
|
||||
return;
|
||||
}
|
||||
CompressionOptions opts;
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
StreamingCompress* compress = StreamingCompress::Create(
|
||||
compression_type, opts, compression_format_version, kBlockSize);
|
||||
StreamingUncompress* uncompress = StreamingUncompress::Create(
|
||||
compression_type, compression_format_version, kBlockSize);
|
||||
MemoryAllocator* allocator = new DefaultMemoryAllocator();
|
||||
std::string input_buffer = BigString("abc", input_size);
|
||||
std::vector<std::string> compressed_buffers;
|
||||
size_t remaining;
|
||||
// Call compress till the entire input is consumed
|
||||
do {
|
||||
char* output_buffer = (char*)allocator->Allocate(kBlockSize);
|
||||
size_t output_size;
|
||||
remaining = compress->Compress(input_buffer.c_str(), input_size,
|
||||
output_buffer, &output_size);
|
||||
if (output_size > 0) {
|
||||
std::string compressed_buffer;
|
||||
compressed_buffer.assign(output_buffer, output_size);
|
||||
compressed_buffers.emplace_back(std::move(compressed_buffer));
|
||||
}
|
||||
allocator->Deallocate((void*)output_buffer);
|
||||
} while (remaining > 0);
|
||||
std::string uncompressed_buffer = "";
|
||||
int ret_val = 0;
|
||||
size_t output_size;
|
||||
char* uncompressed_output_buffer = (char*)allocator->Allocate(kBlockSize);
|
||||
// Uncompress the fragments and concatenate them.
|
||||
for (int i = 0; i < (int)compressed_buffers.size(); i++) {
|
||||
// Call uncompress till either the entire input is consumed or the output
|
||||
// buffer size is equal to the allocated output buffer size.
|
||||
do {
|
||||
ret_val = uncompress->Uncompress(
|
||||
compressed_buffers[i].c_str(), compressed_buffers[i].size(),
|
||||
uncompressed_output_buffer, &output_size);
|
||||
if (output_size > 0) {
|
||||
std::string uncompressed_fragment;
|
||||
uncompressed_fragment.assign(uncompressed_output_buffer, output_size);
|
||||
uncompressed_buffer += uncompressed_fragment;
|
||||
}
|
||||
} while (ret_val > 0 || output_size == kBlockSize);
|
||||
}
|
||||
allocator->Deallocate((void*)uncompressed_output_buffer);
|
||||
delete allocator;
|
||||
delete compress;
|
||||
delete uncompress;
|
||||
// The final return value from uncompress() should be 0.
|
||||
ASSERT_EQ(ret_val, 0);
|
||||
ASSERT_EQ(input_buffer, uncompressed_buffer);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
StreamingCompression, StreamingCompressionTest,
|
||||
::testing::Combine(::testing::Values(10, 100, 1000, kBlockSize,
|
||||
kBlockSize * 2),
|
||||
::testing::Values(CompressionType::kZSTD)));
|
||||
|
||||
} // namespace log
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
+11
-7
@@ -50,7 +50,8 @@ IOStatus Writer::Close() {
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus Writer::AddRecord(const Slice& slice) {
|
||||
IOStatus Writer::AddRecord(const Slice& slice,
|
||||
Env::IOPriority rate_limiter_priority) {
|
||||
const char* ptr = slice.data();
|
||||
size_t left = slice.size();
|
||||
|
||||
@@ -73,7 +74,8 @@ IOStatus Writer::AddRecord(const Slice& slice) {
|
||||
// kRecyclableHeaderSize being <= 11)
|
||||
assert(header_size <= 11);
|
||||
s = dest_->Append(Slice("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
static_cast<size_t>(leftover)));
|
||||
static_cast<size_t>(leftover)),
|
||||
0 /* crc32c_checksum */, rate_limiter_priority);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -99,7 +101,7 @@ IOStatus Writer::AddRecord(const Slice& slice) {
|
||||
type = recycle_log_files_ ? kRecyclableMiddleType : kMiddleType;
|
||||
}
|
||||
|
||||
s = EmitPhysicalRecord(type, ptr, fragment_length);
|
||||
s = EmitPhysicalRecord(type, ptr, fragment_length, rate_limiter_priority);
|
||||
ptr += fragment_length;
|
||||
left -= fragment_length;
|
||||
begin = false;
|
||||
@@ -107,7 +109,7 @@ IOStatus Writer::AddRecord(const Slice& slice) {
|
||||
|
||||
if (s.ok()) {
|
||||
if (!manual_flush_) {
|
||||
s = dest_->Flush();
|
||||
s = dest_->Flush(rate_limiter_priority);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +143,8 @@ IOStatus Writer::AddCompressionTypeRecord() {
|
||||
|
||||
bool Writer::TEST_BufferIsEmpty() { return dest_->TEST_BufferIsEmpty(); }
|
||||
|
||||
IOStatus Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) {
|
||||
IOStatus Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n,
|
||||
Env::IOPriority rate_limiter_priority) {
|
||||
assert(n <= 0xffff); // Must fit in two bytes
|
||||
|
||||
size_t header_size;
|
||||
@@ -180,9 +183,10 @@ IOStatus Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) {
|
||||
EncodeFixed32(buf, crc);
|
||||
|
||||
// Write the header and the payload
|
||||
IOStatus s = dest_->Append(Slice(buf, header_size));
|
||||
IOStatus s = dest_->Append(Slice(buf, header_size), 0 /* crc32c_checksum */,
|
||||
rate_limiter_priority);
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(Slice(ptr, n), payload_crc);
|
||||
s = dest_->Append(Slice(ptr, n), payload_crc, rate_limiter_priority);
|
||||
}
|
||||
block_offset_ += header_size + n;
|
||||
return s;
|
||||
|
||||
+6
-2
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "db/log_format.h"
|
||||
#include "rocksdb/compression_type.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/io_status.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
@@ -81,7 +82,8 @@ class Writer {
|
||||
|
||||
~Writer();
|
||||
|
||||
IOStatus AddRecord(const Slice& slice);
|
||||
IOStatus AddRecord(const Slice& slice,
|
||||
Env::IOPriority rate_limiter_priority = Env::IO_TOTAL);
|
||||
IOStatus AddCompressionTypeRecord();
|
||||
|
||||
WritableFileWriter* file() { return dest_.get(); }
|
||||
@@ -106,7 +108,9 @@ class Writer {
|
||||
// record type stored in the header.
|
||||
uint32_t type_crc_[kMaxRecordType + 1];
|
||||
|
||||
IOStatus EmitPhysicalRecord(RecordType type, const char* ptr, size_t length);
|
||||
IOStatus EmitPhysicalRecord(
|
||||
RecordType type, const char* ptr, size_t length,
|
||||
Env::IOPriority rate_limiter_priority = Env::IO_TOTAL);
|
||||
|
||||
// If true, it does not flush after each write. Instead it relies on the upper
|
||||
// layer to manually does the flush by calling ::WriteBuffer()
|
||||
|
||||
+12
-14
@@ -494,8 +494,8 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
// TODO(myabandeh): Not sure how batch_count could be 0 here.
|
||||
if (batch_count > 0) {
|
||||
uint64_t min_wal_number_to_keep = 0;
|
||||
assert(edit_list.size() > 0);
|
||||
if (vset->db_options()->allow_2pc) {
|
||||
assert(edit_list.size() > 0);
|
||||
// Note that if mempurge is successful, the edit_list will
|
||||
// not be applicable (contains info of new min_log number to keep,
|
||||
// and level 0 file path of SST file created during normal flush,
|
||||
@@ -504,26 +504,23 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
min_wal_number_to_keep = PrecomputeMinLogNumberToKeep2PC(
|
||||
vset, *cfd, edit_list, memtables_to_flush, prep_tracker);
|
||||
|
||||
// We piggyback the information of earliest log file to keep in the
|
||||
// We piggyback the information of earliest log file to keep in the
|
||||
// manifest entry for the last file flushed.
|
||||
} else {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, *cfd, edit_list);
|
||||
edit_list.back()->SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
}
|
||||
edit_list.back()->SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
|
||||
std::unique_ptr<VersionEdit> wal_deletion;
|
||||
if (vset->db_options()->track_and_verify_wals_in_manifest) {
|
||||
if (!vset->db_options()->allow_2pc) {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, *cfd, edit_list);
|
||||
}
|
||||
if (min_wal_number_to_keep >
|
||||
vset->GetWalSet().GetMinWalNumberToKeep()) {
|
||||
wal_deletion.reset(new VersionEdit);
|
||||
wal_deletion->DeleteWalsBefore(min_wal_number_to_keep);
|
||||
edit_list.push_back(wal_deletion.get());
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"MemTableList::TryInstallMemtableFlushResults:"
|
||||
"AfterComputeMinWalToKeep",
|
||||
nullptr);
|
||||
}
|
||||
|
||||
const auto manifest_write_cb = [this, cfd, batch_count, log_buffer,
|
||||
@@ -808,14 +805,15 @@ Status InstallMemtableAtomicFlushResults(
|
||||
if (vset->db_options()->allow_2pc) {
|
||||
min_wal_number_to_keep = PrecomputeMinLogNumberToKeep2PC(
|
||||
vset, cfds, edit_lists, mems_list, prep_tracker);
|
||||
} else {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, cfds, edit_lists);
|
||||
edit_lists.back().back()->SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
}
|
||||
edit_lists.back().back()->SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
|
||||
std::unique_ptr<VersionEdit> wal_deletion;
|
||||
if (vset->db_options()->track_and_verify_wals_in_manifest) {
|
||||
if (!vset->db_options()->allow_2pc) {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, cfds, edit_lists);
|
||||
}
|
||||
if (min_wal_number_to_keep > vset->GetWalSet().GetMinWalNumberToKeep()) {
|
||||
wal_deletion.reset(new VersionEdit);
|
||||
wal_deletion->DeleteWalsBefore(min_wal_number_to_keep);
|
||||
|
||||
@@ -16,41 +16,31 @@ PeriodicWorkScheduler::PeriodicWorkScheduler(
|
||||
timer = std::unique_ptr<Timer>(new Timer(clock.get()));
|
||||
}
|
||||
|
||||
Status PeriodicWorkScheduler::Register(DBImpl* dbi,
|
||||
unsigned int stats_dump_period_sec,
|
||||
unsigned int stats_persist_period_sec) {
|
||||
void PeriodicWorkScheduler::Register(DBImpl* dbi,
|
||||
unsigned int stats_dump_period_sec,
|
||||
unsigned int stats_persist_period_sec) {
|
||||
MutexLock l(&timer_mu_);
|
||||
static std::atomic<uint64_t> initial_delay(0);
|
||||
timer->Start();
|
||||
if (stats_dump_period_sec > 0) {
|
||||
bool succeeded = timer->Add(
|
||||
[dbi]() { dbi->DumpStats(); }, GetTaskName(dbi, "dump_st"),
|
||||
initial_delay.fetch_add(1) %
|
||||
static_cast<uint64_t>(stats_dump_period_sec) * kMicrosInSecond,
|
||||
static_cast<uint64_t>(stats_dump_period_sec) * kMicrosInSecond);
|
||||
if (!succeeded) {
|
||||
return Status::Aborted("Unable to add periodic task DumpStats");
|
||||
}
|
||||
timer->Add([dbi]() { dbi->DumpStats(); }, GetTaskName(dbi, "dump_st"),
|
||||
initial_delay.fetch_add(1) %
|
||||
static_cast<uint64_t>(stats_dump_period_sec) *
|
||||
kMicrosInSecond,
|
||||
static_cast<uint64_t>(stats_dump_period_sec) * kMicrosInSecond);
|
||||
}
|
||||
if (stats_persist_period_sec > 0) {
|
||||
bool succeeded = timer->Add(
|
||||
timer->Add(
|
||||
[dbi]() { dbi->PersistStats(); }, GetTaskName(dbi, "pst_st"),
|
||||
initial_delay.fetch_add(1) %
|
||||
static_cast<uint64_t>(stats_persist_period_sec) * kMicrosInSecond,
|
||||
static_cast<uint64_t>(stats_persist_period_sec) * kMicrosInSecond);
|
||||
if (!succeeded) {
|
||||
return Status::Aborted("Unable to add periodic task PersistStats");
|
||||
}
|
||||
}
|
||||
bool succeeded = timer->Add(
|
||||
[dbi]() { dbi->FlushInfoLog(); }, GetTaskName(dbi, "flush_info_log"),
|
||||
initial_delay.fetch_add(1) % kDefaultFlushInfoLogPeriodSec *
|
||||
kMicrosInSecond,
|
||||
kDefaultFlushInfoLogPeriodSec * kMicrosInSecond);
|
||||
if (!succeeded) {
|
||||
return Status::Aborted("Unable to add periodic task PersistStats");
|
||||
}
|
||||
return Status::OK();
|
||||
timer->Add([dbi]() { dbi->FlushInfoLog(); },
|
||||
GetTaskName(dbi, "flush_info_log"),
|
||||
initial_delay.fetch_add(1) % kDefaultFlushInfoLogPeriodSec *
|
||||
kMicrosInSecond,
|
||||
kDefaultFlushInfoLogPeriodSec * kMicrosInSecond);
|
||||
}
|
||||
|
||||
void PeriodicWorkScheduler::Unregister(DBImpl* dbi) {
|
||||
|
||||
@@ -30,8 +30,8 @@ class PeriodicWorkScheduler {
|
||||
PeriodicWorkScheduler& operator=(const PeriodicWorkScheduler&) = delete;
|
||||
PeriodicWorkScheduler& operator=(PeriodicWorkScheduler&&) = delete;
|
||||
|
||||
Status Register(DBImpl* dbi, unsigned int stats_dump_period_sec,
|
||||
unsigned int stats_persist_period_sec);
|
||||
void Register(DBImpl* dbi, unsigned int stats_dump_period_sec,
|
||||
unsigned int stats_persist_period_sec);
|
||||
|
||||
void Unregister(DBImpl* dbi);
|
||||
|
||||
|
||||
+3
-6
@@ -114,18 +114,15 @@ Status TableCache::GetTableReader(
|
||||
if (s.ok()) {
|
||||
s = ioptions_.fs->NewRandomAccessFile(fname, fopts, &file, nullptr);
|
||||
}
|
||||
if (s.ok()) {
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
} else if (s.IsPathNotFound()) {
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
if (s.IsPathNotFound()) {
|
||||
fname = Rocks2LevelTableFileName(fname);
|
||||
s = PrepareIOFromReadOptions(ro, ioptions_.clock, fopts.io_options);
|
||||
if (s.ok()) {
|
||||
s = ioptions_.fs->NewRandomAccessFile(fname, file_options, &file,
|
||||
nullptr);
|
||||
}
|
||||
if (s.ok()) {
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
}
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
|
||||
@@ -394,7 +394,7 @@ void VersionEditHandler::CheckIterationResult(const log::Reader& reader,
|
||||
if (s->ok()) {
|
||||
version_set_->GetColumnFamilySet()->UpdateMaxColumnFamily(
|
||||
version_edit_params_.max_column_family_);
|
||||
version_set_->MarkMinLogNumberToKeep(
|
||||
version_set_->MarkMinLogNumberToKeep2PC(
|
||||
version_edit_params_.min_log_number_to_keep_);
|
||||
version_set_->MarkFileNumberUsed(version_edit_params_.prev_log_number_);
|
||||
version_set_->MarkFileNumberUsed(version_edit_params_.log_number_);
|
||||
@@ -970,11 +970,12 @@ void DumpManifestHandler::CheckIterationResult(const log::Reader& reader,
|
||||
fprintf(stdout,
|
||||
"next_file_number %" PRIu64 " last_sequence %" PRIu64
|
||||
" prev_log_number %" PRIu64 " max_column_family %" PRIu32
|
||||
" min_log_number_to_keep %" PRIu64 "\n",
|
||||
" min_log_number_to_keep "
|
||||
"%" PRIu64 "\n",
|
||||
version_set_->current_next_file_number(),
|
||||
version_set_->LastSequence(), version_set_->prev_log_number(),
|
||||
version_set_->column_family_set_->GetMaxColumnFamily(),
|
||||
version_set_->min_log_number_to_keep());
|
||||
version_set_->min_log_number_to_keep_2pc());
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+8
-7
@@ -4141,7 +4141,7 @@ void VersionSet::Reset() {
|
||||
}
|
||||
db_id_.clear();
|
||||
next_file_number_.store(2);
|
||||
min_log_number_to_keep_.store(0);
|
||||
min_log_number_to_keep_2pc_.store(0);
|
||||
manifest_file_number_ = 0;
|
||||
options_file_number_ = 0;
|
||||
pending_manifest_file_number_ = 0;
|
||||
@@ -4610,7 +4610,8 @@ Status VersionSet::ProcessManifestWrites(
|
||||
}
|
||||
|
||||
if (last_min_log_number_to_keep != 0) {
|
||||
MarkMinLogNumberToKeep(last_min_log_number_to_keep);
|
||||
// Should only be set in 2PC mode.
|
||||
MarkMinLogNumberToKeep2PC(last_min_log_number_to_keep);
|
||||
}
|
||||
|
||||
for (int i = 0; i < static_cast<int>(versions.size()); ++i) {
|
||||
@@ -4964,7 +4965,7 @@ Status VersionSet::Recover(
|
||||
",min_log_number_to_keep is %" PRIu64 "\n",
|
||||
manifest_path.c_str(), manifest_file_number_, next_file_number_.load(),
|
||||
last_sequence_.load(), log_number, prev_log_number_,
|
||||
column_family_set_->GetMaxColumnFamily(), min_log_number_to_keep());
|
||||
column_family_set_->GetMaxColumnFamily(), min_log_number_to_keep_2pc());
|
||||
|
||||
for (auto cfd : *column_family_set_) {
|
||||
if (cfd->IsDropped()) {
|
||||
@@ -5377,9 +5378,9 @@ void VersionSet::MarkFileNumberUsed(uint64_t number) {
|
||||
}
|
||||
// Called only either from ::LogAndApply which is protected by mutex or during
|
||||
// recovery which is single-threaded.
|
||||
void VersionSet::MarkMinLogNumberToKeep(uint64_t number) {
|
||||
if (min_log_number_to_keep_.load(std::memory_order_relaxed) < number) {
|
||||
min_log_number_to_keep_.store(number, std::memory_order_relaxed);
|
||||
void VersionSet::MarkMinLogNumberToKeep2PC(uint64_t number) {
|
||||
if (min_log_number_to_keep_2pc_.load(std::memory_order_relaxed) < number) {
|
||||
min_log_number_to_keep_2pc_.store(number, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5505,7 +5506,7 @@ Status VersionSet::WriteCurrentStateToManifest(
|
||||
// min_log_number_to_keep is for the whole db, not for specific column family.
|
||||
// So it does not need to be set for every column family, just need to be set once.
|
||||
// Since default CF can never be dropped, we set the min_log to the default CF here.
|
||||
uint64_t min_log = min_log_number_to_keep();
|
||||
uint64_t min_log = min_log_number_to_keep_2pc();
|
||||
if (min_log != 0) {
|
||||
edit.SetMinLogNumberToKeep(min_log);
|
||||
}
|
||||
|
||||
+7
-12
@@ -1128,8 +1128,8 @@ class VersionSet {
|
||||
|
||||
uint64_t current_next_file_number() const { return next_file_number_.load(); }
|
||||
|
||||
uint64_t min_log_number_to_keep() const {
|
||||
return min_log_number_to_keep_.load();
|
||||
uint64_t min_log_number_to_keep_2pc() const {
|
||||
return min_log_number_to_keep_2pc_.load();
|
||||
}
|
||||
|
||||
// Allocate and return a new file number
|
||||
@@ -1187,7 +1187,7 @@ class VersionSet {
|
||||
// Mark the specified log number as deleted
|
||||
// REQUIRED: this is only called during single-threaded recovery or repair, or
|
||||
// from ::LogAndApply where the global mutex is held.
|
||||
void MarkMinLogNumberToKeep(uint64_t number);
|
||||
void MarkMinLogNumberToKeep2PC(uint64_t number);
|
||||
|
||||
// Return the log file number for the log file that is currently
|
||||
// being compacted, or zero if there is no such log file.
|
||||
@@ -1196,12 +1196,10 @@ class VersionSet {
|
||||
// Returns the minimum log number which still has data not flushed to any SST
|
||||
// file.
|
||||
// In non-2PC mode, all the log numbers smaller than this number can be safely
|
||||
// deleted, although we still use `min_log_number_to_keep_` to determine when
|
||||
// to delete a WAL file.
|
||||
// deleted.
|
||||
uint64_t MinLogNumberWithUnflushedData() const {
|
||||
return PreComputeMinLogNumberWithUnflushedData(nullptr);
|
||||
}
|
||||
|
||||
// Returns the minimum log number which still has data not flushed to any SST
|
||||
// file.
|
||||
// Empty column families' log number is considered to be
|
||||
@@ -1299,10 +1297,6 @@ class VersionSet {
|
||||
uint64_t min_pending_output);
|
||||
|
||||
ColumnFamilySet* GetColumnFamilySet() { return column_family_set_.get(); }
|
||||
RefedColumnFamilySet GetRefedColumnFamilySet() {
|
||||
return RefedColumnFamilySet(GetColumnFamilySet());
|
||||
}
|
||||
|
||||
const FileOptions& file_options() { return file_options_; }
|
||||
void ChangeFileOptions(const MutableDBOptions& new_options) {
|
||||
file_options_.writable_file_max_buffer_size =
|
||||
@@ -1405,8 +1399,9 @@ class VersionSet {
|
||||
const ImmutableDBOptions* const db_options_;
|
||||
std::atomic<uint64_t> next_file_number_;
|
||||
// Any WAL number smaller than this should be ignored during recovery,
|
||||
// and is qualified for being deleted.
|
||||
std::atomic<uint64_t> min_log_number_to_keep_ = {0};
|
||||
// and is qualified for being deleted in 2PC mode. In non-2PC mode, this
|
||||
// number is ignored.
|
||||
std::atomic<uint64_t> min_log_number_to_keep_2pc_ = {0};
|
||||
uint64_t manifest_file_number_;
|
||||
uint64_t options_file_number_;
|
||||
uint64_t options_file_size_;
|
||||
|
||||
@@ -3424,7 +3424,6 @@ TEST_F(VersionSetTestMissingFiles, NoFileMissing) {
|
||||
}
|
||||
|
||||
TEST_F(VersionSetTestMissingFiles, MinLogNumberToKeep2PC) {
|
||||
db_options_.allow_2pc = true;
|
||||
NewDB();
|
||||
|
||||
SstInfo sst(100, kDefaultColumnFamilyName, "a");
|
||||
@@ -3436,12 +3435,12 @@ TEST_F(VersionSetTestMissingFiles, MinLogNumberToKeep2PC) {
|
||||
edit.AddFile(0, file_metas[0]);
|
||||
edit.SetMinLogNumberToKeep(kMinWalNumberToKeep2PC);
|
||||
ASSERT_OK(LogAndApplyToDefaultCF(edit));
|
||||
ASSERT_EQ(versions_->min_log_number_to_keep(), kMinWalNumberToKeep2PC);
|
||||
ASSERT_EQ(versions_->min_log_number_to_keep_2pc(), kMinWalNumberToKeep2PC);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
CreateNewManifest();
|
||||
ReopenDB();
|
||||
ASSERT_EQ(versions_->min_log_number_to_keep(), kMinWalNumberToKeep2PC);
|
||||
ASSERT_EQ(versions_->min_log_number_to_keep_2pc(), kMinWalNumberToKeep2PC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -800,6 +800,7 @@ Status WriteBatch::Put(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
}
|
||||
|
||||
needs_in_place_update_ts_ = true;
|
||||
has_key_with_ts_ = true;
|
||||
std::string dummy_ts(ts_sz, '\0');
|
||||
std::array<Slice, 2> key_with_ts{{key, dummy_ts}};
|
||||
return WriteBatchInternal::Put(this, cf_id, SliceParts(key_with_ts.data(), 2),
|
||||
@@ -812,6 +813,7 @@ Status WriteBatch::Put(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
has_key_with_ts_ = true;
|
||||
assert(column_family);
|
||||
uint32_t cf_id = column_family->GetID();
|
||||
std::array<Slice, 2> key_with_ts{{key, ts}};
|
||||
@@ -1002,6 +1004,7 @@ Status WriteBatch::Delete(ColumnFamilyHandle* column_family, const Slice& key) {
|
||||
}
|
||||
|
||||
needs_in_place_update_ts_ = true;
|
||||
has_key_with_ts_ = true;
|
||||
std::string dummy_ts(ts_sz, '\0');
|
||||
std::array<Slice, 2> key_with_ts{{key, dummy_ts}};
|
||||
return WriteBatchInternal::Delete(this, cf_id,
|
||||
@@ -1015,6 +1018,7 @@ Status WriteBatch::Delete(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
return s;
|
||||
}
|
||||
assert(column_family);
|
||||
has_key_with_ts_ = true;
|
||||
uint32_t cf_id = column_family->GetID();
|
||||
std::array<Slice, 2> key_with_ts{{key, ts}};
|
||||
return WriteBatchInternal::Delete(this, cf_id,
|
||||
@@ -1115,6 +1119,7 @@ Status WriteBatch::SingleDelete(ColumnFamilyHandle* column_family,
|
||||
}
|
||||
|
||||
needs_in_place_update_ts_ = true;
|
||||
has_key_with_ts_ = true;
|
||||
std::string dummy_ts(ts_sz, '\0');
|
||||
std::array<Slice, 2> key_with_ts{{key, dummy_ts}};
|
||||
return WriteBatchInternal::SingleDelete(this, cf_id,
|
||||
@@ -1127,6 +1132,7 @@ Status WriteBatch::SingleDelete(ColumnFamilyHandle* column_family,
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
has_key_with_ts_ = true;
|
||||
assert(column_family);
|
||||
uint32_t cf_id = column_family->GetID();
|
||||
std::array<Slice, 2> key_with_ts{{key, ts}};
|
||||
|
||||
@@ -228,6 +228,10 @@ class WriteBatchInternal {
|
||||
static bool TimestampsUpdateNeeded(const WriteBatch& wb) {
|
||||
return wb.needs_in_place_update_ts_;
|
||||
}
|
||||
|
||||
static bool HasKeyWithTimestamp(const WriteBatch& wb) {
|
||||
return wb.has_key_with_ts_;
|
||||
}
|
||||
};
|
||||
|
||||
// LocalSavePoint is similar to a scope guard
|
||||
|
||||
+23
-1
@@ -1009,6 +1009,29 @@ TEST_F(WriteBatchTest, UpdateTimestamps) {
|
||||
{4, cf4.GetComparator()},
|
||||
{5, cf5.GetComparator()}};
|
||||
|
||||
static constexpr size_t timestamp_size = sizeof(uint64_t);
|
||||
|
||||
{
|
||||
WriteBatch wb1, wb2, wb3, wb4, wb5, wb6, wb7;
|
||||
ASSERT_OK(wb1.Put(&cf0, "key", "value"));
|
||||
ASSERT_FALSE(WriteBatchInternal::HasKeyWithTimestamp(wb1));
|
||||
ASSERT_OK(wb2.Put(&cf4, "key", "value"));
|
||||
ASSERT_TRUE(WriteBatchInternal::HasKeyWithTimestamp(wb2));
|
||||
ASSERT_OK(wb3.Put(&cf4, "key", /*ts=*/std::string(timestamp_size, '\xfe'),
|
||||
"value"));
|
||||
ASSERT_TRUE(WriteBatchInternal::HasKeyWithTimestamp(wb3));
|
||||
ASSERT_OK(wb4.Delete(&cf4, "key",
|
||||
/*ts=*/std::string(timestamp_size, '\xfe')));
|
||||
ASSERT_TRUE(WriteBatchInternal::HasKeyWithTimestamp(wb4));
|
||||
ASSERT_OK(wb5.Delete(&cf4, "key"));
|
||||
ASSERT_TRUE(WriteBatchInternal::HasKeyWithTimestamp(wb5));
|
||||
ASSERT_OK(wb6.SingleDelete(&cf4, "key"));
|
||||
ASSERT_TRUE(WriteBatchInternal::HasKeyWithTimestamp(wb6));
|
||||
ASSERT_OK(wb7.SingleDelete(&cf4, "key",
|
||||
/*ts=*/std::string(timestamp_size, '\xfe')));
|
||||
ASSERT_TRUE(WriteBatchInternal::HasKeyWithTimestamp(wb7));
|
||||
}
|
||||
|
||||
WriteBatch batch;
|
||||
// Write to the batch. We will assign timestamps later.
|
||||
for (const auto& key_str : key_strs) {
|
||||
@@ -1017,7 +1040,6 @@ TEST_F(WriteBatchTest, UpdateTimestamps) {
|
||||
ASSERT_OK(batch.Put(&cf5, key_str, "value"));
|
||||
}
|
||||
|
||||
static constexpr size_t timestamp_size = sizeof(uint64_t);
|
||||
const auto checker1 = [](uint32_t cf) {
|
||||
if (cf == 4 || cf == 5) {
|
||||
return timestamp_size;
|
||||
|
||||
@@ -471,6 +471,11 @@ size_t WriteThread::EnterAsBatchGroupLeader(Writer* leader,
|
||||
break;
|
||||
}
|
||||
|
||||
if (w->rate_limiter_priority != leader->rate_limiter_priority) {
|
||||
// Do not mix writes with different rate limiter priorities.
|
||||
break;
|
||||
}
|
||||
|
||||
if (w->batch == nullptr) {
|
||||
// Do not include those writes with nullptr batch. Those are not writes,
|
||||
// those are something else. They want to be alone
|
||||
|
||||
@@ -117,6 +117,7 @@ class WriteThread {
|
||||
bool sync;
|
||||
bool no_slowdown;
|
||||
bool disable_wal;
|
||||
Env::IOPriority rate_limiter_priority;
|
||||
bool disable_memtable;
|
||||
size_t batch_cnt; // if non-zero, number of sub-batches in the write batch
|
||||
size_t protection_bytes_per_key;
|
||||
@@ -141,6 +142,7 @@ class WriteThread {
|
||||
sync(false),
|
||||
no_slowdown(false),
|
||||
disable_wal(false),
|
||||
rate_limiter_priority(Env::IOPriority::IO_TOTAL),
|
||||
disable_memtable(false),
|
||||
batch_cnt(0),
|
||||
protection_bytes_per_key(0),
|
||||
@@ -163,6 +165,7 @@ class WriteThread {
|
||||
sync(write_options.sync),
|
||||
no_slowdown(write_options.no_slowdown),
|
||||
disable_wal(write_options.disableWAL),
|
||||
rate_limiter_priority(write_options.rate_limiter_priority),
|
||||
disable_memtable(_disable_memtable),
|
||||
batch_cnt(_batch_cnt),
|
||||
protection_bytes_per_key(_batch->GetProtectionBytesPerKey()),
|
||||
|
||||
@@ -176,6 +176,7 @@ DECLARE_int32(range_deletion_width);
|
||||
DECLARE_uint64(rate_limiter_bytes_per_sec);
|
||||
DECLARE_bool(rate_limit_bg_reads);
|
||||
DECLARE_bool(rate_limit_user_ops);
|
||||
DECLARE_bool(rate_limit_auto_wal_flush);
|
||||
DECLARE_uint64(sst_file_manager_bytes_per_sec);
|
||||
DECLARE_uint64(sst_file_manager_bytes_per_truncate);
|
||||
DECLARE_bool(use_txn);
|
||||
|
||||
@@ -550,6 +550,12 @@ DEFINE_bool(rate_limit_user_ops, false,
|
||||
"When true use Env::IO_USER priority level to charge internal rate "
|
||||
"limiter for reads associated with user operations.");
|
||||
|
||||
DEFINE_bool(rate_limit_auto_wal_flush, false,
|
||||
"When true use Env::IO_USER priority level to charge internal rate "
|
||||
"limiter for automatic WAL flush (`Options::manual_wal_flush` == "
|
||||
"false) after the user "
|
||||
"write operation.");
|
||||
|
||||
DEFINE_uint64(sst_file_manager_bytes_per_sec, 0,
|
||||
"Set `Options::sst_file_manager` to delete at this rate. By "
|
||||
"default the deletion rate is unbounded.");
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "file/file_util.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "util/coding_lean.h"
|
||||
|
||||
@@ -37,10 +38,31 @@ UniqueIdVerifier::UniqueIdVerifier(const std::string& db_name, Env* env)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Avoid relying on ReopenWritableFile which is not supported by all
|
||||
// file systems. Create a new file and copy the old file contents to it.
|
||||
std::string tmp_path = path_ + ".tmp";
|
||||
st = fs->FileExists(tmp_path, opts, /*dbg*/ nullptr);
|
||||
if (st.IsNotFound()) {
|
||||
st = fs->RenameFile(path_, tmp_path, opts, /*dbg*/ nullptr);
|
||||
// Either it should succeed or fail because src path doesn't exist
|
||||
assert(st.ok() || st.IsPathNotFound());
|
||||
} else {
|
||||
// If path_ and tmp_path both exist, retain tmp_path as its
|
||||
// guaranteed to be more complete. The order of operations are -
|
||||
// 1. Rename path_ to tmp_path
|
||||
// 2. Parse tmp_path contents
|
||||
// 3. Create path_
|
||||
// 4. Copy tmp_path contents to path_
|
||||
// 5. Delete tmp_path
|
||||
st = fs->DeleteFile(path_, opts, /*dbg*/ nullptr);
|
||||
assert(st.ok() || st.IsPathNotFound());
|
||||
}
|
||||
|
||||
uint64_t size = 0;
|
||||
{
|
||||
std::unique_ptr<FSSequentialFile> reader;
|
||||
Status s =
|
||||
fs->NewSequentialFile(path_, FileOptions(), &reader, /*dbg*/ nullptr);
|
||||
Status s = fs->NewSequentialFile(tmp_path, FileOptions(), &reader,
|
||||
/*dbg*/ nullptr);
|
||||
if (s.ok()) {
|
||||
// Load from file
|
||||
std::string id(24U, '\0');
|
||||
@@ -60,37 +82,57 @@ UniqueIdVerifier::UniqueIdVerifier(const std::string& db_name, Env* env)
|
||||
fprintf(stdout, "Warning: clearing corrupt unique id file\n");
|
||||
id_set_.clear();
|
||||
reader.reset();
|
||||
s = fs->DeleteFile(path_, opts, /*dbg*/ nullptr);
|
||||
s = fs->DeleteFile(tmp_path, opts, /*dbg*/ nullptr);
|
||||
assert(s.ok());
|
||||
size = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
size += 24U;
|
||||
VerifyNoWrite(id);
|
||||
}
|
||||
} else {
|
||||
// Newly created is ok.
|
||||
// But FileSystem doesn't tell us whether non-existence was the cause of
|
||||
// the failure. (Issue #9021)
|
||||
Status s2 = fs->FileExists(path_, opts, /*dbg*/ nullptr);
|
||||
Status s2 = fs->FileExists(tmp_path, opts, /*dbg*/ nullptr);
|
||||
if (!s2.IsNotFound()) {
|
||||
fprintf(stderr, "Error opening unique id file: %s\n",
|
||||
s.ToString().c_str());
|
||||
assert(false);
|
||||
}
|
||||
size = 0;
|
||||
}
|
||||
}
|
||||
fprintf(stdout, "(Re-)verified %zu unique IDs\n", id_set_.size());
|
||||
Status s = fs->ReopenWritableFile(path_, FileOptions(), &data_file_writer_,
|
||||
/*dbg*/ nullptr);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "Error opening unique id file for append: %s\n",
|
||||
s.ToString().c_str());
|
||||
|
||||
std::unique_ptr<FSWritableFile> file_writer;
|
||||
st = fs->NewWritableFile(path_, FileOptions(), &file_writer, /*dbg*/ nullptr);
|
||||
if (!st.ok()) {
|
||||
fprintf(stderr, "Error creating the unique ids file: %s\n",
|
||||
st.ToString().c_str());
|
||||
assert(false);
|
||||
}
|
||||
data_file_writer_.reset(
|
||||
new WritableFileWriter(std::move(file_writer), path_, FileOptions()));
|
||||
|
||||
if (size > 0) {
|
||||
st = CopyFile(fs.get(), tmp_path, data_file_writer_, size,
|
||||
/*use_fsync*/ true, /*io_tracer*/ nullptr,
|
||||
/*temparature*/ Temperature::kHot);
|
||||
if (!st.ok()) {
|
||||
fprintf(stderr, "Error copying contents of old unique id file: %s\n",
|
||||
st.ToString().c_str());
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
st = fs->DeleteFile(tmp_path, opts, /*dbg*/ nullptr);
|
||||
assert(st.ok() || st.IsPathNotFound());
|
||||
}
|
||||
|
||||
UniqueIdVerifier::~UniqueIdVerifier() {
|
||||
data_file_writer_->Close(IOOptions(), /*dbg*/ nullptr);
|
||||
IOStatus s = data_file_writer_->Close();
|
||||
assert(s.ok());
|
||||
}
|
||||
|
||||
void UniqueIdVerifier::VerifyNoWrite(const std::string& id) {
|
||||
@@ -112,14 +154,13 @@ void UniqueIdVerifier::Verify(const std::string& id) {
|
||||
if (id_set_.size() >= 4294967) {
|
||||
return;
|
||||
}
|
||||
IOStatus s =
|
||||
data_file_writer_->Append(Slice(id), IOOptions(), /*dbg*/ nullptr);
|
||||
IOStatus s = data_file_writer_->Append(Slice(id));
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "Error writing to unique id file: %s\n",
|
||||
s.ToString().c_str());
|
||||
assert(false);
|
||||
}
|
||||
s = data_file_writer_->Flush(IOOptions(), /*dbg*/ nullptr);
|
||||
s = data_file_writer_->Flush();
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "Error flushing unique id file: %s\n",
|
||||
s.ToString().c_str());
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#include "file/filename.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
@@ -39,7 +40,7 @@ class UniqueIdVerifier {
|
||||
std::mutex mutex_;
|
||||
// IDs persisted to a hidden file inside DB dir
|
||||
std::string path_;
|
||||
std::unique_ptr<FSWritableFile> data_file_writer_;
|
||||
std::unique_ptr<WritableFileWriter> data_file_writer_;
|
||||
// Starting byte for which 8 bytes to check in memory within 24 byte ID
|
||||
size_t offset_;
|
||||
// Working copy of the set of 8 byte pieces
|
||||
|
||||
@@ -485,6 +485,9 @@ void StressTest::PreloadDbAndReopenAsReadOnly(int64_t number_of_keys,
|
||||
if (FLAGS_sync) {
|
||||
write_opts.sync = true;
|
||||
}
|
||||
if (FLAGS_rate_limit_auto_wal_flush) {
|
||||
write_opts.rate_limiter_priority = Env::IO_USER;
|
||||
}
|
||||
char value[100];
|
||||
int cf_idx = 0;
|
||||
Status s;
|
||||
@@ -640,6 +643,9 @@ void StressTest::OperateDb(ThreadState* thread) {
|
||||
read_opts.rate_limiter_priority =
|
||||
FLAGS_rate_limit_user_ops ? Env::IO_USER : Env::IO_TOTAL;
|
||||
WriteOptions write_opts;
|
||||
if (FLAGS_rate_limit_auto_wal_flush) {
|
||||
write_opts.rate_limiter_priority = Env::IO_USER;
|
||||
}
|
||||
auto shared = thread->shared;
|
||||
char value[100];
|
||||
std::string from_db;
|
||||
@@ -1744,6 +1750,8 @@ Status StressTest::TestCheckpoint(ThreadState* thread,
|
||||
if (s.ok()) {
|
||||
Options options(options_);
|
||||
options.listeners.clear();
|
||||
// Avoid race condition in trash handling after delete checkpoint_db
|
||||
options.sst_file_manager.reset();
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs;
|
||||
// TODO(ajkr): `column_family_names_` is not safe to access here when
|
||||
// `clear_column_family_one_in != 0`. But we can't easily switch to
|
||||
|
||||
@@ -271,6 +271,9 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
Transaction* txn = nullptr;
|
||||
if (use_txn) {
|
||||
WriteOptions wo;
|
||||
if (FLAGS_rate_limit_auto_wal_flush) {
|
||||
wo.rate_limiter_priority = Env::IO_USER;
|
||||
}
|
||||
Status s = NewTxn(wo, &txn);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "NewTxn: %s\n", s.ToString().c_str());
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
source 'https://rubygems.org'
|
||||
gem 'github-pages', '~> 209'
|
||||
gem 'github-pages', '~> 225'
|
||||
|
||||
gem "webrick", "~> 1.7"
|
||||
+110
-92
@@ -1,7 +1,7 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (6.0.3.4)
|
||||
activesupport (6.0.4.6)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
@@ -14,60 +14,78 @@ GEM
|
||||
execjs
|
||||
coffee-script-source (1.11.1)
|
||||
colorator (1.1.0)
|
||||
commonmarker (0.17.13)
|
||||
ruby-enum (~> 0.5)
|
||||
concurrent-ruby (1.1.7)
|
||||
dnsruby (1.61.5)
|
||||
commonmarker (0.23.4)
|
||||
concurrent-ruby (1.1.9)
|
||||
dnsruby (1.61.9)
|
||||
simpleidn (~> 0.1)
|
||||
em-websocket (0.5.2)
|
||||
em-websocket (0.5.3)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
ethon (0.12.0)
|
||||
ffi (>= 1.3.0)
|
||||
http_parser.rb (~> 0)
|
||||
ethon (0.15.0)
|
||||
ffi (>= 1.15.0)
|
||||
eventmachine (1.2.7)
|
||||
execjs (2.7.0)
|
||||
faraday (1.3.0)
|
||||
execjs (2.8.1)
|
||||
faraday (1.10.0)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.3)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ruby2_keywords
|
||||
faraday-net_http (1.0.0)
|
||||
ffi (1.14.2)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
ffi (1.15.5)
|
||||
forwardable-extended (2.6.0)
|
||||
gemoji (3.0.1)
|
||||
github-pages (209)
|
||||
github-pages-health-check (= 1.16.1)
|
||||
github-pages (225)
|
||||
github-pages-health-check (= 1.17.9)
|
||||
jekyll (= 3.9.0)
|
||||
jekyll-avatar (= 0.7.0)
|
||||
jekyll-coffeescript (= 1.1.1)
|
||||
jekyll-commonmark-ghpages (= 0.1.6)
|
||||
jekyll-commonmark-ghpages (= 0.2.0)
|
||||
jekyll-default-layout (= 0.1.4)
|
||||
jekyll-feed (= 0.15.1)
|
||||
jekyll-gist (= 1.5.0)
|
||||
jekyll-github-metadata (= 2.13.0)
|
||||
jekyll-include-cache (= 0.2.1)
|
||||
jekyll-mentions (= 1.6.0)
|
||||
jekyll-optional-front-matter (= 0.3.2)
|
||||
jekyll-paginate (= 1.1.0)
|
||||
jekyll-readme-index (= 0.3.0)
|
||||
jekyll-redirect-from (= 0.16.0)
|
||||
jekyll-relative-links (= 0.6.1)
|
||||
jekyll-remote-theme (= 0.4.2)
|
||||
jekyll-remote-theme (= 0.4.3)
|
||||
jekyll-sass-converter (= 1.5.2)
|
||||
jekyll-seo-tag (= 2.6.1)
|
||||
jekyll-seo-tag (= 2.8.0)
|
||||
jekyll-sitemap (= 1.4.0)
|
||||
jekyll-swiss (= 1.0.0)
|
||||
jekyll-theme-architect (= 0.1.1)
|
||||
jekyll-theme-cayman (= 0.1.1)
|
||||
jekyll-theme-dinky (= 0.1.1)
|
||||
jekyll-theme-hacker (= 0.1.2)
|
||||
jekyll-theme-leap-day (= 0.1.1)
|
||||
jekyll-theme-merlot (= 0.1.1)
|
||||
jekyll-theme-midnight (= 0.1.1)
|
||||
jekyll-theme-minimal (= 0.1.1)
|
||||
jekyll-theme-modernist (= 0.1.1)
|
||||
jekyll-theme-primer (= 0.5.4)
|
||||
jekyll-theme-slate (= 0.1.1)
|
||||
jekyll-theme-tactile (= 0.1.1)
|
||||
jekyll-theme-time-machine (= 0.1.1)
|
||||
jekyll-theme-architect (= 0.2.0)
|
||||
jekyll-theme-cayman (= 0.2.0)
|
||||
jekyll-theme-dinky (= 0.2.0)
|
||||
jekyll-theme-hacker (= 0.2.0)
|
||||
jekyll-theme-leap-day (= 0.2.0)
|
||||
jekyll-theme-merlot (= 0.2.0)
|
||||
jekyll-theme-midnight (= 0.2.0)
|
||||
jekyll-theme-minimal (= 0.2.0)
|
||||
jekyll-theme-modernist (= 0.2.0)
|
||||
jekyll-theme-primer (= 0.6.0)
|
||||
jekyll-theme-slate (= 0.2.0)
|
||||
jekyll-theme-tactile (= 0.2.0)
|
||||
jekyll-theme-time-machine (= 0.2.0)
|
||||
jekyll-titles-from-headings (= 0.5.3)
|
||||
jemoji (= 0.12.0)
|
||||
kramdown (= 2.3.1)
|
||||
@@ -75,19 +93,19 @@ GEM
|
||||
liquid (= 4.0.3)
|
||||
mercenary (~> 0.3)
|
||||
minima (= 2.5.1)
|
||||
nokogiri (>= 1.10.4, < 2.0)
|
||||
rouge (= 3.23.0)
|
||||
nokogiri (>= 1.12.5, < 2.0)
|
||||
rouge (= 3.26.0)
|
||||
terminal-table (~> 1.4)
|
||||
github-pages-health-check (1.16.1)
|
||||
github-pages-health-check (1.17.9)
|
||||
addressable (~> 2.3)
|
||||
dnsruby (~> 1.60)
|
||||
octokit (~> 4.0)
|
||||
public_suffix (~> 3.0)
|
||||
public_suffix (>= 3.0, < 5.0)
|
||||
typhoeus (~> 1.3)
|
||||
html-pipeline (2.14.0)
|
||||
activesupport (>= 2)
|
||||
nokogiri (>= 1.4)
|
||||
http_parser.rb (0.6.0)
|
||||
http_parser.rb (0.8.0)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jekyll (3.9.0)
|
||||
@@ -108,12 +126,12 @@ GEM
|
||||
jekyll-coffeescript (1.1.1)
|
||||
coffee-script (~> 2.2)
|
||||
coffee-script-source (~> 1.11.1)
|
||||
jekyll-commonmark (1.3.1)
|
||||
commonmarker (~> 0.14)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-commonmark-ghpages (0.1.6)
|
||||
commonmarker (~> 0.17.6)
|
||||
jekyll-commonmark (~> 1.2)
|
||||
jekyll-commonmark (1.4.0)
|
||||
commonmarker (~> 0.22)
|
||||
jekyll-commonmark-ghpages (0.2.0)
|
||||
commonmarker (~> 0.23.4)
|
||||
jekyll (~> 3.9.0)
|
||||
jekyll-commonmark (~> 1.4.0)
|
||||
rouge (>= 2.0, < 4.0)
|
||||
jekyll-default-layout (0.1.4)
|
||||
jekyll (~> 3.0)
|
||||
@@ -124,6 +142,8 @@ GEM
|
||||
jekyll-github-metadata (2.13.0)
|
||||
jekyll (>= 3.4, < 5.0)
|
||||
octokit (~> 4.0, != 4.4.0)
|
||||
jekyll-include-cache (0.2.1)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-mentions (1.6.0)
|
||||
html-pipeline (~> 2.3)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
@@ -136,57 +156,57 @@ GEM
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-relative-links (0.6.1)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-remote-theme (0.4.2)
|
||||
jekyll-remote-theme (0.4.3)
|
||||
addressable (~> 2.0)
|
||||
jekyll (>= 3.5, < 5.0)
|
||||
jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
|
||||
rubyzip (>= 1.3.0, < 3.0)
|
||||
jekyll-sass-converter (1.5.2)
|
||||
sass (~> 3.4)
|
||||
jekyll-seo-tag (2.6.1)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-seo-tag (2.8.0)
|
||||
jekyll (>= 3.8, < 5.0)
|
||||
jekyll-sitemap (1.4.0)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-swiss (1.0.0)
|
||||
jekyll-theme-architect (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-cayman (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-dinky (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-hacker (0.1.2)
|
||||
jekyll-theme-architect (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-leap-day (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-cayman (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-merlot (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-dinky (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-midnight (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-hacker (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-minimal (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-leap-day (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-modernist (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-merlot (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-primer (0.5.4)
|
||||
jekyll-theme-midnight (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-minimal (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-modernist (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-primer (0.6.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-github-metadata (~> 2.9)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-slate (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-slate (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-tactile (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-tactile (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-time-machine (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-time-machine (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-titles-from-headings (0.5.3)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
@@ -201,36 +221,34 @@ GEM
|
||||
kramdown-parser-gfm (1.1.0)
|
||||
kramdown (~> 2.0)
|
||||
liquid (4.0.3)
|
||||
listen (3.4.0)
|
||||
listen (3.7.1)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
mercenary (0.3.6)
|
||||
mini_portile2 (2.6.1)
|
||||
mini_portile2 (2.8.0)
|
||||
minima (2.5.1)
|
||||
jekyll (>= 3.5, < 5.0)
|
||||
jekyll-feed (~> 0.9)
|
||||
jekyll-seo-tag (~> 2.1)
|
||||
minitest (5.14.3)
|
||||
minitest (5.15.0)
|
||||
multipart-post (2.1.1)
|
||||
nokogiri (1.12.5)
|
||||
mini_portile2 (~> 2.6.1)
|
||||
nokogiri (1.13.3)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
octokit (4.20.0)
|
||||
octokit (4.22.0)
|
||||
faraday (>= 0.9)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (3.1.1)
|
||||
racc (1.5.2)
|
||||
rb-fsevent (0.10.4)
|
||||
public_suffix (4.0.6)
|
||||
racc (1.6.0)
|
||||
rb-fsevent (0.11.1)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rexml (3.2.5)
|
||||
rouge (3.23.0)
|
||||
ruby-enum (0.8.0)
|
||||
i18n
|
||||
ruby2_keywords (0.0.2)
|
||||
rubyzip (2.3.0)
|
||||
rouge (3.26.0)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
safe_yaml (1.0.5)
|
||||
sass (3.7.4)
|
||||
sass-listen (~> 4.0.0)
|
||||
@@ -240,7 +258,7 @@ GEM
|
||||
sawyer (0.8.2)
|
||||
addressable (>= 2.3.5)
|
||||
faraday (> 0.8, < 2.0)
|
||||
simpleidn (0.1.1)
|
||||
simpleidn (0.2.1)
|
||||
unf (~> 0.1.4)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
@@ -251,16 +269,16 @@ GEM
|
||||
thread_safe (~> 0.1)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.7)
|
||||
unicode-display_width (1.7.0)
|
||||
unf_ext (0.0.8)
|
||||
unicode-display_width (1.8.0)
|
||||
webrick (1.7.0)
|
||||
zeitwerk (2.4.2)
|
||||
zeitwerk (2.5.4)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
github-pages (~> 209)
|
||||
github-pages (~> 225)
|
||||
webrick (~> 1.7)
|
||||
|
||||
BUNDLED WITH
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
{% include head.html %}
|
||||
<div class="socialBanner">
|
||||
<div>
|
||||
Support Ukraine 🇺🇦
|
||||
<a href="https://opensource.facebook.com/support-ukraine">
|
||||
Help Provide Humanitarian Aid to Ukraine
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
<body>
|
||||
{% include nav.html alwayson=true %}
|
||||
<div class="navPusher">
|
||||
|
||||
@@ -147,3 +147,13 @@ h5:hover .header-link,
|
||||
h6:hover .header-link {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Social Banner */
|
||||
.socialBanner {
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
padding: 20px;
|
||||
max-width: 768px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
Vendored
+1
-1
@@ -166,7 +166,7 @@ class PosixClock : public SystemClock {
|
||||
defined(OS_AIX) || (defined(__MACH__) && defined(__MAC_10_12))
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
|
||||
return (static_cast<uint64_t>(ts.tv_sec) * 1000000000 + ts.tv_nsec) / 1000;
|
||||
return static_cast<uint64_t>(ts.tv_sec) * 1000000000;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vendored
+198
@@ -3110,6 +3110,204 @@ TEST_F(EnvTest, CreateCompositeEnv) {
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Forward declaration
|
||||
class ReadAsyncFS;
|
||||
|
||||
struct MockIOHandle {
|
||||
std::function<void(const FSReadRequest&, void*)> cb;
|
||||
void* cb_arg;
|
||||
bool create_io_error;
|
||||
};
|
||||
|
||||
// ReadAsyncFS and ReadAsyncRandomAccessFile mocks the FS doing asynchronous
|
||||
// reads by creating threads that submit read requests and then calling Poll API
|
||||
// to obtain those results.
|
||||
class ReadAsyncRandomAccessFile : public FSRandomAccessFileOwnerWrapper {
|
||||
public:
|
||||
ReadAsyncRandomAccessFile(ReadAsyncFS& fs,
|
||||
std::unique_ptr<FSRandomAccessFile>& file)
|
||||
: FSRandomAccessFileOwnerWrapper(std::move(file)), fs_(fs) {}
|
||||
|
||||
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:
|
||||
ReadAsyncFS& fs_;
|
||||
std::unique_ptr<FSRandomAccessFile> file_;
|
||||
int counter = 0;
|
||||
};
|
||||
|
||||
class ReadAsyncFS : public FileSystemWrapper {
|
||||
public:
|
||||
explicit ReadAsyncFS(const std::shared_ptr<FileSystem>& wrapped)
|
||||
: FileSystemWrapper(wrapped) {}
|
||||
|
||||
static const char* kClassName() { return "ReadAsyncFS"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
IOStatus NewRandomAccessFile(const std::string& fname,
|
||||
const FileOptions& opts,
|
||||
std::unique_ptr<FSRandomAccessFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
std::unique_ptr<FSRandomAccessFile> file;
|
||||
IOStatus s = target()->NewRandomAccessFile(fname, opts, &file, dbg);
|
||||
EXPECT_OK(s);
|
||||
result->reset(new ReadAsyncRandomAccessFile(*this, file));
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus Poll(std::vector<void*>& io_handles,
|
||||
size_t /*min_completions*/) override {
|
||||
// Wait for the threads completion.
|
||||
for (auto& t : workers) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < io_handles.size(); i++) {
|
||||
MockIOHandle* handle = static_cast<MockIOHandle*>(io_handles[i]);
|
||||
if (handle->create_io_error) {
|
||||
FSReadRequest req;
|
||||
req.status = IOStatus::IOError();
|
||||
handle->cb(req, handle->cb_arg);
|
||||
}
|
||||
}
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
std::vector<std::thread> workers;
|
||||
};
|
||||
|
||||
IOStatus ReadAsyncRandomAccessFile::ReadAsync(
|
||||
FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(const FSReadRequest&, void*)> cb, void* cb_arg,
|
||||
void** io_handle, IOHandleDeleter* del_fn, IODebugContext* dbg) {
|
||||
IOHandleDeleter deletefn = [](void* args) -> void {
|
||||
delete (static_cast<MockIOHandle*>(args));
|
||||
args = nullptr;
|
||||
};
|
||||
*del_fn = deletefn;
|
||||
|
||||
// Allocate and populate io_handle.
|
||||
MockIOHandle* mock_handle = new MockIOHandle();
|
||||
bool create_io_error = false;
|
||||
if (counter % 2) {
|
||||
create_io_error = true;
|
||||
}
|
||||
mock_handle->create_io_error = create_io_error;
|
||||
mock_handle->cb = cb;
|
||||
mock_handle->cb_arg = cb_arg;
|
||||
*io_handle = static_cast<void*>(mock_handle);
|
||||
counter++;
|
||||
|
||||
// Submit read request asynchronously.
|
||||
std::function<void(FSReadRequest)> submit_request =
|
||||
[&opts, cb, cb_arg, io_handle, del_fn, dbg, create_io_error,
|
||||
this](FSReadRequest _req) {
|
||||
if (!create_io_error) {
|
||||
target()->ReadAsync(_req, opts, cb, cb_arg, io_handle, del_fn, dbg);
|
||||
}
|
||||
};
|
||||
|
||||
fs_.workers.emplace_back(submit_request, req);
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
class TestAsyncRead : public testing::Test {
|
||||
public:
|
||||
TestAsyncRead() { env_ = Env::Default(); }
|
||||
Env* env_;
|
||||
};
|
||||
|
||||
// Tests the default implementation of ReadAsync API.
|
||||
TEST_F(TestAsyncRead, ReadAsync) {
|
||||
EnvOptions soptions;
|
||||
std::shared_ptr<ReadAsyncFS> fs =
|
||||
std::make_shared<ReadAsyncFS>(env_->GetFileSystem());
|
||||
|
||||
std::string fname = test::PerThreadDBPath(env_, "testfile");
|
||||
|
||||
const size_t kSectorSize = 4096;
|
||||
const size_t kNumSectors = 8;
|
||||
|
||||
// 1. create & write to a file.
|
||||
{
|
||||
std::unique_ptr<FSWritableFile> wfile;
|
||||
ASSERT_OK(
|
||||
fs->NewWritableFile(fname, FileOptions(), &wfile, nullptr /*dbg*/));
|
||||
|
||||
for (size_t i = 0; i < kNumSectors; ++i) {
|
||||
auto data = NewAligned(kSectorSize * 8, static_cast<char>(i + 1));
|
||||
Slice slice(data.get(), kSectorSize);
|
||||
ASSERT_OK(wfile->Append(slice, IOOptions(), nullptr));
|
||||
}
|
||||
ASSERT_OK(wfile->Close(IOOptions(), nullptr));
|
||||
}
|
||||
// 2. Read file
|
||||
{
|
||||
std::unique_ptr<FSRandomAccessFile> file;
|
||||
ASSERT_OK(fs->NewRandomAccessFile(fname, FileOptions(), &file, nullptr));
|
||||
|
||||
IOOptions opts;
|
||||
std::vector<void*> io_handles(kNumSectors);
|
||||
std::vector<FSReadRequest> reqs(kNumSectors);
|
||||
std::vector<std::unique_ptr<char, Deleter>> data;
|
||||
std::vector<size_t> vals;
|
||||
IOHandleDeleter del_fn;
|
||||
uint64_t offset = 0;
|
||||
|
||||
// Initialize read requests
|
||||
for (size_t i = 0; i < kNumSectors; i++) {
|
||||
reqs[i].offset = offset;
|
||||
reqs[i].len = kSectorSize;
|
||||
data.emplace_back(NewAligned(kSectorSize, 0));
|
||||
reqs[i].scratch = data.back().get();
|
||||
vals.push_back(i);
|
||||
offset += kSectorSize;
|
||||
}
|
||||
|
||||
// callback function passed to async read.
|
||||
std::function<void(const FSReadRequest&, void*)> callback =
|
||||
[&](const FSReadRequest& req, void* cb_arg) {
|
||||
assert(cb_arg != nullptr);
|
||||
size_t i = *(reinterpret_cast<size_t*>(cb_arg));
|
||||
reqs[i].offset = req.offset;
|
||||
reqs[i].result = req.result;
|
||||
reqs[i].status = req.status;
|
||||
};
|
||||
|
||||
// Submit asynchronous read requests.
|
||||
for (size_t i = 0; i < kNumSectors; i++) {
|
||||
void* cb_arg = static_cast<void*>(&(vals[i]));
|
||||
ASSERT_OK(file->ReadAsync(reqs[i], opts, callback, cb_arg,
|
||||
&(io_handles[i]), &del_fn, nullptr));
|
||||
}
|
||||
|
||||
// Poll for the submitted requests.
|
||||
fs->Poll(io_handles, kNumSectors);
|
||||
|
||||
// Check the status of read requests.
|
||||
for (size_t i = 0; i < kNumSectors; i++) {
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(reqs[i].status, IOStatus::IOError());
|
||||
} else {
|
||||
auto buf = NewAligned(kSectorSize * 8, static_cast<char>(i + 1));
|
||||
Slice expected_data(buf.get(), kSectorSize);
|
||||
|
||||
ASSERT_EQ(reqs[i].offset, i * kSectorSize);
|
||||
ASSERT_OK(reqs[i].status);
|
||||
ASSERT_EQ(expected_data.ToString(), reqs[i].result.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// Delete io_handles.
|
||||
for (size_t i = 0; i < io_handles.size(); i++) {
|
||||
del_fn(io_handles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
Vendored
+23
-22
@@ -325,7 +325,14 @@ class PosixFileSystem : public FileSystem {
|
||||
SetFD_CLOEXEC(fd, &options);
|
||||
|
||||
if (options.use_mmap_writes) {
|
||||
MaybeForceDisableMmap(fd);
|
||||
if (!checkedDiskForMmap_) {
|
||||
// this will be executed once in the program's lifetime.
|
||||
// do not use mmapWrite on non ext-3/xfs/tmpfs systems.
|
||||
if (!SupportsFastAllocate(fname)) {
|
||||
forceMmapOff_ = true;
|
||||
}
|
||||
checkedDiskForMmap_ = true;
|
||||
}
|
||||
}
|
||||
if (options.use_mmap_writes && !forceMmapOff_) {
|
||||
result->reset(new PosixMmapFile(fname, fd, page_size_, options));
|
||||
@@ -424,7 +431,14 @@ class PosixFileSystem : public FileSystem {
|
||||
}
|
||||
|
||||
if (options.use_mmap_writes) {
|
||||
MaybeForceDisableMmap(fd);
|
||||
if (!checkedDiskForMmap_) {
|
||||
// this will be executed once in the program's lifetime.
|
||||
// do not use mmapWrite on non ext-3/xfs/tmpfs systems.
|
||||
if (!SupportsFastAllocate(fname)) {
|
||||
forceMmapOff_ = true;
|
||||
}
|
||||
checkedDiskForMmap_ = true;
|
||||
}
|
||||
}
|
||||
if (options.use_mmap_writes && !forceMmapOff_) {
|
||||
result->reset(new PosixMmapFile(fname, fd, page_size_, options));
|
||||
@@ -985,7 +999,8 @@ class PosixFileSystem : public FileSystem {
|
||||
}
|
||||
#endif
|
||||
private:
|
||||
bool forceMmapOff_ = false; // do we override Env options?
|
||||
bool checkedDiskForMmap_;
|
||||
bool forceMmapOff_; // do we override Env options?
|
||||
|
||||
// Returns true iff the named directory exists and is a directory.
|
||||
virtual bool DirExists(const std::string& dname) {
|
||||
@@ -996,10 +1011,10 @@ class PosixFileSystem : public FileSystem {
|
||||
return false; // stat() failed return false
|
||||
}
|
||||
|
||||
bool SupportsFastAllocate(int fd) {
|
||||
bool SupportsFastAllocate(const std::string& path) {
|
||||
#ifdef ROCKSDB_FALLOCATE_PRESENT
|
||||
struct statfs s;
|
||||
if (fstatfs(fd, &s)) {
|
||||
if (statfs(path.c_str(), &s)) {
|
||||
return false;
|
||||
}
|
||||
switch (s.f_type) {
|
||||
@@ -1013,26 +1028,11 @@ class PosixFileSystem : public FileSystem {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
(void)fd;
|
||||
(void)path;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MaybeForceDisableMmap(int fd) {
|
||||
static std::once_flag s_check_disk_for_mmap_once;
|
||||
assert(this == FileSystem::Default().get());
|
||||
std::call_once(
|
||||
s_check_disk_for_mmap_once,
|
||||
[this](int fdesc) {
|
||||
// this will be executed once in the program's lifetime.
|
||||
// do not use mmapWrite on non ext-3/xfs/tmpfs systems.
|
||||
if (!SupportsFastAllocate(fdesc)) {
|
||||
forceMmapOff_ = true;
|
||||
}
|
||||
},
|
||||
fd);
|
||||
}
|
||||
|
||||
#ifdef ROCKSDB_IOURING_PRESENT
|
||||
bool IsIOUringEnabled() {
|
||||
if (RocksDbIOUringEnable && RocksDbIOUringEnable()) {
|
||||
@@ -1096,7 +1096,8 @@ size_t PosixFileSystem::GetLogicalBlockSizeForWriteIfNeeded(
|
||||
}
|
||||
|
||||
PosixFileSystem::PosixFileSystem()
|
||||
: forceMmapOff_(false),
|
||||
: checkedDiskForMmap_(false),
|
||||
forceMmapOff_(false),
|
||||
page_size_(getpagesize()),
|
||||
allow_non_owner_access_(true) {
|
||||
#if defined(ROCKSDB_IOURING_PRESENT)
|
||||
|
||||
Vendored
+36
-3
@@ -110,12 +110,45 @@ IOStatus RemapFileSystem::NewDirectory(const std::string& dir,
|
||||
const IOOptions& options,
|
||||
std::unique_ptr<FSDirectory>* result,
|
||||
IODebugContext* dbg) {
|
||||
// A hassle to remap DirFsyncOptions::renamed_new_name
|
||||
class RemapFSDirectory : public FSDirectoryWrapper {
|
||||
public:
|
||||
RemapFSDirectory(RemapFileSystem* fs, std::unique_ptr<FSDirectory>&& t)
|
||||
: FSDirectoryWrapper(std::move(t)), fs_(fs) {}
|
||||
IOStatus FsyncWithDirOptions(
|
||||
const IOOptions& options, IODebugContext* dbg,
|
||||
const DirFsyncOptions& dir_fsync_options) override {
|
||||
if (dir_fsync_options.renamed_new_name.empty()) {
|
||||
return FSDirectoryWrapper::FsyncWithDirOptions(options, dbg,
|
||||
dir_fsync_options);
|
||||
} else {
|
||||
auto status_and_enc_path =
|
||||
fs_->EncodePath(dir_fsync_options.renamed_new_name);
|
||||
if (status_and_enc_path.first.ok()) {
|
||||
DirFsyncOptions mapped_options = dir_fsync_options;
|
||||
mapped_options.renamed_new_name = status_and_enc_path.second;
|
||||
return FSDirectoryWrapper::FsyncWithDirOptions(options, dbg,
|
||||
mapped_options);
|
||||
} else {
|
||||
return status_and_enc_path.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
RemapFileSystem* const fs_;
|
||||
};
|
||||
|
||||
auto status_and_enc_path = EncodePathWithNewBasename(dir);
|
||||
if (!status_and_enc_path.first.ok()) {
|
||||
return status_and_enc_path.first;
|
||||
}
|
||||
return FileSystemWrapper::NewDirectory(status_and_enc_path.second, options,
|
||||
result, dbg);
|
||||
IOStatus ios = FileSystemWrapper::NewDirectory(status_and_enc_path.second,
|
||||
options, result, dbg);
|
||||
if (ios.ok()) {
|
||||
*result = std::make_unique<RemapFSDirectory>(this, std::move(*result));
|
||||
}
|
||||
return ios;
|
||||
}
|
||||
|
||||
IOStatus RemapFileSystem::FileExists(const std::string& fname,
|
||||
@@ -293,7 +326,7 @@ IOStatus RemapFileSystem::GetAbsolutePath(const std::string& db_path,
|
||||
const IOOptions& options,
|
||||
std::string* output_path,
|
||||
IODebugContext* dbg) {
|
||||
auto status_and_enc_path = EncodePath(db_path);
|
||||
auto status_and_enc_path = EncodePathWithNewBasename(db_path);
|
||||
if (!status_and_enc_path.first.ok()) {
|
||||
return status_and_enc_path.first;
|
||||
}
|
||||
|
||||
@@ -139,5 +139,4 @@
|
||||
pin_l0_filter_and_index_blocks_in_cache=false
|
||||
pin_top_level_index_and_filter=false
|
||||
index_type=kBinarySearch
|
||||
hash_index_allow_collision=true
|
||||
flush_block_policy_factory=FlushBlockBySizePolicyFactory
|
||||
+26
-9
@@ -18,13 +18,13 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Utility function to copy a file up to a specified length
|
||||
IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
const std::string& destination, uint64_t size, bool use_fsync,
|
||||
std::unique_ptr<WritableFileWriter>& dest_writer,
|
||||
uint64_t size, bool use_fsync,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const Temperature temperature) {
|
||||
FileOptions soptions;
|
||||
IOStatus io_s;
|
||||
std::unique_ptr<SequentialFileReader> src_reader;
|
||||
std::unique_ptr<WritableFileWriter> dest_writer;
|
||||
|
||||
{
|
||||
soptions.temperature = temperature;
|
||||
@@ -33,11 +33,6 @@ IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
std::unique_ptr<FSWritableFile> destfile;
|
||||
io_s = fs->NewWritableFile(destination, soptions, &destfile, nullptr);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
// default argument means copy everything
|
||||
@@ -48,8 +43,6 @@ IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
}
|
||||
src_reader.reset(
|
||||
new SequentialFileReader(std::move(srcfile), source, io_tracer));
|
||||
dest_writer.reset(
|
||||
new WritableFileWriter(std::move(destfile), destination, soptions));
|
||||
}
|
||||
|
||||
char buffer[4096];
|
||||
@@ -72,6 +65,30 @@ IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
return dest_writer->Sync(use_fsync);
|
||||
}
|
||||
|
||||
IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
const std::string& destination, uint64_t size, bool use_fsync,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const Temperature temperature) {
|
||||
FileOptions options;
|
||||
IOStatus io_s;
|
||||
std::unique_ptr<WritableFileWriter> dest_writer;
|
||||
|
||||
{
|
||||
options.temperature = temperature;
|
||||
std::unique_ptr<FSWritableFile> destfile;
|
||||
io_s = fs->NewWritableFile(destination, options, &destfile, nullptr);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
|
||||
dest_writer.reset(
|
||||
new WritableFileWriter(std::move(destfile), destination, options));
|
||||
}
|
||||
|
||||
return CopyFile(fs, source, dest_writer, size, use_fsync, io_tracer,
|
||||
temperature);
|
||||
}
|
||||
|
||||
// Utility function to create a file with the provided contents
|
||||
IOStatus CreateFile(FileSystem* fs, const std::string& destination,
|
||||
const std::string& contents, bool use_fsync) {
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
// use_fsync maps to options.use_fsync, which determines the way that
|
||||
// the file is synced after copying.
|
||||
extern IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
std::unique_ptr<WritableFileWriter>& dest_writer,
|
||||
uint64_t size, bool use_fsync,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const Temperature temperature);
|
||||
extern IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
const std::string& destination, uint64_t size,
|
||||
bool use_fsync,
|
||||
|
||||
@@ -41,8 +41,8 @@ IOStatus WritableFileWriter::Create(const std::shared_ptr<FileSystem>& fs,
|
||||
return io_s;
|
||||
}
|
||||
|
||||
IOStatus WritableFileWriter::Append(const Slice& data,
|
||||
uint32_t crc32c_checksum) {
|
||||
IOStatus WritableFileWriter::Append(const Slice& data, uint32_t crc32c_checksum,
|
||||
Env::IOPriority op_rate_limiter_priority) {
|
||||
const char* src = data.data();
|
||||
size_t left = data.size();
|
||||
IOStatus s;
|
||||
@@ -79,7 +79,7 @@ IOStatus WritableFileWriter::Append(const Slice& data,
|
||||
// Flush only when buffered I/O
|
||||
if (!use_direct_io() && (buf_.Capacity() - buf_.CurrentSize()) < left) {
|
||||
if (buf_.CurrentSize() > 0) {
|
||||
s = Flush();
|
||||
s = Flush(op_rate_limiter_priority);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -109,7 +109,7 @@ IOStatus WritableFileWriter::Append(const Slice& data,
|
||||
src += appended;
|
||||
|
||||
if (left > 0) {
|
||||
s = Flush();
|
||||
s = Flush(op_rate_limiter_priority);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ IOStatus WritableFileWriter::Append(const Slice& data,
|
||||
} else {
|
||||
assert(buf_.CurrentSize() == 0);
|
||||
buffered_data_crc32c_checksum_ = crc32c_checksum;
|
||||
s = WriteBufferedWithChecksum(src, left);
|
||||
s = WriteBufferedWithChecksum(src, left, op_rate_limiter_priority);
|
||||
}
|
||||
} else {
|
||||
// In this case, either we do not need to do the data verification or
|
||||
@@ -139,7 +139,7 @@ IOStatus WritableFileWriter::Append(const Slice& data,
|
||||
src += appended;
|
||||
|
||||
if (left > 0) {
|
||||
s = Flush();
|
||||
s = Flush(op_rate_limiter_priority);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -150,9 +150,9 @@ IOStatus WritableFileWriter::Append(const Slice& data,
|
||||
assert(buf_.CurrentSize() == 0);
|
||||
if (perform_data_verification_ && buffered_data_with_checksum_) {
|
||||
buffered_data_crc32c_checksum_ = crc32c::Value(src, left);
|
||||
s = WriteBufferedWithChecksum(src, left);
|
||||
s = WriteBufferedWithChecksum(src, left, op_rate_limiter_priority);
|
||||
} else {
|
||||
s = WriteBuffered(src, left);
|
||||
s = WriteBuffered(src, left, op_rate_limiter_priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,8 @@ IOStatus WritableFileWriter::Append(const Slice& data,
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus WritableFileWriter::Pad(const size_t pad_bytes) {
|
||||
IOStatus WritableFileWriter::Pad(const size_t pad_bytes,
|
||||
Env::IOPriority op_rate_limiter_priority) {
|
||||
assert(pad_bytes < kDefaultPageSize);
|
||||
size_t left = pad_bytes;
|
||||
size_t cap = buf_.Capacity() - buf_.CurrentSize();
|
||||
@@ -178,7 +179,7 @@ IOStatus WritableFileWriter::Pad(const size_t pad_bytes) {
|
||||
buf_.PadWith(append_bytes, 0);
|
||||
left -= append_bytes;
|
||||
if (left > 0) {
|
||||
IOStatus s = Flush();
|
||||
IOStatus s = Flush(op_rate_limiter_priority);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -294,7 +295,7 @@ IOStatus WritableFileWriter::Close() {
|
||||
|
||||
// write out the cached data to the OS cache or storage if direct I/O
|
||||
// enabled
|
||||
IOStatus WritableFileWriter::Flush() {
|
||||
IOStatus WritableFileWriter::Flush(Env::IOPriority op_rate_limiter_priority) {
|
||||
IOStatus s;
|
||||
TEST_KILL_RANDOM_WITH_WEIGHT("WritableFileWriter::Flush:0", REDUCE_ODDS2);
|
||||
|
||||
@@ -303,17 +304,19 @@ IOStatus WritableFileWriter::Flush() {
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (pending_sync_) {
|
||||
if (perform_data_verification_ && buffered_data_with_checksum_) {
|
||||
s = WriteDirectWithChecksum();
|
||||
s = WriteDirectWithChecksum(op_rate_limiter_priority);
|
||||
} else {
|
||||
s = WriteDirect();
|
||||
s = WriteDirect(op_rate_limiter_priority);
|
||||
}
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
} else {
|
||||
if (perform_data_verification_ && buffered_data_with_checksum_) {
|
||||
s = WriteBufferedWithChecksum(buf_.BufferStart(), buf_.CurrentSize());
|
||||
s = WriteBufferedWithChecksum(buf_.BufferStart(), buf_.CurrentSize(),
|
||||
op_rate_limiter_priority);
|
||||
} else {
|
||||
s = WriteBuffered(buf_.BufferStart(), buf_.CurrentSize());
|
||||
s = WriteBuffered(buf_.BufferStart(), buf_.CurrentSize(),
|
||||
op_rate_limiter_priority);
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
@@ -479,7 +482,8 @@ IOStatus WritableFileWriter::RangeSync(uint64_t offset, uint64_t nbytes) {
|
||||
|
||||
// This method writes to disk the specified data and makes use of the rate
|
||||
// limiter if available
|
||||
IOStatus WritableFileWriter::WriteBuffered(const char* data, size_t size) {
|
||||
IOStatus WritableFileWriter::WriteBuffered(
|
||||
const char* data, size_t size, Env::IOPriority op_rate_limiter_priority) {
|
||||
IOStatus s;
|
||||
assert(!use_direct_io());
|
||||
const char* src = data;
|
||||
@@ -489,10 +493,14 @@ IOStatus WritableFileWriter::WriteBuffered(const char* data, size_t size) {
|
||||
|
||||
while (left > 0) {
|
||||
size_t allowed;
|
||||
if (rate_limiter_ != nullptr) {
|
||||
allowed = rate_limiter_->RequestToken(
|
||||
left, 0 /* alignment */, writable_file_->GetIOPriority(), stats_,
|
||||
RateLimiter::OpType::kWrite);
|
||||
Env::IOPriority rate_limiter_priority_used =
|
||||
WritableFileWriter::DecideRateLimiterPriority(
|
||||
writable_file_->GetIOPriority(), op_rate_limiter_priority);
|
||||
if (rate_limiter_ != nullptr &&
|
||||
rate_limiter_priority_used != Env::IO_TOTAL) {
|
||||
allowed = rate_limiter_->RequestToken(left, 0 /* alignment */,
|
||||
rate_limiter_priority_used, stats_,
|
||||
RateLimiter::OpType::kWrite);
|
||||
} else {
|
||||
allowed = left;
|
||||
}
|
||||
@@ -562,8 +570,8 @@ IOStatus WritableFileWriter::WriteBuffered(const char* data, size_t size) {
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus WritableFileWriter::WriteBufferedWithChecksum(const char* data,
|
||||
size_t size) {
|
||||
IOStatus WritableFileWriter::WriteBufferedWithChecksum(
|
||||
const char* data, size_t size, Env::IOPriority op_rate_limiter_priority) {
|
||||
IOStatus s;
|
||||
assert(!use_direct_io());
|
||||
assert(perform_data_verification_ && buffered_data_with_checksum_);
|
||||
@@ -577,12 +585,15 @@ IOStatus WritableFileWriter::WriteBufferedWithChecksum(const char* data,
|
||||
// TODO: need to be improved since it sort of defeats the purpose of the rate
|
||||
// limiter
|
||||
size_t data_size = left;
|
||||
if (rate_limiter_ != nullptr) {
|
||||
Env::IOPriority rate_limiter_priority_used =
|
||||
WritableFileWriter::DecideRateLimiterPriority(
|
||||
writable_file_->GetIOPriority(), op_rate_limiter_priority);
|
||||
if (rate_limiter_ != nullptr && rate_limiter_priority_used != Env::IO_TOTAL) {
|
||||
while (data_size > 0) {
|
||||
size_t tmp_size;
|
||||
tmp_size = rate_limiter_->RequestToken(
|
||||
data_size, buf_.Alignment(), writable_file_->GetIOPriority(), stats_,
|
||||
RateLimiter::OpType::kWrite);
|
||||
tmp_size = rate_limiter_->RequestToken(data_size, buf_.Alignment(),
|
||||
rate_limiter_priority_used, stats_,
|
||||
RateLimiter::OpType::kWrite);
|
||||
data_size -= tmp_size;
|
||||
}
|
||||
}
|
||||
@@ -674,7 +685,8 @@ void WritableFileWriter::Crc32cHandoffChecksumCalculation(const char* data,
|
||||
// only write on aligned
|
||||
// offsets.
|
||||
#ifndef ROCKSDB_LITE
|
||||
IOStatus WritableFileWriter::WriteDirect() {
|
||||
IOStatus WritableFileWriter::WriteDirect(
|
||||
Env::IOPriority op_rate_limiter_priority) {
|
||||
assert(use_direct_io());
|
||||
IOStatus s;
|
||||
const size_t alignment = buf_.Alignment();
|
||||
@@ -701,7 +713,11 @@ IOStatus WritableFileWriter::WriteDirect() {
|
||||
while (left > 0) {
|
||||
// Check how much is allowed
|
||||
size_t size;
|
||||
if (rate_limiter_ != nullptr) {
|
||||
Env::IOPriority rate_limiter_priority_used =
|
||||
WritableFileWriter::DecideRateLimiterPriority(
|
||||
writable_file_->GetIOPriority(), op_rate_limiter_priority);
|
||||
if (rate_limiter_ != nullptr &&
|
||||
rate_limiter_priority_used != Env::IO_TOTAL) {
|
||||
size = rate_limiter_->RequestToken(left, buf_.Alignment(),
|
||||
writable_file_->GetIOPriority(),
|
||||
stats_, RateLimiter::OpType::kWrite);
|
||||
@@ -762,7 +778,8 @@ IOStatus WritableFileWriter::WriteDirect() {
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus WritableFileWriter::WriteDirectWithChecksum() {
|
||||
IOStatus WritableFileWriter::WriteDirectWithChecksum(
|
||||
Env::IOPriority op_rate_limiter_priority) {
|
||||
assert(use_direct_io());
|
||||
assert(perform_data_verification_ && buffered_data_with_checksum_);
|
||||
IOStatus s;
|
||||
@@ -798,7 +815,10 @@ IOStatus WritableFileWriter::WriteDirectWithChecksum() {
|
||||
// TODO: need to be improved since it sort of defeats the purpose of the rate
|
||||
// limiter
|
||||
size_t data_size = left;
|
||||
if (rate_limiter_ != nullptr) {
|
||||
Env::IOPriority rate_limiter_priority_used =
|
||||
WritableFileWriter::DecideRateLimiterPriority(
|
||||
writable_file_->GetIOPriority(), op_rate_limiter_priority);
|
||||
if (rate_limiter_ != nullptr && rate_limiter_priority_used != Env::IO_TOTAL) {
|
||||
while (data_size > 0) {
|
||||
size_t size;
|
||||
size = rate_limiter_->RequestToken(data_size, buf_.Alignment(),
|
||||
@@ -860,4 +880,18 @@ IOStatus WritableFileWriter::WriteDirectWithChecksum() {
|
||||
return s;
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
Env::IOPriority WritableFileWriter::DecideRateLimiterPriority(
|
||||
Env::IOPriority writable_file_io_priority,
|
||||
Env::IOPriority op_rate_limiter_priority) {
|
||||
if (writable_file_io_priority == Env::IO_TOTAL &&
|
||||
op_rate_limiter_priority == Env::IO_TOTAL) {
|
||||
return Env::IO_TOTAL;
|
||||
} else if (writable_file_io_priority == Env::IO_TOTAL) {
|
||||
return op_rate_limiter_priority;
|
||||
} else if (op_rate_limiter_priority == Env::IO_TOTAL) {
|
||||
return writable_file_io_priority;
|
||||
} else {
|
||||
return op_rate_limiter_priority;
|
||||
}
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+18
-14
@@ -160,9 +160,7 @@ class WritableFileWriter {
|
||||
bool perform_data_verification_;
|
||||
uint32_t buffered_data_crc32c_checksum_;
|
||||
bool buffered_data_with_checksum_;
|
||||
#ifndef ROCKSDB_LITE
|
||||
Temperature temperature_;
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
public:
|
||||
WritableFileWriter(
|
||||
@@ -193,10 +191,8 @@ class WritableFileWriter {
|
||||
checksum_finalized_(false),
|
||||
perform_data_verification_(perform_data_verification),
|
||||
buffered_data_crc32c_checksum_(0),
|
||||
buffered_data_with_checksum_(buffered_data_with_checksum) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
temperature_ = options.temperature;
|
||||
#endif // ROCKSDB_LITE
|
||||
buffered_data_with_checksum_(buffered_data_with_checksum),
|
||||
temperature_(options.temperature) {
|
||||
assert(!use_direct_io() || max_buffer_size_ > 0);
|
||||
TEST_SYNC_POINT_CALLBACK("WritableFileWriter::WritableFileWriter:0",
|
||||
reinterpret_cast<void*>(max_buffer_size_));
|
||||
@@ -238,11 +234,13 @@ class WritableFileWriter {
|
||||
|
||||
// When this Append API is called, if the crc32c_checksum is not provided, we
|
||||
// will calculate the checksum internally.
|
||||
IOStatus Append(const Slice& data, uint32_t crc32c_checksum = 0);
|
||||
IOStatus Append(const Slice& data, uint32_t crc32c_checksum = 0,
|
||||
Env::IOPriority op_rate_limiter_priority = Env::IO_TOTAL);
|
||||
|
||||
IOStatus Pad(const size_t pad_bytes);
|
||||
IOStatus Pad(const size_t pad_bytes,
|
||||
Env::IOPriority op_rate_limiter_priority = Env::IO_TOTAL);
|
||||
|
||||
IOStatus Flush();
|
||||
IOStatus Flush(Env::IOPriority op_rate_limiter_priority = Env::IO_TOTAL);
|
||||
|
||||
IOStatus Close();
|
||||
|
||||
@@ -275,15 +273,21 @@ class WritableFileWriter {
|
||||
const char* GetFileChecksumFuncName() const;
|
||||
|
||||
private:
|
||||
static Env::IOPriority DecideRateLimiterPriority(
|
||||
Env::IOPriority writable_file_io_priority,
|
||||
Env::IOPriority op_rate_limiter_priority);
|
||||
|
||||
// Used when os buffering is OFF and we are writing
|
||||
// DMA such as in Direct I/O mode
|
||||
#ifndef ROCKSDB_LITE
|
||||
IOStatus WriteDirect();
|
||||
IOStatus WriteDirectWithChecksum();
|
||||
IOStatus WriteDirect(Env::IOPriority op_rate_limiter_priority);
|
||||
IOStatus WriteDirectWithChecksum(Env::IOPriority op_rate_limiter_priority);
|
||||
#endif // !ROCKSDB_LITE
|
||||
// Normal write
|
||||
IOStatus WriteBuffered(const char* data, size_t size);
|
||||
IOStatus WriteBufferedWithChecksum(const char* data, size_t size);
|
||||
// Normal write.
|
||||
IOStatus WriteBuffered(const char* data, size_t size,
|
||||
Env::IOPriority op_rate_limiter_priority);
|
||||
IOStatus WriteBufferedWithChecksum(const char* data, size_t size,
|
||||
Env::IOPriority op_rate_limiter_priority);
|
||||
IOStatus RangeSync(uint64_t offset, uint64_t nbytes);
|
||||
IOStatus SyncInternal(bool use_fsync);
|
||||
};
|
||||
|
||||
@@ -471,6 +471,14 @@ struct AdvancedColumnFamilyOptions {
|
||||
// according to compression_per_level[1], L3 using compression_per_level[2]
|
||||
// and L4 using compression_per_level[3]. Compaction for each level can
|
||||
// change when data grows.
|
||||
//
|
||||
// NOTE: if the vector size is smaller than the level number, the undefined
|
||||
// lower level uses the last option in the vector, for example, for 3 level
|
||||
// LSM tree the following settings are the same:
|
||||
// {kNoCompression, kSnappyCompression}
|
||||
// {kNoCompression, kSnappyCompression, kSnappyCompression}
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
std::vector<CompressionType> compression_per_level;
|
||||
|
||||
// Number of levels for this database
|
||||
|
||||
+3
-3
@@ -872,9 +872,9 @@ extern ROCKSDB_LIBRARY_API void rocksdb_block_based_options_set_data_block_index
|
||||
rocksdb_block_based_table_options_t*, int); // uses one of the above enums
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_block_based_options_set_data_block_hash_ratio(
|
||||
rocksdb_block_based_table_options_t* options, double v);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_block_based_options_set_hash_index_allow_collision(
|
||||
rocksdb_block_based_table_options_t*, unsigned char);
|
||||
// rocksdb_block_based_options_set_hash_index_allow_collision()
|
||||
// is removed since BlockBasedTableOptions.hash_index_allow_collision()
|
||||
// is removed
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_block_based_options_set_cache_index_and_filter_blocks(
|
||||
rocksdb_block_based_table_options_t*, unsigned char);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/compression_type.h"
|
||||
#include "rocksdb/memory_allocator.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
@@ -127,6 +128,53 @@ extern std::shared_ptr<Cache> NewLRUCache(
|
||||
|
||||
extern std::shared_ptr<Cache> NewLRUCache(const LRUCacheOptions& cache_opts);
|
||||
|
||||
// EXPERIMENTAL
|
||||
// Options structure for configuring a SecondaryCache instance based on
|
||||
// LRUCache. The LRUCacheOptions.secondary_cache is not used and
|
||||
// should not be set.
|
||||
struct LRUSecondaryCacheOptions : LRUCacheOptions {
|
||||
// The compression method (if any) that is used to compress data.
|
||||
CompressionType compression_type = CompressionType::kLZ4Compression;
|
||||
|
||||
// compress_format_version can have two values:
|
||||
// compress_format_version == 1 -- decompressed size is not included in the
|
||||
// block header.
|
||||
// compress_format_version == 2 -- decompressed size is included in the block
|
||||
// header in varint32 format.
|
||||
uint32_t compress_format_version = 2;
|
||||
|
||||
LRUSecondaryCacheOptions() {}
|
||||
LRUSecondaryCacheOptions(
|
||||
size_t _capacity, int _num_shard_bits, bool _strict_capacity_limit,
|
||||
double _high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> _memory_allocator = nullptr,
|
||||
bool _use_adaptive_mutex = kDefaultToAdaptiveMutex,
|
||||
CacheMetadataChargePolicy _metadata_charge_policy =
|
||||
kDefaultCacheMetadataChargePolicy,
|
||||
CompressionType _compression_type = CompressionType::kLZ4Compression,
|
||||
uint32_t _compress_format_version = 2)
|
||||
: LRUCacheOptions(_capacity, _num_shard_bits, _strict_capacity_limit,
|
||||
_high_pri_pool_ratio, std::move(_memory_allocator),
|
||||
_use_adaptive_mutex, _metadata_charge_policy),
|
||||
compression_type(_compression_type),
|
||||
compress_format_version(_compress_format_version) {}
|
||||
};
|
||||
|
||||
// EXPERIMENTAL
|
||||
// Create a new Secondary Cache that is implemented on top of LRUCache.
|
||||
extern std::shared_ptr<SecondaryCache> NewLRUSecondaryCache(
|
||||
size_t capacity, int num_shard_bits = -1,
|
||||
bool strict_capacity_limit = false, double high_pri_pool_ratio = 0.5,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator = nullptr,
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy =
|
||||
kDefaultCacheMetadataChargePolicy,
|
||||
CompressionType compression_type = CompressionType::kLZ4Compression,
|
||||
uint32_t compress_format_version = 2);
|
||||
|
||||
extern std::shared_ptr<SecondaryCache> NewLRUSecondaryCache(
|
||||
const LRUSecondaryCacheOptions& opts);
|
||||
|
||||
// Similar to NewLRUCache, but create a cache based on CLOCK algorithm with
|
||||
// better concurrent performance in some cases. See util/clock_cache.cc for
|
||||
// more detail.
|
||||
|
||||
@@ -780,7 +780,7 @@ class DB {
|
||||
// snapshot is no longer needed.
|
||||
//
|
||||
// nullptr will be returned if the DB fails to take a snapshot or does
|
||||
// not support snapshot (eg: inplace_update_support enabled).
|
||||
// not support snapshot.
|
||||
virtual const Snapshot* GetSnapshot() = 0;
|
||||
|
||||
// Release a previously acquired snapshot. The caller must not
|
||||
|
||||
@@ -959,8 +959,16 @@ class WritableFile {
|
||||
// Use the returned alignment value to allocate
|
||||
// aligned buffer for Direct I/O
|
||||
virtual size_t GetRequiredBufferAlignment() const { return kDefaultPageSize; }
|
||||
|
||||
/*
|
||||
* Change the priority in rate limiter if rate limiting is enabled.
|
||||
* If rate limiting is enabled, change the file-granularity priority used in
|
||||
* rate-limiting writes.
|
||||
*
|
||||
* In the presence of finer-granularity priority such as
|
||||
* `WriteOptions::rate_limiter_priority`, this file-granularity priority may
|
||||
* be overridden by a non-Env::IO_TOTAL finer-granularity priority and used as
|
||||
* a fallback for Env::IO_TOTAL finer-granularity priority.
|
||||
*
|
||||
* If rate limiting is not enabled, this call has no effect.
|
||||
*/
|
||||
virtual void SetIOPriority(Env::IOPriority pri) { io_priority_ = pri; }
|
||||
|
||||
@@ -221,10 +221,10 @@ struct IODebugContext {
|
||||
}
|
||||
};
|
||||
|
||||
// IOHandle is used by underlying file system to store any information it needs
|
||||
// during Async Read requests.
|
||||
// A function pointer type for custom destruction of void pointer passed to
|
||||
// ReadAsync API. RocksDB/caller is responsible for deleting the void pointer
|
||||
// allocated by FS in ReadAsync API.
|
||||
using IOHandleDeleter = std::function<void(void*)>;
|
||||
using IOHandle = std::unique_ptr<void, IOHandleDeleter>;
|
||||
|
||||
// The FileSystem, FSSequentialFile, FSRandomAccessFile, FSWritableFile,
|
||||
// FSRandomRWFileclass, and FSDIrectory classes define the interface between
|
||||
@@ -647,13 +647,15 @@ class FileSystem : public Customizable {
|
||||
|
||||
// EXPERIMENTAL
|
||||
// Poll for completion of read IO requests. The Poll() method should call the
|
||||
// callback functions to indicate completion of read requests. If Poll is not
|
||||
// supported it means callee should be informed of IO completions via the
|
||||
// callback on another thread.
|
||||
// callback functions to indicate completion of read requests.
|
||||
// Underlying FS is required to support Poll API. Poll implementation should
|
||||
// ensure that the callback gets called at IO completion, and return only
|
||||
// after the callback has been called.
|
||||
//
|
||||
//
|
||||
// Default implementation is to return IOStatus::OK.
|
||||
|
||||
virtual IOStatus Poll(std::vector<IOHandle*>& /*io_handles*/,
|
||||
virtual IOStatus Poll(std::vector<void*>& /*io_handles*/,
|
||||
size_t /*min_completions*/) {
|
||||
return IOStatus::OK();
|
||||
}
|
||||
@@ -865,9 +867,13 @@ class FSRandomAccessFile {
|
||||
// cb_arg should be used by the callback to track the original request
|
||||
// submitted.
|
||||
//
|
||||
// This API should also populate IOHandle which should be used by
|
||||
// This API should also populate io_handle which should be used by
|
||||
// underlying FileSystem to store the context in order to distinguish the read
|
||||
// requests at their side.
|
||||
// requests at their side and provide the custom deletion function in del_fn.
|
||||
// RocksDB guarantees that the del_fn for io_handle will be called after
|
||||
// receiving the callback. Furthermore, RocksDB guarantees that if it calls
|
||||
// the Poll API for this io_handle, del_fn will be called after the Poll
|
||||
// returns. RocksDB is responsible for managing the lifetime of io_handle.
|
||||
//
|
||||
// req contains the request offset and size passed as input parameter of read
|
||||
// request and result and status fields are output parameter set by underlying
|
||||
@@ -877,7 +883,7 @@ class FSRandomAccessFile {
|
||||
virtual IOStatus ReadAsync(
|
||||
FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(const FSReadRequest&, void*)> cb, void* cb_arg,
|
||||
IOHandle* /*io_handle*/, IODebugContext* dbg) {
|
||||
void** /*io_handle*/, IOHandleDeleter* /*del_fn*/, IODebugContext* dbg) {
|
||||
req.status =
|
||||
Read(req.offset, req.len, opts, &(req.result), req.scratch, dbg);
|
||||
cb(req, cb_arg);
|
||||
@@ -1025,6 +1031,17 @@ class FSWritableFile {
|
||||
write_hint_ = hint;
|
||||
}
|
||||
|
||||
/*
|
||||
* If rate limiting is enabled, change the file-granularity priority used in
|
||||
* rate-limiting writes.
|
||||
*
|
||||
* In the presence of finer-granularity priority such as
|
||||
* `WriteOptions::rate_limiter_priority`, this file-granularity priority may
|
||||
* be overridden by a non-Env::IO_TOTAL finer-granularity priority and used as
|
||||
* a fallback for Env::IO_TOTAL finer-granularity priority.
|
||||
*
|
||||
* If rate limiting is not enabled, this call has no effect.
|
||||
*/
|
||||
virtual void SetIOPriority(Env::IOPriority pri) { io_priority_ = pri; }
|
||||
|
||||
virtual Env::IOPriority GetIOPriority() { return io_priority_; }
|
||||
@@ -1470,7 +1487,7 @@ class FileSystemWrapper : public FileSystem {
|
||||
const std::string& header) const override;
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
virtual IOStatus Poll(std::vector<IOHandle*>& io_handles,
|
||||
virtual IOStatus Poll(std::vector<void*>& io_handles,
|
||||
size_t min_completions) override {
|
||||
return target_->Poll(io_handles, min_completions);
|
||||
}
|
||||
@@ -1557,9 +1574,9 @@ class FSRandomAccessFileWrapper : public FSRandomAccessFile {
|
||||
}
|
||||
IOStatus ReadAsync(FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(const FSReadRequest&, void*)> cb,
|
||||
void* cb_arg, IOHandle* io_handle,
|
||||
void* cb_arg, void** io_handle, IOHandleDeleter* del_fn,
|
||||
IODebugContext* dbg) override {
|
||||
return target()->ReadAsync(req, opts, cb, cb_arg, io_handle, dbg);
|
||||
return target()->ReadAsync(req, opts, cb, cb_arg, io_handle, del_fn, dbg);
|
||||
}
|
||||
Temperature GetTemperature() const override {
|
||||
return target_->GetTemperature();
|
||||
|
||||
@@ -90,19 +90,6 @@ class FilterPolicy : public Customizable {
|
||||
virtual ~FilterPolicy();
|
||||
static const char* Type() { return "FilterPolicy"; }
|
||||
|
||||
// The name used for identifying whether a filter on disk is readable
|
||||
// by this FilterPolicy. If this FilterPolicy is part of a family that
|
||||
// can read each others filters, such as built-in BloomFilterPolcy and
|
||||
// RibbonFilterPolicy, the CompatibilityName is a shared family name,
|
||||
// while kinds of filters in the family can have distinct Customizable
|
||||
// Names. This function is pure virtual so that wrappers around built-in
|
||||
// policies are prompted to defer to CompatibilityName() of the wrapped
|
||||
// policy, which is important for compatibility.
|
||||
//
|
||||
// For custom filter policies that are not part of a read-compatible
|
||||
// family (rare), implementations may return Name().
|
||||
virtual const char* CompatibilityName() const = 0;
|
||||
|
||||
// Creates a new FilterPolicy based on the input value string and returns the
|
||||
// result The value might be an ID, and ID with properties, or an old-style
|
||||
// policy string.
|
||||
|
||||
+11
-26
@@ -28,7 +28,7 @@ class IOStatus : public Status {
|
||||
using Code = Status::Code;
|
||||
using SubCode = Status::SubCode;
|
||||
|
||||
enum IOErrorScope {
|
||||
enum IOErrorScope : unsigned char {
|
||||
kIOErrorScopeFileSystem,
|
||||
kIOErrorScopeFile,
|
||||
kIOErrorScopeRange,
|
||||
@@ -57,11 +57,13 @@ class IOStatus : public Status {
|
||||
|
||||
void SetRetryable(bool retryable) { retryable_ = retryable; }
|
||||
void SetDataLoss(bool data_loss) { data_loss_ = data_loss; }
|
||||
void SetScope(IOErrorScope scope) { scope_ = scope; }
|
||||
void SetScope(IOErrorScope scope) {
|
||||
scope_ = static_cast<unsigned char>(scope);
|
||||
}
|
||||
|
||||
bool GetRetryable() const { return retryable_; }
|
||||
bool GetDataLoss() const { return data_loss_; }
|
||||
IOErrorScope GetScope() const { return scope_; }
|
||||
IOErrorScope GetScope() const { return static_cast<IOErrorScope>(scope_); }
|
||||
|
||||
// Return a success status.
|
||||
static IOStatus OK() { return IOStatus(); }
|
||||
@@ -137,15 +139,9 @@ class IOStatus : public Status {
|
||||
|
||||
private:
|
||||
friend IOStatus status_to_io_status(Status&&);
|
||||
bool retryable_;
|
||||
bool data_loss_;
|
||||
IOErrorScope scope_;
|
||||
|
||||
explicit IOStatus(Code _code, SubCode _subcode = kNone)
|
||||
: Status(_code, _subcode),
|
||||
retryable_(false),
|
||||
data_loss_(false),
|
||||
scope_(kIOErrorScopeFileSystem) {}
|
||||
: Status(_code, _subcode, false, false, kIOErrorScopeFileSystem) {}
|
||||
|
||||
IOStatus(Code _code, SubCode _subcode, const Slice& msg, const Slice& msg2);
|
||||
IOStatus(Code _code, const Slice& msg, const Slice& msg2)
|
||||
@@ -154,10 +150,7 @@ class IOStatus : public Status {
|
||||
|
||||
inline IOStatus::IOStatus(Code _code, SubCode _subcode, const Slice& msg,
|
||||
const Slice& msg2)
|
||||
: Status(_code, _subcode),
|
||||
retryable_(false),
|
||||
data_loss_(false),
|
||||
scope_(kIOErrorScopeFileSystem) {
|
||||
: Status(_code, _subcode, false, false, kIOErrorScopeFileSystem) {
|
||||
assert(code_ != kOk);
|
||||
assert(subcode_ != kMaxSubCode);
|
||||
const size_t len1 = msg.size();
|
||||
@@ -249,18 +242,10 @@ inline bool IOStatus::operator!=(const IOStatus& rhs) const {
|
||||
}
|
||||
|
||||
inline IOStatus status_to_io_status(Status&& status) {
|
||||
if (status.ok()) {
|
||||
// Fast path
|
||||
return IOStatus::OK();
|
||||
} else {
|
||||
const char* state = status.getState();
|
||||
if (state) {
|
||||
return IOStatus(status.code(), status.subcode(),
|
||||
Slice(state, strlen(status.getState()) + 1), Slice());
|
||||
} else {
|
||||
return IOStatus(status.code(), status.subcode());
|
||||
}
|
||||
}
|
||||
IOStatus io_s;
|
||||
Status& s = io_s;
|
||||
s = std::move(status);
|
||||
return io_s;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1659,13 +1659,29 @@ struct WriteOptions {
|
||||
// Default: false
|
||||
bool memtable_insert_hint_per_batch;
|
||||
|
||||
// For writes associated with this option, charge the internal rate
|
||||
// limiter (see `DBOptions::rate_limiter`) at the specified priority. The
|
||||
// special value `Env::IO_TOTAL` disables charging the rate limiter.
|
||||
//
|
||||
// Currently the support covers automatic WAL flushes, which happen during
|
||||
// live updates (`Put()`, `Write()`, `Delete()`, etc.)
|
||||
// when `WriteOptions::disableWAL == false`
|
||||
// and `DBOptions::manual_wal_flush == false`.
|
||||
//
|
||||
// Only `Env::IO_USER` and `Env::IO_TOTAL` are allowed
|
||||
// due to implementation constraints.
|
||||
//
|
||||
// Default: `Env::IO_TOTAL`
|
||||
Env::IOPriority rate_limiter_priority;
|
||||
|
||||
WriteOptions()
|
||||
: sync(false),
|
||||
disableWAL(false),
|
||||
ignore_missing_column_families(false),
|
||||
no_slowdown(false),
|
||||
low_pri(false),
|
||||
memtable_insert_hint_per_batch(false) {}
|
||||
memtable_insert_hint_per_batch(false),
|
||||
rate_limiter_priority(Env::IO_TOTAL) {}
|
||||
};
|
||||
|
||||
// Options that control flush operations
|
||||
|
||||
@@ -35,7 +35,14 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class Status {
|
||||
public:
|
||||
// Create a success status.
|
||||
Status() : code_(kOk), subcode_(kNone), sev_(kNoError), state_(nullptr) {}
|
||||
Status()
|
||||
: code_(kOk),
|
||||
subcode_(kNone),
|
||||
sev_(kNoError),
|
||||
retryable_(false),
|
||||
data_loss_(false),
|
||||
scope_(0),
|
||||
state_(nullptr) {}
|
||||
~Status() {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
if (!checked_) {
|
||||
@@ -454,6 +461,9 @@ class Status {
|
||||
Code code_;
|
||||
SubCode subcode_;
|
||||
Severity sev_;
|
||||
bool retryable_;
|
||||
bool data_loss_;
|
||||
unsigned char scope_;
|
||||
// A nullptr state_ (which is at least the case for OK) means the extra
|
||||
// message is empty.
|
||||
std::unique_ptr<const char[]> state_;
|
||||
@@ -462,7 +472,21 @@ class Status {
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
|
||||
explicit Status(Code _code, SubCode _subcode = kNone)
|
||||
: code_(_code), subcode_(_subcode), sev_(kNoError) {}
|
||||
: code_(_code),
|
||||
subcode_(_subcode),
|
||||
sev_(kNoError),
|
||||
retryable_(false),
|
||||
data_loss_(false),
|
||||
scope_(0) {}
|
||||
|
||||
explicit Status(Code _code, SubCode _subcode, bool retryable, bool data_loss,
|
||||
unsigned char scope)
|
||||
: code_(_code),
|
||||
subcode_(_subcode),
|
||||
sev_(kNoError),
|
||||
retryable_(retryable),
|
||||
data_loss_(data_loss),
|
||||
scope_(scope) {}
|
||||
|
||||
Status(Code _code, SubCode _subcode, const Slice& msg, const Slice& msg2,
|
||||
Severity sev = kNoError);
|
||||
@@ -479,12 +503,22 @@ class Status {
|
||||
};
|
||||
|
||||
inline Status::Status(const Status& s)
|
||||
: code_(s.code_), subcode_(s.subcode_), sev_(s.sev_) {
|
||||
: code_(s.code_),
|
||||
subcode_(s.subcode_),
|
||||
sev_(s.sev_),
|
||||
retryable_(s.retryable_),
|
||||
data_loss_(s.data_loss_),
|
||||
scope_(s.scope_) {
|
||||
s.MarkChecked();
|
||||
state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_.get());
|
||||
}
|
||||
inline Status::Status(const Status& s, Severity sev)
|
||||
: code_(s.code_), subcode_(s.subcode_), sev_(sev) {
|
||||
: code_(s.code_),
|
||||
subcode_(s.subcode_),
|
||||
sev_(sev),
|
||||
retryable_(s.retryable_),
|
||||
data_loss_(s.data_loss_),
|
||||
scope_(s.scope_) {
|
||||
s.MarkChecked();
|
||||
state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_.get());
|
||||
}
|
||||
@@ -495,6 +529,9 @@ inline Status& Status::operator=(const Status& s) {
|
||||
code_ = s.code_;
|
||||
subcode_ = s.subcode_;
|
||||
sev_ = s.sev_;
|
||||
retryable_ = s.retryable_;
|
||||
data_loss_ = s.data_loss_;
|
||||
scope_ = s.scope_;
|
||||
state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_.get());
|
||||
}
|
||||
return *this;
|
||||
@@ -523,6 +560,12 @@ inline Status& Status::operator=(Status&& s)
|
||||
s.subcode_ = kNone;
|
||||
sev_ = std::move(s.sev_);
|
||||
s.sev_ = kNoError;
|
||||
retryable_ = std::move(s.retryable_);
|
||||
s.retryable_ = false;
|
||||
data_loss_ = std::move(s.data_loss_);
|
||||
s.data_loss_ = false;
|
||||
scope_ = std::move(s.scope_);
|
||||
s.scope_ = 0;
|
||||
state_ = std::move(s.state_);
|
||||
}
|
||||
return *this;
|
||||
|
||||
@@ -227,9 +227,8 @@ struct BlockBasedTableOptions {
|
||||
// kDataBlockBinaryAndHash.
|
||||
double data_block_hash_table_util_ratio = 0.75;
|
||||
|
||||
// This option is now deprecated. No matter what value it is set to,
|
||||
// it will behave as if hash_index_allow_collision=true.
|
||||
bool hash_index_allow_collision = true;
|
||||
// Option hash_index_allow_collision is now deleted.
|
||||
// It will behave as if hash_index_allow_collision=true.
|
||||
|
||||
// Use the specified checksum type. Newly created table files will be
|
||||
// protected with this checksum type. Old table files will still be readable,
|
||||
@@ -372,6 +371,10 @@ struct BlockBasedTableOptions {
|
||||
// useful in detecting software bugs or CPU+memory malfunction.
|
||||
// Turning on this feature increases filter construction time by 30%.
|
||||
//
|
||||
// This parameter can be changed dynamically by
|
||||
// DB::SetOptions({{"block_based_table_factory",
|
||||
// "{detect_filter_construct_corruption=true;}"}});
|
||||
//
|
||||
// TODO: optimize this performance
|
||||
bool detect_filter_construct_corruption = false;
|
||||
|
||||
|
||||
@@ -61,6 +61,14 @@ class LDBCommand {
|
||||
static const std::string ARG_CREATE_IF_MISSING;
|
||||
static const std::string ARG_NO_VALUE;
|
||||
static const std::string ARG_DISABLE_CONSISTENCY_CHECKS;
|
||||
static const std::string ARG_ENABLE_BLOB_FILES;
|
||||
static const std::string ARG_MIN_BLOB_SIZE;
|
||||
static const std::string ARG_BLOB_FILE_SIZE;
|
||||
static const std::string ARG_BLOB_COMPRESSION_TYPE;
|
||||
static const std::string ARG_ENABLE_BLOB_GARBAGE_COLLECTION;
|
||||
static const std::string ARG_BLOB_GARBAGE_COLLECTION_AGE_CUTOFF;
|
||||
static const std::string ARG_BLOB_GARBAGE_COLLECTION_FORCE_THRESHOLD;
|
||||
static const std::string ARG_BLOB_COMPACTION_READAHEAD_SIZE;
|
||||
|
||||
struct ParsedParams {
|
||||
std::string cmd;
|
||||
@@ -173,6 +181,10 @@ class LDBCommand {
|
||||
// The value passed to options.force_consistency_checks.
|
||||
bool force_consistency_checks_;
|
||||
|
||||
bool enable_blob_files_;
|
||||
|
||||
bool enable_blob_garbage_collection_;
|
||||
|
||||
bool create_if_missing_;
|
||||
|
||||
/**
|
||||
@@ -233,9 +245,18 @@ class LDBCommand {
|
||||
const std::string& option, int& value,
|
||||
LDBCommandExecuteResult& exec_state);
|
||||
|
||||
bool ParseDoubleOption(const std::map<std::string, std::string>& options,
|
||||
const std::string& option, double& value,
|
||||
LDBCommandExecuteResult& exec_state);
|
||||
|
||||
bool ParseStringOption(const std::map<std::string, std::string>& options,
|
||||
const std::string& option, std::string* value);
|
||||
|
||||
bool ParseCompressionTypeOption(
|
||||
const std::map<std::string, std::string>& options,
|
||||
const std::string& option, CompressionType& value,
|
||||
LDBCommandExecuteResult& exec_state);
|
||||
|
||||
/**
|
||||
* Returns the value of the specified option as a boolean.
|
||||
* default_val is used if the option is not found in options.
|
||||
|
||||
@@ -275,6 +275,7 @@ class WriteBatchWithIndex : public WriteBatchBase {
|
||||
friend class WritePreparedTxn;
|
||||
friend class WriteUnpreparedTxn;
|
||||
friend class WriteBatchWithIndex_SubBatchCnt_Test;
|
||||
friend class WriteBatchWithIndexInternal;
|
||||
// Returns the number of sub-batches inside the write batch. A sub-batch
|
||||
// starts right before inserting a key that is a duplicate of a key in the
|
||||
// last sub-batch.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#define ROCKSDB_MAJOR 7
|
||||
#define ROCKSDB_MINOR 0
|
||||
#define ROCKSDB_PATCH 4
|
||||
#define ROCKSDB_PATCH 0
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
// double underscore. Now we have to live with our choice. We'll deprecate these
|
||||
|
||||
Loaded 100 of 209 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user