mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 22:55:23 +08:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 985c8741ad | |||
| da47b71229 | |||
| 076ac983f4 | |||
| 9622795deb | |||
| e7de808d35 | |||
| 8b2cfef9e0 | |||
| 2de3bfc7d1 | |||
| 6733f75086 | |||
| fdd0e3a875 | |||
| 7789c45856 | |||
| 1ac5d37276 | |||
| 4ae8687e88 | |||
| 3e18c47db0 | |||
| 802093fe44 | |||
| 63a39d9110 | |||
| 6996495c1b | |||
| b7119ff818 | |||
| 418e58e6a2 | |||
| 0e4c063c16 | |||
| fddc1a79dd | |||
| 9db48d6e08 | |||
| 348414d502 | |||
| 7b6ca63ed0 | |||
| 9fe3a53443 | |||
| 22e011fe0a | |||
| bbae679ce7 | |||
| fe9e363ba1 | |||
| 3a4d73d337 | |||
| 4ab2fe77f3 | |||
| 596c606739 | |||
| a6fd3332e5 | |||
| f278e0e2d3 | |||
| 0344b1d331 | |||
| a4bf9311da | |||
| b251c4f5a9 | |||
| a12be569af |
+71
-101
@@ -87,6 +87,7 @@ 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
|
||||
@@ -170,42 +171,19 @@ 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 all
|
||||
- run: ulimit -S -n 1048576 && OPT=-DCIRCLECI make V=1 J=32 -j32 check
|
||||
- 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:
|
||||
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
|
||||
- run: ulimit -S -n 1048576 && (mkdir build && cd build && cmake -DWITH_GFLAGS=1 .. && make V=1 -j32 && ctest -j10)
|
||||
- post-steps
|
||||
|
||||
build-linux:
|
||||
@@ -241,38 +219,38 @@ jobs:
|
||||
build-linux-release:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: 2xlarge
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: make V=1 -j32 release
|
||||
- run: make V=1 -j8 release
|
||||
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
|
||||
- install-gflags
|
||||
- run: make V=1 -j32 release
|
||||
- run: make V=1 -j8 release
|
||||
- run: ./db_stress --version # ensure with gflags
|
||||
- post-steps
|
||||
|
||||
build-linux-release-rtti:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: xlarge
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: make clean
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j16 static_lib tools db_bench
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j8 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 -j16 static_lib tools db_bench
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j8 static_lib tools db_bench
|
||||
- run: ./db_stress --version # ensure with gflags
|
||||
|
||||
build-linux-lite:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: large
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: LITE=1 make V=1 J=8 -j8 check
|
||||
- run: LITE=1 make V=1 J=32 -j32 check
|
||||
- post-steps
|
||||
|
||||
build-linux-lite-release:
|
||||
@@ -306,37 +284,18 @@ jobs:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-clang-10
|
||||
- run: ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check # aligned new doesn't work for reason we haven't figured out
|
||||
- run: COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check # aligned new doesn't work for reason we haven't figured out
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-mini-tsan:
|
||||
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
|
||||
- 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.
|
||||
- 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.
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-ubsan:
|
||||
@@ -408,11 +367,11 @@ jobs:
|
||||
build-linux-gcc-8-no_test_run:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: xlarge
|
||||
resource_class: large
|
||||
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 -j16 all
|
||||
- run: CC=gcc-8 CXX=g++-8 V=1 make -j8 all
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-10-cxx20-no_test_run:
|
||||
@@ -422,7 +381,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 ROCKSDB_CXX_STANDARD=c++20 make -j16 all
|
||||
- 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
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-11-no_test_run:
|
||||
@@ -431,9 +390,8 @@ jobs:
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-benchmark
|
||||
- run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test && sudo apt-get update -y && sudo apt-get install gcc-11 g++-11 libgflags-dev
|
||||
- run: CC=gcc-11 CXX=g++-11 V=1 make -j16 all microbench
|
||||
- run: CC=gcc-11 CXX=g++-11 V=1 SKIP_LINK=1 make -j16 all # Linking broken because libgflags compiled with newer ABI
|
||||
- post-steps
|
||||
|
||||
build-linux-clang-13-no_test_run:
|
||||
@@ -443,30 +401,18 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-clang-13
|
||||
- install-benchmark
|
||||
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j16 all microbench
|
||||
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j16 all
|
||||
- post-steps
|
||||
|
||||
# This job is only to make sure the microbench tests are able to run, the benchmark result is not meaningful as the CI host is changing.
|
||||
build-linux-run-microbench:
|
||||
build-linux-microbench:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: 2xlarge
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-benchmark
|
||||
- run: DEBUG_LEVEL=0 make -j32 run_microbench
|
||||
- post-steps
|
||||
|
||||
build-linux-mini-crashtest:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-compression-libs
|
||||
- run: make V=1 -j8 CRASH_TEST_EXT_ARGS=--duration=960 blackbox_crash_test_with_atomic_flush
|
||||
- run: DEBUG_LEVEL=0 make microbench
|
||||
- post-steps
|
||||
|
||||
build-windows:
|
||||
@@ -585,7 +531,7 @@ jobs:
|
||||
build-macos-java:
|
||||
macos:
|
||||
xcode: 12.5.1
|
||||
resource_class: large
|
||||
resource_class: medium
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1 # jemalloc causes java 8 crash
|
||||
@@ -603,16 +549,16 @@ jobs:
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Shared Library"
|
||||
command: make V=1 J=16 -j16 rocksdbjava
|
||||
command: make V=1 J=8 -j8 rocksdbjava
|
||||
- run:
|
||||
name: "Test RocksDBJava"
|
||||
command: make V=1 J=16 -j16 jtest
|
||||
command: make V=1 J=8 -j8 jtest
|
||||
- post-steps
|
||||
|
||||
build-macos-java-static:
|
||||
macos:
|
||||
xcode: 12.5.1
|
||||
resource_class: large
|
||||
resource_class: medium
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
|
||||
steps:
|
||||
@@ -630,13 +576,13 @@ jobs:
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava x86 and ARM Static Libraries"
|
||||
command: make V=1 J=16 -j16 rocksdbjavastaticosx
|
||||
command: make V=1 J=8 -j8 rocksdbjavastaticosx
|
||||
- post-steps
|
||||
|
||||
build-macos-java-static-universal:
|
||||
macos:
|
||||
xcode: 12.5.1
|
||||
resource_class: large
|
||||
resource_class: medium
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
|
||||
steps:
|
||||
@@ -654,7 +600,7 @@ jobs:
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Universal Binary Static Library"
|
||||
command: make V=1 J=16 -j16 rocksdbjavastaticosx_ub
|
||||
command: make V=1 J=8 -j8 rocksdbjavastaticosx_ub
|
||||
- post-steps
|
||||
|
||||
build-examples:
|
||||
@@ -673,7 +619,7 @@ jobs:
|
||||
build-cmake-mingw:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: large
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
@@ -697,12 +643,30 @@ jobs:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: 2xlarge
|
||||
environment:
|
||||
TEST_TMPDIR: /tmp/rocksdb_test_tmp
|
||||
parameters:
|
||||
start_test:
|
||||
default: ""
|
||||
type: string
|
||||
end_test:
|
||||
default: ""
|
||||
type: string
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: make V=1 -j32 check
|
||||
- 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.
|
||||
- post-steps
|
||||
|
||||
build-linux-arm-test-full:
|
||||
@@ -824,12 +788,7 @@ workflows:
|
||||
- build-linux-clang10-asan
|
||||
build-linux-clang10-mini-tsan:
|
||||
jobs:
|
||||
- build-linux-clang10-mini-tsan:
|
||||
start_test: ""
|
||||
end_test: "env_test"
|
||||
- build-linux-clang10-mini-tsan:
|
||||
start_test: "env_test"
|
||||
end_test: ""
|
||||
- build-linux-clang10-mini-tsan
|
||||
build-linux-clang10-ubsan:
|
||||
jobs:
|
||||
- build-linux-clang10-ubsan
|
||||
@@ -839,9 +798,6 @@ workflows:
|
||||
build-linux-unity-and-headers:
|
||||
jobs:
|
||||
- build-linux-unity-and-headers
|
||||
build-linux-mini-crashtest:
|
||||
jobs:
|
||||
- build-linux-mini-crashtest
|
||||
build-windows-vs2019:
|
||||
jobs:
|
||||
- build-windows:
|
||||
@@ -867,6 +823,20 @@ 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
|
||||
@@ -879,16 +849,18 @@ workflows:
|
||||
build-macos:
|
||||
jobs:
|
||||
- build-macos
|
||||
- build-macos-cmake:
|
||||
run_even_tests: true
|
||||
- build-macos-cmake:
|
||||
run_even_tests: false
|
||||
build-macos-cmake:
|
||||
jobs:
|
||||
- build-macos-cmake
|
||||
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
|
||||
@@ -903,5 +875,3 @@ 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/release/12.x/clang/tools/clang-format/clang-format-diff.py
|
||||
args: https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py
|
||||
|
||||
- name: Check format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
|
||||
@@ -629,7 +629,6 @@ 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
|
||||
@@ -832,7 +831,6 @@ 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
|
||||
@@ -1158,7 +1156,6 @@ 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
|
||||
@@ -1336,7 +1333,6 @@ 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
|
||||
|
||||
+19
-24
@@ -1,34 +1,29 @@
|
||||
# Rocksdb Change Log
|
||||
## 7.1.0 (03/23/2022)
|
||||
### New Features
|
||||
* Allow WriteBatchWithIndex to index a WriteBatch that includes keys with user-defined timestamps. The index itself does not have timestamp.
|
||||
* 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`.
|
||||
* Automatically recover from retryable read IO errors during backgorund flush/compaction.
|
||||
* Experimental support for preserving file Temperatures through backup and restore, and for updating DB metadata for outside changes to file Temperature (`UpdateManifestForFilesState` or `ldb update_manifest --update_temperatures`).
|
||||
* Experimental support for async_io in ReadOptions which is used by FilePrefetchBuffer to prefetch some of the data asynchronously, if reads are sequential and auto readahead is enabled by rocksdb internally.
|
||||
## 7.0.4 (03/29/2022)
|
||||
### 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 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.
|
||||
* Fixed a bug that DisableManualCompaction may assert when disable an unscheduled manual compaction.
|
||||
* Fix a race condition when cancel manual compaction with `DisableManualCompaction`. Also DB close can cancel the manual compaction thread.
|
||||
* Fixed a potential timer crash when open close DB concurrently.
|
||||
* 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 bug that `Iterator::Refresh()` reads stale keys after DeleteRange() performed.
|
||||
* Fixed a race condition when disable and re-enable manual compaction.
|
||||
* Fixed automatic error recovery failure in atomic flush.
|
||||
* Fixed a race condition when mmaping a WritableFile on POSIX.
|
||||
|
||||
### 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. This change only affects those with their own custom or wrapper FilterPolicy classes.
|
||||
* `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`.
|
||||
* Add DB::OpenAndTrimHistory API. This API will open DB and trim data to the timestamp specified by trim_ts (The data with timestamp larger than specified trim bound will be removed). This API should only be used at a timestamp-enabled column families recovery. If the column family doesn't have timestamp enabled, this API won't trim any data on that column family. This API is not compatible with avoid_flush_during_recovery option.
|
||||
* Remove BlockBasedTableOptions.hash_index_allow_collision which already takes no effect.
|
||||
* 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.
|
||||
|
||||
## 7.0.0 (02/20/2022)
|
||||
### Bug Fixes
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
|
||||
BASH_EXISTS := $(shell which bash)
|
||||
SHELL := $(shell which bash)
|
||||
include python.mk
|
||||
# Default to python3. Some distros like CentOS 8 do not have `python`.
|
||||
ifeq ($(origin PYTHON), undefined)
|
||||
PYTHON := $(shell which python3 || which python || echo python3)
|
||||
endif
|
||||
export PYTHON
|
||||
|
||||
CLEAN_FILES = # deliberately empty, so we can append below.
|
||||
CFLAGS += ${EXTRA_CFLAGS}
|
||||
@@ -789,9 +793,7 @@ endif # PLATFORM_SHARED_EXT
|
||||
blackbox_crash_test_with_atomic_flush whitebox_crash_test_with_atomic_flush \
|
||||
blackbox_crash_test_with_txn whitebox_crash_test_with_txn \
|
||||
blackbox_crash_test_with_best_efforts_recovery \
|
||||
blackbox_crash_test_with_ts whitebox_crash_test_with_ts \
|
||||
blackbox_crash_test_with_multiops_wc_txn \
|
||||
blackbox_crash_test_with_multiops_wp_txn
|
||||
blackbox_crash_test_with_ts whitebox_crash_test_with_ts
|
||||
|
||||
|
||||
all: $(LIBRARY) $(BENCHMARKS) tools tools_lib test_libs $(TESTS)
|
||||
@@ -813,8 +815,6 @@ 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)
|
||||
@@ -909,7 +909,7 @@ gen_parallel_tests:
|
||||
# 107.816 PASS t/DBTest.EncodeDecompressedBlockSizeTest
|
||||
#
|
||||
slow_test_regexp = \
|
||||
^.*MySQLStyleTransactionTest.*$$|^.*SnapshotConcurrentAccessTest.*$$|^.*SeqAdvanceConcurrentTest.*$$|^t/run-table_test-HarnessTest.Randomized$$|^t/run-db_test-.*(?:FileCreationRandomFailure|EncodeDecompressedBlockSizeTest)$$|^.*RecoverFromCorruptedWALWithoutFlush$$
|
||||
^.*SnapshotConcurrentAccessTest.*$$|^.*SeqAdvanceConcurrentTest.*$$|^t/run-table_test-HarnessTest.Randomized$$|^t/run-db_test-.*(?:FileCreationRandomFailure|EncodeDecompressedBlockSizeTest)$$|^.*RecoverFromCorruptedWALWithoutFlush$$
|
||||
prioritize_long_running_tests = \
|
||||
perl -pe 's,($(slow_test_regexp)),100 $$1,' \
|
||||
| sort -k1,1gr \
|
||||
@@ -1029,34 +1029,92 @@ check_some: $(ROCKSDBTESTS_SUBSET)
|
||||
ldb_tests: ldb
|
||||
$(PYTHON) tools/ldb_test.py
|
||||
|
||||
include crash_test.mk
|
||||
crash_test:
|
||||
# Do not parallelize
|
||||
$(MAKE) whitebox_crash_test
|
||||
$(MAKE) blackbox_crash_test
|
||||
|
||||
crash_test_with_atomic_flush:
|
||||
# Do not parallelize
|
||||
$(MAKE) whitebox_crash_test_with_atomic_flush
|
||||
$(MAKE) blackbox_crash_test_with_atomic_flush
|
||||
|
||||
crash_test_with_txn:
|
||||
# Do not parallelize
|
||||
$(MAKE) whitebox_crash_test_with_txn
|
||||
$(MAKE) blackbox_crash_test_with_txn
|
||||
|
||||
crash_test_with_best_efforts_recovery: blackbox_crash_test_with_best_efforts_recovery
|
||||
|
||||
crash_test_with_ts:
|
||||
# Do not parallelize
|
||||
$(MAKE) whitebox_crash_test_with_ts
|
||||
$(MAKE) blackbox_crash_test_with_ts
|
||||
|
||||
blackbox_crash_test: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(PYTHON) -u tools/db_crashtest.py blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_atomic_flush: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --cf_consistency blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_txn: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --txn blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_best_efforts_recovery: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --test_best_efforts_recovery blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_ts: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --enable_ts blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
endif
|
||||
|
||||
whitebox_crash_test: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --simple whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
$(PYTHON) -u tools/db_crashtest.py whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_atomic_flush: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --cf_consistency whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_txn: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --txn whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_ts: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --enable_ts whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
asan_check: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) check -j32
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) check -j32
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
whitebox_asan_crash_test: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) whitebox_crash_test
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) whitebox_crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
blackbox_asan_crash_test: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) blackbox_crash_test
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) blackbox_crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_atomic_flush: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_atomic_flush
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_atomic_flush
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_txn: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_txn
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_txn
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_best_efforts_recovery: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_best_efforts_recovery
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_best_efforts_recovery
|
||||
$(MAKE) clean
|
||||
|
||||
ubsan_check: clean
|
||||
@@ -1806,9 +1864,6 @@ 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)
|
||||
|
||||
@@ -1842,9 +1897,6 @@ 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)
|
||||
|
||||
@@ -2046,7 +2098,7 @@ SHA256_CMD = sha256sum
|
||||
|
||||
ZLIB_VER ?= 1.2.11
|
||||
ZLIB_SHA256 ?= c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
ZLIB_DOWNLOAD_BASE ?= https://zlib.net/fossils
|
||||
BZIP2_VER ?= 1.0.8
|
||||
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
|
||||
BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
|
||||
@@ -2472,9 +2524,6 @@ 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,34 +210,14 @@ 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:
|
||||
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'])
|
||||
TARGETS.add_fancy_bench_config(config_dict['name'],config_dict['benchmarks'], False, config_dict['expected_runtime'])
|
||||
|
||||
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:
|
||||
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:
|
||||
TARGETS.add_fancy_bench_config(config_dict['name']+"_slow",config_dict['benchmarks'], True, config_dict['expected_runtime'])
|
||||
|
||||
except (FileNotFoundError, KeyError):
|
||||
pass
|
||||
|
||||
TARGETS.add_test_header()
|
||||
|
||||
@@ -92,14 +92,12 @@ add_c_test_wrapper()
|
||||
# will not be included.
|
||||
""")
|
||||
|
||||
def add_fancy_bench_config(self, name, bench_config, slow, expected_runtime, sl_iterations, regression_threshold):
|
||||
def add_fancy_bench_config(self, name, bench_config, slow, expected_runtime):
|
||||
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}, sl_iterations={sl_iterations}, regression_threshold={regression_threshold})
|
||||
fancy_bench_wrapper(suite_name="{name}", binary_to_bench_to_metric_list_map={bench_config}, slow={slow}, expected_runtime={expected_runtime})
|
||||
|
||||
"""
|
||||
|
||||
@@ -638,9 +638,6 @@ 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
|
||||
@@ -661,11 +658,6 @@ 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"
|
||||
|
||||
@@ -106,14 +106,12 @@ SHM="TEST_TMPDIR=/dev/shm/rocksdb"
|
||||
NON_SHM="TMPD=/tmp/rocksdb_test_tmp"
|
||||
GCC_481="ROCKSDB_FBCODE_BUILD_WITH_481=1"
|
||||
ASAN="COMPILE_WITH_ASAN=1"
|
||||
ASAN_TEST_OPTIONS="ASAN_OPTIONS=detect_stack_use_after_return=1"
|
||||
CLANG="USE_CLANG=1"
|
||||
TSAN="COMPILE_WITH_TSAN=1"
|
||||
UBSAN="COMPILE_WITH_UBSAN=1"
|
||||
ASAN_CRASH="ASAN_OPTIONS=disable_coredump=0:detect_stack_use_after_return=1"
|
||||
TSAN_CRASH="TSAN_OPTIONS=disable_coredump=0 CRASH_TEST_EXT_ARGS=\\\"--compression_type=zstd --log2_keys_per_lock=22\\\""
|
||||
ASAN_CRASH="ASAN_OPTIONS=disable_coredump=0"
|
||||
TSAN_CRASH="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"
|
||||
@@ -598,7 +596,7 @@ ASAN_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
\"name\":\"Test RocksDB debug under ASAN\",
|
||||
\"shell\":\"cd $WORKING_DIR; set -o pipefail && $SHM $ASAN $ASAN_TEST_OPTIONS $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM asan_check || $CONTRUN_NAME=asan_check $TASK_CREATION_TOOL\",
|
||||
\"shell\":\"cd $WORKING_DIR; set -o pipefail && $SHM $ASAN $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM asan_check || $CONTRUN_NAME=asan_check $TASK_CREATION_TOOL\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
}
|
||||
@@ -762,7 +760,7 @@ UBSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
\"name\":\"Build and run RocksDB debug ubsan_crash_test\",
|
||||
\"timeout\": 86400,
|
||||
\"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\",
|
||||
\"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\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
@@ -786,7 +784,7 @@ UBSAN_BLACKBOX_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
\"name\":\"Build and run RocksDB debug blackbox ubsan_crash_test\",
|
||||
\"timeout\": 86400,
|
||||
\"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\",
|
||||
\"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\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
@@ -810,7 +808,7 @@ UBSAN_WHITEBOX_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
\"name\":\"Build and run RocksDB debug whitebox ubsan_crash_test\",
|
||||
\"timeout\": 86400,
|
||||
\"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\",
|
||||
\"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\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
@@ -834,7 +832,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 $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\",
|
||||
\"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\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
@@ -858,7 +856,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 $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\",
|
||||
\"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\",
|
||||
\"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 CLOCK cache being full.");
|
||||
s = Status::Incomplete("Insert failed due to LRU cache being full.");
|
||||
} else {
|
||||
*out_handle = reinterpret_cast<Cache::Handle*>(handle);
|
||||
}
|
||||
|
||||
Vendored
-168
@@ -1,168 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#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
@@ -1,85 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#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
@@ -1,597 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#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();
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
# This file is used by Meta-internal infrastructure as well as by Makefile
|
||||
|
||||
# When included from Makefile, there are rules to build DB_STRESS_CMD. When
|
||||
# used directly with `make -f crashtest.mk ...` there will be no rules to
|
||||
# build DB_STRESS_CMD so it must exist prior.
|
||||
DB_STRESS_CMD?=./db_stress
|
||||
|
||||
include python.mk
|
||||
|
||||
CRASHTEST_MAKE=$(MAKE) -f crash_test.mk
|
||||
CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD)
|
||||
|
||||
.PHONY: crash_test crash_test_with_atomic_flush crash_test_with_txn \
|
||||
crash_test_with_best_efforts_recovery crash_test_with_ts \
|
||||
blackbox_crash_test blackbox_crash_test_with_atomic_flush \
|
||||
blackbox_crash_test_with_txn blackbox_crash_test_with_ts \
|
||||
blackbox_crash_test_with_best_efforts_recovery \
|
||||
whitebox_crash_test whitebox_crash_test_with_atomic_flush \
|
||||
whitebox_crash_test_with_txn whitebox_crash_test_with_ts \
|
||||
blackbox_crash_test_with_multiops_wc_txn \
|
||||
blackbox_crash_test_with_multiops_wp_txn
|
||||
|
||||
crash_test: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test
|
||||
|
||||
crash_test_with_atomic_flush: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_atomic_flush
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_atomic_flush
|
||||
|
||||
crash_test_with_txn: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_txn
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_txn
|
||||
|
||||
crash_test_with_best_efforts_recovery: blackbox_crash_test_with_best_efforts_recovery
|
||||
|
||||
crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_ts
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_ts
|
||||
|
||||
blackbox_crash_test: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(CRASHTEST_PY) blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_atomic_flush: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --cf_consistency blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --txn blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_best_efforts_recovery: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_best_efforts_recovery blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --enable_ts blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_multiops_wc_txn: $(DB_STRESS_CMD)
|
||||
$(PYTHON) -u tools/db_crashtest.py --test_multiops_txn --write_policy write_committed blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_multiops_wp_txn: $(DB_STRESS_CMD)
|
||||
$(PYTHON) -u tools/db_crashtest.py --test_multiops_txn --write_policy write_prepared blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
endif
|
||||
|
||||
whitebox_crash_test: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --simple whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
$(CRASHTEST_PY) whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_atomic_flush: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --cf_consistency whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --txn whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --enable_ts whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
@@ -318,7 +318,6 @@ Status BuildTable(
|
||||
|
||||
// TODO Also check the IO status when create the Iterator.
|
||||
|
||||
TEST_SYNC_POINT("BuildTable:BeforeOutputValidation");
|
||||
if (s.ok() && !empty) {
|
||||
// Verify that the table is usable
|
||||
// We set for_compaction to false and don't OptimizeForCompactionTableRead
|
||||
|
||||
@@ -2297,6 +2297,11 @@ 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;
|
||||
|
||||
+2
-16
@@ -1164,12 +1164,12 @@ Compaction* ColumnFamilyData::CompactRange(
|
||||
int output_level, const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* conflict,
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts) {
|
||||
uint64_t max_file_num_to_ignore) {
|
||||
auto* result = compaction_picker_->CompactRange(
|
||||
GetName(), mutable_cf_options, mutable_db_options,
|
||||
current_->storage_info(), input_level, output_level,
|
||||
compact_range_options, begin, end, compaction_end, conflict,
|
||||
max_file_num_to_ignore, trim_ts);
|
||||
max_file_num_to_ignore);
|
||||
if (result != nullptr) {
|
||||
result->SetInputVersion(current_);
|
||||
}
|
||||
@@ -1562,20 +1562,6 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
return new_cfd;
|
||||
}
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
void ColumnFamilySet::FreeDeadColumnFamilies() {
|
||||
autovector<ColumnFamilyData*> to_delete;
|
||||
for (auto cfd = dummy_cfd_->next_; cfd != dummy_cfd_; cfd = cfd->next_) {
|
||||
if (cfd->refs_.load(std::memory_order_relaxed) == 0) {
|
||||
to_delete.push_back(cfd);
|
||||
}
|
||||
}
|
||||
for (auto cfd : to_delete) {
|
||||
// this is very rare, so it's not a problem that we do it under a mutex
|
||||
delete cfd;
|
||||
}
|
||||
}
|
||||
|
||||
// under a DB mutex AND from a write thread
|
||||
void ColumnFamilySet::RemoveColumnFamily(ColumnFamilyData* cfd) {
|
||||
auto cfd_iter = column_family_data_.find(cfd->GetID());
|
||||
|
||||
+57
-19
@@ -413,8 +413,7 @@ class ColumnFamilyData {
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore,
|
||||
const std::string& trim_ts);
|
||||
uint64_t max_file_num_to_ignore);
|
||||
|
||||
CompactionPicker* compaction_picker() { return compaction_picker_.get(); }
|
||||
// thread-safe
|
||||
@@ -520,9 +519,10 @@ class ColumnFamilyData {
|
||||
ThreadLocalPtr* TEST_GetLocalSV() { return local_sv_.get(); }
|
||||
WriteBufferManager* write_buffer_mgr() { return write_buffer_manager_; }
|
||||
|
||||
static const uint32_t kDummyColumnFamilyDataId;
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
static const uint32_t kDummyColumnFamilyDataId;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
Version* dummy_versions, Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
@@ -628,10 +628,8 @@ class ColumnFamilyData {
|
||||
// held and it needs to be executed from the write thread. SetDropped() also
|
||||
// guarantees that it will be called only from single-threaded LogAndApply(),
|
||||
// but this condition is not that important.
|
||||
// * Iteration -- hold DB mutex, but you can release it in the body of
|
||||
// iteration. If you release DB mutex in body, reference the column
|
||||
// family before the mutex and unreference after you unlock, since the column
|
||||
// family might get dropped when the DB mutex is released
|
||||
// * Iteration -- hold DB mutex. If you want to release the DB mutex in the
|
||||
// body of the iteration, wrap in a RefedColumnFamilySet.
|
||||
// * GetDefault() -- thread safe
|
||||
// * GetColumnFamily() -- either inside of DB mutex or from a write thread
|
||||
// * GetNextColumnFamilyID(), GetMaxColumnFamily(), UpdateMaxColumnFamily(),
|
||||
@@ -643,17 +641,12 @@ class ColumnFamilySet {
|
||||
public:
|
||||
explicit iterator(ColumnFamilyData* cfd)
|
||||
: current_(cfd) {}
|
||||
// NOTE: minimum operators for for-loop iteration
|
||||
iterator& operator++() {
|
||||
// dropped column families might still be included in this iteration
|
||||
// (we're only removing them when client drops the last reference to the
|
||||
// column family).
|
||||
// dummy is never dead, so this will never be infinite
|
||||
do {
|
||||
current_ = current_->next_;
|
||||
} while (current_->refs_.load(std::memory_order_relaxed) == 0);
|
||||
current_ = current_->next_;
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(const iterator& other) {
|
||||
bool operator!=(const iterator& other) const {
|
||||
return this->current_ != other.current_;
|
||||
}
|
||||
ColumnFamilyData* operator*() { return current_; }
|
||||
@@ -692,10 +685,6 @@ class ColumnFamilySet {
|
||||
iterator begin() { return iterator(dummy_cfd_->next_); }
|
||||
iterator end() { return iterator(dummy_cfd_); }
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
// Don't call while iterating over ColumnFamilySet
|
||||
void FreeDeadColumnFamilies();
|
||||
|
||||
Cache* get_table_cache() { return table_cache_; }
|
||||
|
||||
WriteBufferManager* write_buffer_manager() { return write_buffer_manager_; }
|
||||
@@ -738,6 +727,55 @@ class ColumnFamilySet {
|
||||
std::string db_session_id_;
|
||||
};
|
||||
|
||||
// A wrapper for ColumnFamilySet that supports releasing DB mutex during each
|
||||
// iteration over the iterator, because the cfd is Refed and Unrefed during
|
||||
// each iteration to prevent concurrent CF drop from destroying it (until
|
||||
// Unref).
|
||||
class RefedColumnFamilySet {
|
||||
public:
|
||||
explicit RefedColumnFamilySet(ColumnFamilySet* cfs) : wrapped_(cfs) {}
|
||||
|
||||
class iterator {
|
||||
public:
|
||||
explicit iterator(ColumnFamilySet::iterator wrapped) : wrapped_(wrapped) {
|
||||
MaybeRef(*wrapped_);
|
||||
}
|
||||
~iterator() { MaybeUnref(*wrapped_); }
|
||||
inline void MaybeRef(ColumnFamilyData* cfd) {
|
||||
if (cfd->GetID() != ColumnFamilyData::kDummyColumnFamilyDataId) {
|
||||
cfd->Ref();
|
||||
}
|
||||
}
|
||||
inline void MaybeUnref(ColumnFamilyData* cfd) {
|
||||
if (cfd->GetID() != ColumnFamilyData::kDummyColumnFamilyDataId) {
|
||||
cfd->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
// NOTE: minimum operators for for-loop iteration
|
||||
inline iterator& operator++() {
|
||||
ColumnFamilyData* old = *wrapped_;
|
||||
++wrapped_;
|
||||
// Can only unref & potentially free cfd after accessing its next_
|
||||
MaybeUnref(old);
|
||||
MaybeRef(*wrapped_);
|
||||
return *this;
|
||||
}
|
||||
inline bool operator!=(const iterator& other) const {
|
||||
return this->wrapped_ != other.wrapped_;
|
||||
}
|
||||
inline ColumnFamilyData* operator*() { return *wrapped_; }
|
||||
|
||||
private:
|
||||
ColumnFamilySet::iterator wrapped_;
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(wrapped_->begin()); }
|
||||
iterator end() { return iterator(wrapped_->end()); }
|
||||
|
||||
private:
|
||||
ColumnFamilySet* wrapped_;
|
||||
};
|
||||
|
||||
// We use ColumnFamilyMemTablesImpl to provide WriteBatch a way to access
|
||||
// memtables of different column families (specified by ID in the write batch)
|
||||
class ColumnFamilyMemTablesImpl : public ColumnFamilyMemTables {
|
||||
|
||||
@@ -138,9 +138,6 @@ 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));
|
||||
|
||||
@@ -299,9 +296,6 @@ 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());
|
||||
|
||||
@@ -420,9 +414,6 @@ 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());
|
||||
|
||||
|
||||
@@ -213,8 +213,8 @@ Compaction::Compaction(
|
||||
uint32_t _output_path_id, CompressionType _compression,
|
||||
CompressionOptions _compression_opts, Temperature _output_temperature,
|
||||
uint32_t _max_subcompactions, std::vector<FileMetaData*> _grandparents,
|
||||
bool _manual_compaction, const std::string& _trim_ts, double _score,
|
||||
bool _deletion_compaction, CompactionReason _compaction_reason)
|
||||
bool _manual_compaction, double _score, bool _deletion_compaction,
|
||||
CompactionReason _compaction_reason)
|
||||
: input_vstorage_(vstorage),
|
||||
start_level_(_inputs[0].level),
|
||||
output_level_(_output_level),
|
||||
@@ -237,7 +237,6 @@ Compaction::Compaction(
|
||||
bottommost_level_(IsBottommostLevel(output_level_, vstorage, inputs_)),
|
||||
is_full_compaction_(IsFullCompaction(vstorage, inputs_)),
|
||||
is_manual_compaction_(_manual_compaction),
|
||||
trim_ts_(_trim_ts),
|
||||
is_trivial_move_(false),
|
||||
compaction_reason_(_compaction_reason),
|
||||
notify_on_compaction_completion_(false) {
|
||||
@@ -278,9 +277,9 @@ Compaction::~Compaction() {
|
||||
|
||||
bool Compaction::InputCompressionMatchesOutput() const {
|
||||
int base_level = input_vstorage_->base_level();
|
||||
bool matches =
|
||||
(GetCompressionType(input_vstorage_, mutable_cf_options_, start_level_,
|
||||
base_level) == output_compression_);
|
||||
bool matches = (GetCompressionType(immutable_options_, input_vstorage_,
|
||||
mutable_cf_options_, start_level_,
|
||||
base_level) == output_compression_);
|
||||
if (matches) {
|
||||
TEST_SYNC_POINT("Compaction::InputCompressionMatchesOutput:Matches");
|
||||
return true;
|
||||
|
||||
@@ -79,8 +79,8 @@ class Compaction {
|
||||
CompressionOptions compression_opts,
|
||||
Temperature output_temperature, uint32_t max_subcompactions,
|
||||
std::vector<FileMetaData*> grandparents,
|
||||
bool manual_compaction = false, const std::string& trim_ts = "",
|
||||
double score = -1, bool deletion_compaction = false,
|
||||
bool manual_compaction = false, double score = -1,
|
||||
bool deletion_compaction = false,
|
||||
CompactionReason compaction_reason = CompactionReason::kUnknown);
|
||||
|
||||
// No copying allowed
|
||||
@@ -208,8 +208,6 @@ class Compaction {
|
||||
// Was this compaction triggered manually by the client?
|
||||
bool is_manual_compaction() const { return is_manual_compaction_; }
|
||||
|
||||
std::string trim_ts() const { return trim_ts_; }
|
||||
|
||||
// Used when allow_trivial_move option is set in
|
||||
// Universal compaction. If all the input files are
|
||||
// non overlapping, then is_trivial_move_ variable
|
||||
@@ -387,9 +385,6 @@ class Compaction {
|
||||
// Is this compaction requested by the client?
|
||||
const bool is_manual_compaction_;
|
||||
|
||||
// The data with timestamp > trim_ts_ will be removed
|
||||
const std::string trim_ts_;
|
||||
|
||||
// True if we can do trivial move in Universal multi level
|
||||
// compaction
|
||||
bool is_trivial_move_;
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include "db/dbformat.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/history_trimming_iterator.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/memtable.h"
|
||||
@@ -430,8 +429,7 @@ CompactionJob::CompactionJob(
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::atomic<bool>* manual_compaction_canceled,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
std::string full_history_ts_low, std::string trim_ts,
|
||||
BlobFileCompletionCallback* blob_callback)
|
||||
std::string full_history_ts_low, BlobFileCompletionCallback* blob_callback)
|
||||
: compact_(new CompactionState(compaction)),
|
||||
compaction_stats_(compaction->compaction_reason(), 1),
|
||||
db_options_(db_options),
|
||||
@@ -470,7 +468,6 @@ CompactionJob::CompactionJob(
|
||||
measure_io_stats_(measure_io_stats),
|
||||
thread_pri_(thread_pri),
|
||||
full_history_ts_low_(std::move(full_history_ts_low)),
|
||||
trim_ts_(std::move(trim_ts)),
|
||||
blob_callback_(blob_callback) {
|
||||
assert(compaction_job_stats_ != nullptr);
|
||||
assert(log_buffer_ != nullptr);
|
||||
@@ -1383,28 +1380,21 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
|
||||
std::unique_ptr<InternalIterator> clip;
|
||||
if (start || end) {
|
||||
clip = std::make_unique<ClippingIterator>(
|
||||
clip.reset(new ClippingIterator(
|
||||
raw_input.get(), start ? &start_slice : nullptr,
|
||||
end ? &end_slice : nullptr, &cfd->internal_comparator());
|
||||
end ? &end_slice : nullptr, &cfd->internal_comparator()));
|
||||
input = clip.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<InternalIterator> blob_counter;
|
||||
|
||||
if (sub_compact->compaction->DoesInputReferenceBlobFiles()) {
|
||||
sub_compact->blob_garbage_meter = std::make_unique<BlobGarbageMeter>();
|
||||
blob_counter = std::make_unique<BlobCountingIterator>(
|
||||
input, sub_compact->blob_garbage_meter.get());
|
||||
sub_compact->blob_garbage_meter.reset(new BlobGarbageMeter);
|
||||
blob_counter.reset(
|
||||
new BlobCountingIterator(input, sub_compact->blob_garbage_meter.get()));
|
||||
input = blob_counter.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<InternalIterator> trim_history_iter;
|
||||
if (cfd->user_comparator()->timestamp_size() > 0 && !trim_ts_.empty()) {
|
||||
trim_history_iter = std::make_unique<HistoryTrimmingIterator>(
|
||||
input, cfd->user_comparator(), trim_ts_);
|
||||
input = trim_history_iter.get();
|
||||
}
|
||||
|
||||
input->SeekToFirst();
|
||||
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
|
||||
@@ -82,7 +82,7 @@ class CompactionJob {
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::atomic<bool>* manual_compaction_canceled = nullptr,
|
||||
const std::string& db_id = "", const std::string& db_session_id = "",
|
||||
std::string full_history_ts_low = "", std::string trim_ts = "",
|
||||
std::string full_history_ts_low = "",
|
||||
BlobFileCompletionCallback* blob_callback = nullptr);
|
||||
|
||||
virtual ~CompactionJob();
|
||||
@@ -226,7 +226,6 @@ class CompactionJob {
|
||||
std::vector<uint64_t> sizes_;
|
||||
Env::Priority thread_pri_;
|
||||
std::string full_history_ts_low_;
|
||||
std::string trim_ts_;
|
||||
BlobFileCompletionCallback* blob_callback_;
|
||||
|
||||
uint64_t GetCompactionId(SubcompactionState* sub_compact);
|
||||
|
||||
@@ -100,7 +100,8 @@ 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 VersionStorageInfo* vstorage,
|
||||
CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
const VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int level, int base_level,
|
||||
const bool enable_compression) {
|
||||
@@ -117,19 +118,17 @@ CompressionType GetCompressionType(const VersionStorageInfo* vstorage,
|
||||
}
|
||||
// If the user has specified a different compression level for each level,
|
||||
// then pick the compression for that level.
|
||||
if (!mutable_cf_options.compression_per_level.empty()) {
|
||||
if (!ioptions.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>(mutable_cf_options.compression_per_level.size()) - 1;
|
||||
const int n = static_cast<int>(ioptions.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 mutable_cf_options
|
||||
.compression_per_level[std::max(0, std::min(idx, n))];
|
||||
return ioptions.compression_per_level[std::max(0, std::min(idx, n))];
|
||||
} else {
|
||||
return mutable_cf_options.compression;
|
||||
}
|
||||
@@ -348,8 +347,9 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
} else {
|
||||
base_level = 1;
|
||||
}
|
||||
compression_type = GetCompressionType(vstorage, mutable_cf_options,
|
||||
output_level, base_level);
|
||||
compression_type =
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, base_level);
|
||||
} else {
|
||||
// TODO(ajkr): `CompactionOptions` offers configurable `CompressionType`
|
||||
// without configurable `CompressionOptions`, which is inconsistent.
|
||||
@@ -571,7 +571,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
int input_level, int output_level,
|
||||
const CompactRangeOptions& compact_range_options, const InternalKey* begin,
|
||||
const InternalKey* end, InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts) {
|
||||
uint64_t max_file_num_to_ignore) {
|
||||
// CompactionPickerFIFO has its own implementation of compact range
|
||||
assert(ioptions_.compaction_style != kCompactionStyleFIFO);
|
||||
|
||||
@@ -637,10 +637,12 @@ Compaction* CompactionPicker::CompactRange(
|
||||
ioptions_.compaction_style),
|
||||
/* max_compaction_bytes */ LLONG_MAX,
|
||||
compact_range_options.target_path_id,
|
||||
GetCompressionType(vstorage, mutable_cf_options, output_level, 1),
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
/* grandparents */ {}, /* is manual */ true, trim_ts);
|
||||
/* grandparents */ {},
|
||||
/* is manual */ true);
|
||||
RegisterCompaction(c);
|
||||
vstorage->ComputeCompactionScore(ioptions_, mutable_cf_options);
|
||||
return c;
|
||||
@@ -814,12 +816,12 @@ Compaction* CompactionPicker::CompactRange(
|
||||
ioptions_.level_compaction_dynamic_level_bytes),
|
||||
mutable_cf_options.max_compaction_bytes,
|
||||
compact_range_options.target_path_id,
|
||||
GetCompressionType(vstorage, mutable_cf_options, output_level,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, output_level,
|
||||
vstorage->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
std::move(grandparents),
|
||||
/* is manual compaction */ true, trim_ts);
|
||||
/* is manual compaction */ true);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionPicker::CompactRange:Return", compaction);
|
||||
RegisterCompaction(compaction);
|
||||
|
||||
@@ -78,7 +78,7 @@ class CompactionPicker {
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts);
|
||||
uint64_t max_file_num_to_ignore);
|
||||
|
||||
// The maximum allowed output level. Default value is NumberLevels() - 1.
|
||||
virtual int MaxOutputLevel() const { return NumberLevels() - 1; }
|
||||
@@ -270,8 +270,7 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
const InternalKey* /*end*/,
|
||||
InternalKey** /*compaction_end*/,
|
||||
bool* /*manual_conflict*/,
|
||||
uint64_t /*max_file_num_to_ignore*/,
|
||||
const std::string& /*trim_ts*/) override {
|
||||
uint64_t /*max_file_num_to_ignore*/) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -305,7 +304,8 @@ bool FindIntraL0Compaction(
|
||||
CompactionInputFiles* comp_inputs,
|
||||
SequenceNumber earliest_mem_seqno = kMaxSequenceNumber);
|
||||
|
||||
CompressionType GetCompressionType(const VersionStorageInfo* vstorage,
|
||||
CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
const VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int level, int base_level,
|
||||
const bool enable_compression = true);
|
||||
|
||||
@@ -115,7 +115,7 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
std::move(inputs), 0, 0, 0, 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOTtl);
|
||||
return c;
|
||||
}
|
||||
@@ -157,8 +157,8 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
0 /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID */, mutable_cf_options.compression,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
0 /* max_subcompactions */, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
0 /* max_subcompactions */, {},
|
||||
/* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
CompactionReason::kFIFOReduceNumFiles);
|
||||
return c;
|
||||
@@ -208,7 +208,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
std::move(inputs), 0, 0, 0, 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOMaxSize);
|
||||
return c;
|
||||
}
|
||||
@@ -313,7 +313,7 @@ Compaction* FIFOCompactionPicker::PickCompactionToWarm(
|
||||
0 /* max compaction bytes, not applicable */, 0 /* output path ID */,
|
||||
mutable_cf_options.compression, mutable_cf_options.compression_opts,
|
||||
Temperature::kWarm,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false, /* trim_ts */ "",
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false, CompactionReason::kChangeTemperature);
|
||||
return c;
|
||||
@@ -349,7 +349,7 @@ Compaction* FIFOCompactionPicker::CompactRange(
|
||||
const CompactRangeOptions& /*compact_range_options*/,
|
||||
const InternalKey* /*begin*/, const InternalKey* /*end*/,
|
||||
InternalKey** compaction_end, bool* /*manual_conflict*/,
|
||||
uint64_t /*max_file_num_to_ignore*/, const std::string& /*trim_ts*/) {
|
||||
uint64_t /*max_file_num_to_ignore*/) {
|
||||
#ifdef NDEBUG
|
||||
(void)input_level;
|
||||
(void)output_level;
|
||||
|
||||
@@ -32,7 +32,7 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts) override;
|
||||
uint64_t max_file_num_to_ignore) override;
|
||||
|
||||
// The maximum allowed output level. Always returns 0.
|
||||
virtual int MaxOutputLevel() const override { return 0; }
|
||||
|
||||
@@ -343,13 +343,12 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
ioptions_.level_compaction_dynamic_level_bytes),
|
||||
mutable_cf_options_.max_compaction_bytes,
|
||||
GetPathId(ioptions_, mutable_cf_options_, output_level_),
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level_,
|
||||
vstorage_->base_level()),
|
||||
GetCompressionType(ioptions_, 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_,
|
||||
/* trim_ts */ "", start_level_score_, false /* deletion_compaction */,
|
||||
compaction_reason_);
|
||||
start_level_score_, false /* deletion_compaction */, compaction_reason_);
|
||||
|
||||
// If it's level 0 compaction, make sure we don't execute any other level 0
|
||||
// compactions in parallel
|
||||
|
||||
@@ -2648,13 +2648,12 @@ TEST_F(CompactionPickerTest, UniversalMarkedManualCompaction) {
|
||||
ASSERT_EQ(3U, vstorage_->FilesMarkedForCompaction().size());
|
||||
|
||||
bool manual_conflict = false;
|
||||
InternalKey* manual_end = nullptr;
|
||||
InternalKey* manual_end = NULL;
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.CompactRange(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
ColumnFamilyData::kCompactAllLevels, 6, CompactRangeOptions(),
|
||||
nullptr, nullptr, &manual_end, &manual_conflict, port::kMaxUint64,
|
||||
""));
|
||||
ColumnFamilyData::kCompactAllLevels, 6, CompactRangeOptions(), NULL,
|
||||
NULL, &manual_end, &manual_conflict, port::kMaxUint64));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
|
||||
|
||||
@@ -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(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, /* trim_ts */ "", 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(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);
|
||||
}
|
||||
|
||||
// Look at overall size amplification. If size amplification
|
||||
@@ -1076,13 +1076,13 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level,
|
||||
kCompactionStyleUniversal),
|
||||
GetMaxOverlappingBytes(), path_id,
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1,
|
||||
true /* enable_compression */),
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level, 1, true /* enable_compression */),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
score_, false /* deletion_compaction */,
|
||||
CompactionReason::kUniversalSizeAmplification);
|
||||
}
|
||||
|
||||
@@ -1220,11 +1220,12 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level,
|
||||
kCompactionStyleUniversal),
|
||||
/* max_grandparent_overlap_bytes */ GetMaxOverlappingBytes(), path_id,
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1),
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, grandparents, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* max_subcompactions */ 0, grandparents, /* is manual */ false, score_,
|
||||
false /* deletion_compaction */,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
}
|
||||
|
||||
@@ -1293,14 +1294,13 @@ Compaction* UniversalCompactionBuilder::PickCompactionToOldest(
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level,
|
||||
kCompactionStyleUniversal),
|
||||
GetMaxOverlappingBytes(), path_id,
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1,
|
||||
true /* enable_compression */),
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level, 1, true /* enable_compression */),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
compaction_reason);
|
||||
score_, false /* deletion_compaction */, compaction_reason);
|
||||
}
|
||||
|
||||
Compaction* UniversalCompactionBuilder::PickPeriodicCompaction() {
|
||||
|
||||
@@ -1540,93 +1540,6 @@ 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.
|
||||
@@ -2256,6 +2169,7 @@ 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_;
|
||||
|
||||
@@ -45,17 +45,15 @@ Status DBImpl::FlushForGetLiveFiles() {
|
||||
}
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions(), FlushReason::kGetLiveFiles);
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:1");
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:2");
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (!status.ok() && !status.IsColumnFamilyDropped()) {
|
||||
break;
|
||||
} else if (status.IsColumnFamilyDropped()) {
|
||||
@@ -63,7 +61,6 @@ Status DBImpl::FlushForGetLiveFiles() {
|
||||
}
|
||||
}
|
||||
}
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -1374,140 +1374,6 @@ TEST_F(DBFlushTest, DISABLED_MemPurgeWALSupport) {
|
||||
} while (ChangeWalOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, MemPurgeCorrectLogNumberAndSSTFileCreation) {
|
||||
// Before our bug fix, we noticed that when 2 memtables were
|
||||
// being flushed (with one memtable being the output of a
|
||||
// previous MemPurge and one memtable being a newly-sealed memtable),
|
||||
// the SST file created was not properly added to the DB version
|
||||
// (via the VersionEdit obj), leading to data loss (the SST file
|
||||
// was later being purged as an obsolete file).
|
||||
// Therefore, we reproduce this scenario to test our fix.
|
||||
Options options = CurrentOptions();
|
||||
|
||||
options.create_if_missing = true;
|
||||
options.compression = kNoCompression;
|
||||
options.inplace_update_support = false;
|
||||
options.allow_concurrent_memtable_write = true;
|
||||
|
||||
// Enforce size of a single MemTable to 1MB (64MB = 1048576 bytes).
|
||||
options.write_buffer_size = 1 << 20;
|
||||
// Activate the MemPurge prototype.
|
||||
options.experimental_mempurge_threshold = 1.0;
|
||||
|
||||
// Force to have more than one memtable to trigger a flush.
|
||||
// For some reason this option does not seem to be enforced,
|
||||
// so the following test is designed to make sure that we
|
||||
// are testing the correct test case.
|
||||
options.min_write_buffer_number_to_merge = 3;
|
||||
options.max_write_buffer_number = 5;
|
||||
options.max_write_buffer_size_to_maintain = 2 * (options.write_buffer_size);
|
||||
options.disable_auto_compactions = true;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
std::atomic<uint32_t> mempurge_count{0};
|
||||
std::atomic<uint32_t> sst_count{0};
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushJob:MemPurgeSuccessful",
|
||||
[&](void* /*arg*/) { mempurge_count++; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushJob:SSTFileCreated", [&](void* /*arg*/) { sst_count++; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Dummy variable used for the following callback function.
|
||||
uint64_t ZERO = 0;
|
||||
// We will first execute mempurge operations exclusively.
|
||||
// Therefore, when the first flush is triggered, we want to make
|
||||
// sure there is at least 2 memtables being flushed: one output
|
||||
// from a previous mempurge, and one newly sealed memtable.
|
||||
// This is when we observed in the past that some SST files created
|
||||
// were not properly added to the DB version (via the VersionEdit obj).
|
||||
std::atomic<uint64_t> num_memtable_at_first_flush(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:num_memtables", [&](void* arg) {
|
||||
uint64_t* mems_size = reinterpret_cast<uint64_t*>(arg);
|
||||
// atomic_compare_exchange_strong sometimes updates the value
|
||||
// of ZERO (the "expected" object), so we make sure ZERO is indeed...
|
||||
// zero.
|
||||
ZERO = 0;
|
||||
std::atomic_compare_exchange_strong(&num_memtable_at_first_flush, &ZERO,
|
||||
*mems_size);
|
||||
});
|
||||
|
||||
const std::vector<std::string> KEYS = {
|
||||
"ThisIsKey1", "ThisIsKey2", "ThisIsKey3", "ThisIsKey4", "ThisIsKey5",
|
||||
"ThisIsKey6", "ThisIsKey7", "ThisIsKey8", "ThisIsKey9"};
|
||||
const std::string NOT_FOUND = "NOT_FOUND";
|
||||
|
||||
Random rnd(117);
|
||||
const uint64_t NUM_REPEAT_OVERWRITES = 100;
|
||||
const uint64_t NUM_RAND_INSERTS = 500;
|
||||
const uint64_t RAND_VALUES_LENGTH = 10240;
|
||||
|
||||
std::string key, value;
|
||||
std::vector<std::string> values(9, "");
|
||||
|
||||
// Keys used to check that no SST file disappeared.
|
||||
for (uint64_t k = 0; k < 5; k++) {
|
||||
values[k] = rnd.RandomString(RAND_VALUES_LENGTH);
|
||||
ASSERT_OK(Put(KEYS[k], values[k]));
|
||||
}
|
||||
|
||||
// Insertion of of K-V pairs, multiple times.
|
||||
// Trigger at least one mempurge and no SST file creation.
|
||||
for (size_t i = 0; i < NUM_REPEAT_OVERWRITES; i++) {
|
||||
// Create value strings of arbitrary length RAND_VALUES_LENGTH bytes.
|
||||
for (uint64_t k = 5; k < values.size(); k++) {
|
||||
values[k] = rnd.RandomString(RAND_VALUES_LENGTH);
|
||||
ASSERT_OK(Put(KEYS[k], values[k]));
|
||||
}
|
||||
// Check database consistency.
|
||||
for (uint64_t k = 0; k < values.size(); k++) {
|
||||
ASSERT_EQ(Get(KEYS[k]), values[k]);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that there was at least one mempurge
|
||||
uint32_t expected_min_mempurge_count = 1;
|
||||
// Check that there was no SST files created during flush.
|
||||
uint32_t expected_sst_count = 0;
|
||||
EXPECT_GE(mempurge_count.load(), expected_min_mempurge_count);
|
||||
EXPECT_EQ(sst_count.load(), expected_sst_count);
|
||||
|
||||
// Trigger an SST file creation and no mempurge.
|
||||
for (size_t i = 0; i < NUM_RAND_INSERTS; i++) {
|
||||
key = rnd.RandomString(RAND_VALUES_LENGTH);
|
||||
// Create value strings of arbitrary length RAND_VALUES_LENGTH bytes.
|
||||
value = rnd.RandomString(RAND_VALUES_LENGTH);
|
||||
ASSERT_OK(Put(key, value));
|
||||
// Check database consistency.
|
||||
for (uint64_t k = 0; k < values.size(); k++) {
|
||||
ASSERT_EQ(Get(KEYS[k]), values[k]);
|
||||
}
|
||||
ASSERT_EQ(Get(key), value);
|
||||
}
|
||||
|
||||
// Check that there was at least one SST files created during flush.
|
||||
expected_sst_count = 1;
|
||||
EXPECT_GE(sst_count.load(), expected_sst_count);
|
||||
|
||||
// Oddly enough, num_memtable_at_first_flush is not enforced to be
|
||||
// equal to min_write_buffer_number_to_merge. So by asserting that
|
||||
// the first SST file creation comes from one output memtable
|
||||
// from a previous mempurge, and one newly sealed memtable. This
|
||||
// is the scenario where we observed that some SST files created
|
||||
// were not properly added to the DB version before our bug fix.
|
||||
ASSERT_GE(num_memtable_at_first_flush.load(), 2);
|
||||
|
||||
// Check that no data was lost after SST file creation.
|
||||
for (uint64_t k = 0; k < values.size(); k++) {
|
||||
ASSERT_EQ(Get(KEYS[k]), values[k]);
|
||||
}
|
||||
// Extra check of database consistency.
|
||||
ASSERT_EQ(Get(key), value);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBFlushDirectIOTest, DirectIO) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
@@ -1709,9 +1575,6 @@ 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();
|
||||
|
||||
+20
-31
@@ -119,16 +119,18 @@ 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 &&
|
||||
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 {
|
||||
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 mutable_cf_options.compression_per_level[0];
|
||||
return ioptions.compression_per_level[0];
|
||||
} else {
|
||||
return mutable_cf_options.compression;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,15 +377,12 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
s = AtomicFlushMemTables(cfds, flush_opts, context.flush_reason);
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
s = FlushMemTable(cfd, flush_opts, context.flush_reason);
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -495,18 +494,14 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
s.PermitUncheckedError(); //**TODO: What to do on error?
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (!cfd->IsDropped() && cfd->initialized() && !cfd->mem()->IsEmpty()) {
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
Status s = FlushMemTable(cfd, FlushOptions(), FlushReason::kShutDown);
|
||||
s.PermitUncheckedError(); //**TODO: What to do on error?
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
}
|
||||
|
||||
shutting_down_.store(true, std::memory_order_release);
|
||||
@@ -969,18 +964,13 @@ void DBImpl::DumpStats() {
|
||||
TEST_SYNC_POINT("DBImpl::DumpStats:StartRunning");
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->initialized()) {
|
||||
// Release DB mutex for gathering cache entry stats. Pass over all
|
||||
// column families for this first so that other stats are dumped
|
||||
// near-atomically.
|
||||
// Get a ref before unlocking
|
||||
cfd->Ref();
|
||||
{
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
cfd->internal_stats()->CollectCacheEntryStats(/*foreground=*/false);
|
||||
}
|
||||
cfd->UnrefAndTryDelete();
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
cfd->internal_stats()->CollectCacheEntryStats(/*foreground=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3490,15 +3480,13 @@ bool DBImpl::GetAggregatedIntProperty(const Slice& property,
|
||||
// Needs mutex to protect the list of column families.
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
uint64_t value;
|
||||
for (auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto* cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (!cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
ret = GetIntPropertyInternal(cfd, *property_info, true, &value);
|
||||
// GetIntPropertyInternal may release db mutex and re-acquire it.
|
||||
mutex_.AssertHeld();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (ret) {
|
||||
sum += value;
|
||||
} else {
|
||||
@@ -5089,6 +5077,7 @@ Status DBImpl::VerifyChecksumInternal(const ReadOptions& read_options,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: simplify using GetRefedColumnFamilySet?
|
||||
std::vector<ColumnFamilyData*> cfd_list;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
@@ -663,8 +663,7 @@ class DBImpl : public DB {
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const Slice* begin, const Slice* end,
|
||||
bool exclusive, bool disallow_trivial_move,
|
||||
uint64_t max_file_num_to_ignore,
|
||||
const std::string& trim_ts);
|
||||
uint64_t max_file_num_to_ignore);
|
||||
|
||||
// Return an internal iterator over the current state of the database.
|
||||
// The keys of this iterator are internal keys (see format.h).
|
||||
@@ -1248,8 +1247,7 @@ class DBImpl : public DB {
|
||||
|
||||
Status CompactRangeInternal(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end,
|
||||
const std::string& trim_ts);
|
||||
const Slice* begin, const Slice* end);
|
||||
|
||||
// The following two functions can only be called when:
|
||||
// 1. WriteThread::Writer::EnterUnbatched() is used.
|
||||
@@ -1783,12 +1781,8 @@ 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,
|
||||
Env::IOPriority rate_limiter_priority,
|
||||
bool with_db_mutex = false, bool with_log_mutex = false);
|
||||
|
||||
IOStatus WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
|
||||
@@ -263,6 +263,11 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
if (!s.ok() && need_cancel) {
|
||||
flush_job.Cancel();
|
||||
}
|
||||
IOStatus io_s = IOStatus::OK();
|
||||
io_s = flush_job.io_status();
|
||||
if (s.ok()) {
|
||||
s = io_s;
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, superversion_context,
|
||||
@@ -298,7 +303,9 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
}
|
||||
|
||||
if (!s.ok() && !s.IsShutdownInProgress() && !s.IsColumnFamilyDropped()) {
|
||||
if (log_io_s.ok()) {
|
||||
if (!io_s.ok() && !io_s.IsShutdownInProgress() &&
|
||||
!io_s.IsColumnFamilyDropped()) {
|
||||
assert(log_io_s.ok());
|
||||
// Error while writing to MANIFEST.
|
||||
// In fact, versions_->io_status() can also be the result of renaming
|
||||
// CURRENT file. With current code, it's just difficult to tell. So just
|
||||
@@ -309,19 +316,24 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// error), all the Manifest write will be map to soft error.
|
||||
// TODO: kManifestWriteNoWAL and kFlushNoWAL are misleading. Refactor is
|
||||
// needed.
|
||||
error_handler_.SetBGError(s,
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWriteNoWAL);
|
||||
} else {
|
||||
// If WAL sync is successful (either WAL size is 0 or there is no IO
|
||||
// error), all the other SST file write errors will be set as
|
||||
// kFlushNoWAL.
|
||||
error_handler_.SetBGError(s, BackgroundErrorReason::kFlushNoWAL);
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
} else {
|
||||
assert(s == log_io_s);
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
if (log_io_s.ok()) {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If we got here, then we decided not to care about the i_os status (either
|
||||
// from never needing it or ignoring the flush job status
|
||||
io_s.PermitUncheckedError();
|
||||
}
|
||||
// If flush ran smoothly and no mempurge happened
|
||||
// install new SST file path.
|
||||
@@ -370,14 +382,13 @@ Status DBImpl::FlushMemTablesToOutputFiles(
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
const auto& bg_flush_arg = bg_flush_args[0];
|
||||
ColumnFamilyData* cfd = bg_flush_arg.cfd_;
|
||||
// intentional infrequent copy for each flush
|
||||
MutableCFOptions mutable_cf_options_copy = *cfd->GetLatestMutableCFOptions();
|
||||
MutableCFOptions mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
SuperVersionContext* superversion_context =
|
||||
bg_flush_arg.superversion_context_;
|
||||
Status s = FlushMemTableToOutputFile(
|
||||
cfd, mutable_cf_options_copy, made_progress, job_context,
|
||||
superversion_context, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, log_buffer, thread_pri);
|
||||
cfd, mutable_cf_options, made_progress, job_context, superversion_context,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
log_buffer, thread_pri);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -404,7 +415,6 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
for (const auto cfd : cfds) {
|
||||
assert(cfd->imm()->NumNotFlushed() != 0);
|
||||
assert(cfd->imm()->IsFlushPending());
|
||||
assert(cfd->GetFlushReason() == cfds[0]->GetFlushReason());
|
||||
}
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
@@ -491,10 +501,12 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// exec_status stores the execution status of flush_jobs as
|
||||
// <bool /* executed */, Status /* status code */>
|
||||
autovector<std::pair<bool, Status>> exec_status;
|
||||
autovector<IOStatus> io_status;
|
||||
std::vector<bool> pick_status;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
// Initially all jobs are not executed, with status OK.
|
||||
exec_status.emplace_back(false, Status::OK());
|
||||
io_status.emplace_back(IOStatus::OK());
|
||||
pick_status.push_back(false);
|
||||
}
|
||||
|
||||
@@ -514,6 +526,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
jobs[i]->Run(&logs_with_prep_tracker_, &file_meta[i],
|
||||
&(switched_to_mempurge.at(i)));
|
||||
exec_status[i].first = true;
|
||||
io_status[i] = jobs[i]->io_status();
|
||||
}
|
||||
if (num_cfs > 1) {
|
||||
TEST_SYNC_POINT(
|
||||
@@ -527,6 +540,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
&logs_with_prep_tracker_, file_meta.data() /* &file_meta[0] */,
|
||||
switched_to_mempurge.empty() ? nullptr : &(switched_to_mempurge.at(0)));
|
||||
exec_status[0].first = true;
|
||||
io_status[0] = jobs[0]->io_status();
|
||||
|
||||
Status error_status;
|
||||
for (const auto& e : exec_status) {
|
||||
@@ -545,6 +559,21 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
s = error_status.ok() ? s : error_status;
|
||||
}
|
||||
|
||||
IOStatus io_s = IOStatus::OK();
|
||||
if (io_s.ok()) {
|
||||
IOStatus io_error = IOStatus::OK();
|
||||
for (int i = 0; i != static_cast<int>(io_status.size()); i++) {
|
||||
if (!io_status[i].ok() && !io_status[i].IsShutdownInProgress() &&
|
||||
!io_status[i].IsColumnFamilyDropped()) {
|
||||
io_error = io_status[i];
|
||||
}
|
||||
}
|
||||
io_s = io_error;
|
||||
if (s.ok() && !io_s.ok()) {
|
||||
s = io_s;
|
||||
}
|
||||
}
|
||||
|
||||
if (s.IsColumnFamilyDropped()) {
|
||||
s = Status::OK();
|
||||
}
|
||||
@@ -617,10 +646,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
return std::make_pair(Status::OK(), !ready);
|
||||
};
|
||||
|
||||
bool resuming_from_bg_err =
|
||||
error_handler_.IsDBStopped() ||
|
||||
(cfds[0]->GetFlushReason() == FlushReason::kErrorRecovery ||
|
||||
cfds[0]->GetFlushReason() == FlushReason::kErrorRecoveryRetryFlush);
|
||||
bool resuming_from_bg_err = error_handler_.IsDBStopped();
|
||||
while ((!resuming_from_bg_err || error_handler_.GetRecoveryError().ok())) {
|
||||
std::pair<Status, bool> res = wait_to_install_func();
|
||||
|
||||
@@ -635,10 +661,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
}
|
||||
atomic_flush_install_cv_.Wait();
|
||||
|
||||
resuming_from_bg_err =
|
||||
error_handler_.IsDBStopped() ||
|
||||
(cfds[0]->GetFlushReason() == FlushReason::kErrorRecovery ||
|
||||
cfds[0]->GetFlushReason() == FlushReason::kErrorRecoveryRetryFlush);
|
||||
resuming_from_bg_err = error_handler_.IsDBStopped();
|
||||
}
|
||||
|
||||
if (!resuming_from_bg_err) {
|
||||
@@ -762,7 +785,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// Need to undo atomic flush if something went wrong, i.e. s is not OK and
|
||||
// it is not because of CF drop.
|
||||
if (!s.ok() && !s.IsColumnFamilyDropped()) {
|
||||
if (log_io_s.ok()) {
|
||||
if (!io_s.ok() && !io_s.IsColumnFamilyDropped()) {
|
||||
assert(log_io_s.ok());
|
||||
// Error while writing to MANIFEST.
|
||||
// In fact, versions_->io_status() can also be the result of renaming
|
||||
// CURRENT file. With current code, it's just difficult to tell. So just
|
||||
@@ -773,18 +797,19 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// error), all the Manifest write will be map to soft error.
|
||||
// TODO: kManifestWriteNoWAL and kFlushNoWAL are misleading. Refactor
|
||||
// is needed.
|
||||
error_handler_.SetBGError(s,
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWriteNoWAL);
|
||||
} else {
|
||||
// If WAL sync is successful (either WAL size is 0 or there is no IO
|
||||
// error), all the other SST file write errors will be set as
|
||||
// kFlushNoWAL.
|
||||
error_handler_.SetBGError(s, BackgroundErrorReason::kFlushNoWAL);
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
} else {
|
||||
assert(s == log_io_s);
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
if (log_io_s.ok()) {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -901,7 +926,7 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
size_t ts_sz = ucmp->timestamp_size();
|
||||
if (ts_sz == 0) {
|
||||
return CompactRangeInternal(options, column_family, begin_without_ts,
|
||||
end_without_ts, "" /*trim_ts*/);
|
||||
end_without_ts);
|
||||
}
|
||||
|
||||
std::string begin_str;
|
||||
@@ -923,7 +948,7 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
Slice* end_with_ts = end_without_ts ? &end : nullptr;
|
||||
|
||||
return CompactRangeInternal(options, column_family, begin_with_ts,
|
||||
end_with_ts, "" /*trim_ts*/);
|
||||
end_with_ts);
|
||||
}
|
||||
|
||||
Status DBImpl::IncreaseFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
@@ -969,8 +994,7 @@ Status DBImpl::IncreaseFullHistoryTsLowImpl(ColumnFamilyData* cfd,
|
||||
|
||||
Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end,
|
||||
const std::string& trim_ts) {
|
||||
const Slice* begin, const Slice* end) {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
|
||||
@@ -1041,7 +1065,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
}
|
||||
s = RunManualCompaction(cfd, ColumnFamilyData::kCompactAllLevels,
|
||||
final_output_level, options, begin, end, exclusive,
|
||||
false, port::kMaxUint64, trim_ts);
|
||||
false, port::kMaxUint64);
|
||||
} else {
|
||||
int first_overlapped_level = kInvalidLevel;
|
||||
int max_overlapped_level = kInvalidLevel;
|
||||
@@ -1127,13 +1151,9 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
disallow_trivial_move = true;
|
||||
}
|
||||
}
|
||||
// trim_ts need real compaction to remove latest record
|
||||
if (!trim_ts.empty()) {
|
||||
disallow_trivial_move = true;
|
||||
}
|
||||
s = RunManualCompaction(cfd, level, output_level, options, begin, end,
|
||||
exclusive, disallow_trivial_move,
|
||||
max_file_num_to_ignore, trim_ts);
|
||||
max_file_num_to_ignore);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -1372,8 +1392,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, Env::Priority::USER, io_tracer_,
|
||||
&manual_compaction_paused_, nullptr, db_id_, db_session_id_,
|
||||
c->column_family_data()->GetFullHistoryTsLow(), c->trim_ts(),
|
||||
&blob_callback_);
|
||||
c->column_family_data()->GetFullHistoryTsLow(), &blob_callback_);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
@@ -1762,7 +1781,7 @@ Status DBImpl::RunManualCompaction(
|
||||
ColumnFamilyData* cfd, int input_level, int output_level,
|
||||
const CompactRangeOptions& compact_range_options, const Slice* begin,
|
||||
const Slice* end, bool exclusive, bool disallow_trivial_move,
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts) {
|
||||
uint64_t max_file_num_to_ignore) {
|
||||
assert(input_level == ColumnFamilyData::kCompactAllLevels ||
|
||||
input_level >= 0);
|
||||
|
||||
@@ -1874,7 +1893,7 @@ Status DBImpl::RunManualCompaction(
|
||||
*manual.cfd->GetLatestMutableCFOptions(), mutable_db_options_,
|
||||
manual.input_level, manual.output_level, compact_range_options,
|
||||
manual.begin, manual.end, &manual.manual_end, &manual_conflict,
|
||||
max_file_num_to_ignore, trim_ts)) == nullptr &&
|
||||
max_file_num_to_ignore)) == nullptr &&
|
||||
manual_conflict))) {
|
||||
// exclusive manual compactions should not see a conflict during
|
||||
// CompactRange
|
||||
@@ -2973,8 +2992,6 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
bg_bottom_compaction_scheduled_--;
|
||||
}
|
||||
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
|
||||
// See if there's more work to be done
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
|
||||
@@ -3371,7 +3388,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
is_manual ? &manual_compaction_paused_ : nullptr,
|
||||
is_manual ? manual_compaction->canceled : nullptr, db_id_,
|
||||
db_session_id_, c->column_family_data()->GetFullHistoryTsLow(),
|
||||
c->trim_ts(), &blob_callback_);
|
||||
&blob_callback_);
|
||||
compaction_job.Prepare();
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
|
||||
@@ -120,8 +120,7 @@ Status DBImpl::TEST_CompactRange(int level, const Slice* begin,
|
||||
: level + 1;
|
||||
return RunManualCompaction(cfd, level, output_level, CompactRangeOptions(),
|
||||
begin, end, true, disallow_trivial_move,
|
||||
port::kMaxUint64 /*max_file_num_to_ignore*/,
|
||||
"" /*trim_ts*/);
|
||||
port::kMaxUint64 /*max_file_num_to_ignore*/);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_SwitchMemtable(ColumnFamilyData* cfd) {
|
||||
|
||||
@@ -23,11 +23,7 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
uint64_t DBImpl::MinLogNumberToKeep() {
|
||||
if (allow_2pc()) {
|
||||
return versions_->min_log_number_to_keep_2pc();
|
||||
} else {
|
||||
return versions_->MinLogNumberWithUnflushedData();
|
||||
}
|
||||
return versions_->min_log_number_to_keep();
|
||||
}
|
||||
|
||||
uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
|
||||
@@ -224,7 +220,6 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
}
|
||||
|
||||
// Add log files in wal_dir
|
||||
|
||||
if (!immutable_db_options_.IsWalDirSameAsDBPath(dbname_)) {
|
||||
std::vector<std::string> log_files;
|
||||
Status s = env_->GetChildren(immutable_db_options_.wal_dir, &log_files);
|
||||
@@ -234,6 +229,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
log_file, immutable_db_options_.wal_dir);
|
||||
}
|
||||
}
|
||||
|
||||
// Add info log files in db_log_dir
|
||||
if (!immutable_db_options_.db_log_dir.empty() &&
|
||||
immutable_db_options_.db_log_dir != dbname_) {
|
||||
|
||||
+23
-70
@@ -866,6 +866,11 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
bool flushed = false;
|
||||
uint64_t corrupted_wal_number = kMaxSequenceNumber;
|
||||
uint64_t min_wal_number = MinLogNumberToKeep();
|
||||
if (!allow_2pc()) {
|
||||
// In non-2pc mode, we skip WALs that do not back unflushed data.
|
||||
min_wal_number =
|
||||
std::max(min_wal_number, versions_->MinLogNumberWithUnflushedData());
|
||||
}
|
||||
for (auto wal_number : wal_numbers) {
|
||||
if (wal_number < min_wal_number) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
@@ -1270,9 +1275,16 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
}
|
||||
|
||||
std::unique_ptr<VersionEdit> wal_deletion;
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest) {
|
||||
wal_deletion.reset(new VersionEdit);
|
||||
wal_deletion->DeleteWalsBefore(max_wal_number + 1);
|
||||
if (flushed) {
|
||||
wal_deletion = std::make_unique<VersionEdit>();
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest) {
|
||||
wal_deletion->DeleteWalsBefore(max_wal_number + 1);
|
||||
}
|
||||
if (!allow_2pc()) {
|
||||
// In non-2pc mode, flushing the memtables of the column families
|
||||
// means we can advance min_log_number_to_keep.
|
||||
wal_deletion->SetMinLogNumberToKeep(max_wal_number + 1);
|
||||
}
|
||||
edit_lists.back().push_back(wal_deletion.get());
|
||||
}
|
||||
|
||||
@@ -1351,7 +1363,14 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& wal_numbers) {
|
||||
// FindObsoleteFiles()
|
||||
total_log_size_ = 0;
|
||||
log_empty_ = false;
|
||||
uint64_t min_wal_with_unflushed_data =
|
||||
versions_->MinLogNumberWithUnflushedData();
|
||||
for (auto wal_number : wal_numbers) {
|
||||
if (!allow_2pc() && wal_number < min_wal_with_unflushed_data) {
|
||||
// In non-2pc mode, the WAL files not backing unflushed data are not
|
||||
// alive, thus should not be added to the alive_log_files_.
|
||||
continue;
|
||||
}
|
||||
// We preallocate space for wals, but then after a crash and restart, those
|
||||
// preallocated space are not needed anymore. It is likely only the last
|
||||
// log has such preallocated space, so we only truncate for the last log.
|
||||
@@ -1547,72 +1566,6 @@ Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
!kSeqPerBatch, kBatchPerTxn);
|
||||
}
|
||||
|
||||
// TODO: Implement the trimming in flush code path.
|
||||
// TODO: Perform trimming before inserting into memtable during recovery.
|
||||
// TODO: Pick files with max_timestamp > trim_ts by each file's timestamp meta
|
||||
// info, and handle only these files to reduce io.
|
||||
Status DB::OpenAndTrimHistory(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
std::string trim_ts) {
|
||||
assert(dbptr != nullptr);
|
||||
assert(handles != nullptr);
|
||||
auto validate_options = [&db_options] {
|
||||
if (db_options.avoid_flush_during_recovery) {
|
||||
return Status::InvalidArgument(
|
||||
"avoid_flush_during_recovery incompatible with "
|
||||
"OpenAndTrimHistory");
|
||||
}
|
||||
return Status::OK();
|
||||
};
|
||||
auto s = validate_options();
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
DB* db = nullptr;
|
||||
s = DB::Open(db_options, dbname, column_families, handles, &db);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
assert(db);
|
||||
CompactRangeOptions options;
|
||||
options.bottommost_level_compaction =
|
||||
BottommostLevelCompaction::kForceOptimized;
|
||||
auto db_impl = static_cast_with_check<DBImpl>(db);
|
||||
for (auto handle : *handles) {
|
||||
assert(handle != nullptr);
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(handle);
|
||||
auto cfd = cfh->cfd();
|
||||
assert(cfd != nullptr);
|
||||
// Only compact column families with timestamp enabled
|
||||
if (cfd->user_comparator() != nullptr &&
|
||||
cfd->user_comparator()->timestamp_size() > 0) {
|
||||
s = db_impl->CompactRangeInternal(options, handle, nullptr, nullptr,
|
||||
trim_ts);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto clean_op = [&handles, &db] {
|
||||
for (auto handle : *handles) {
|
||||
auto temp_s = db->DestroyColumnFamilyHandle(handle);
|
||||
assert(temp_s.ok());
|
||||
}
|
||||
handles->clear();
|
||||
delete db;
|
||||
};
|
||||
if (!s.ok()) {
|
||||
clean_op();
|
||||
return s;
|
||||
}
|
||||
|
||||
*dbptr = db;
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus DBImpl::CreateWAL(uint64_t log_file_num, uint64_t recycle_log_number,
|
||||
size_t preallocate_block_size,
|
||||
log::Writer** new_log) {
|
||||
@@ -1798,7 +1751,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,
|
||||
Env::IO_TOTAL, /*with_db_mutex==*/true);
|
||||
/*with_db_mutex==*/true);
|
||||
if (s.ok()) {
|
||||
// Need to fsync, otherwise it might get lost after a power reset.
|
||||
s = impl->FlushWAL(false);
|
||||
|
||||
@@ -130,31 +130,8 @@ 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 (!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.
|
||||
} else if (WriteBatchInternal::TimestampsUpdateNeeded(*my_batch)) {
|
||||
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.
|
||||
@@ -1171,7 +1148,6 @@ 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,
|
||||
Env::IOPriority rate_limiter_priority,
|
||||
bool with_db_mutex, bool with_log_mutex) {
|
||||
assert(log_size != nullptr);
|
||||
|
||||
@@ -1196,7 +1172,7 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
IOStatus io_s = log_writer->AddRecord(log_entry, rate_limiter_priority);
|
||||
IOStatus io_s = log_writer->AddRecord(log_entry);
|
||||
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Unlock();
|
||||
@@ -1238,8 +1214,7 @@ 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,
|
||||
write_group.leader->rate_limiter_priority);
|
||||
io_s = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
@@ -1335,7 +1310,6 @@ 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,
|
||||
write_group.leader->rate_limiter_priority,
|
||||
/*with_db_mutex=*/false, /*with_log_mutex=*/true);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
|
||||
@@ -167,14 +167,9 @@ TEST_F(DBLogicalBlockSizeCacheTest, CreateColumnFamily) {
|
||||
}
|
||||
|
||||
TEST_F(DBLogicalBlockSizeCacheTest, CreateColumnFamilies) {
|
||||
// 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
|
||||
|
||||
// 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.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_.get();
|
||||
@@ -195,7 +190,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 cf_path_0_'s entry from cache
|
||||
// Drop column family does not drop cache.
|
||||
for (ColumnFamilyHandle* cf : cfs) {
|
||||
ASSERT_OK(db->DropColumnFamily(cf));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
@@ -205,24 +200,22 @@ TEST_F(DBLogicalBlockSizeCacheTest, CreateColumnFamilies) {
|
||||
ASSERT_EQ(2, cache_->GetRefCount(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_.
|
||||
// Delete one handle will not drop 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 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
|
||||
// Delete the last handle will drop cache.
|
||||
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
|
||||
delete db;
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
ASSERT_OK(DestroyDB(dbname_, options,
|
||||
{{"cf1", cf_options}, {"cf2", cf_options}}));
|
||||
|
||||
@@ -1067,17 +1067,11 @@ 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 = 3;
|
||||
options.num_levels = 2;
|
||||
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);
|
||||
|
||||
|
||||
@@ -500,7 +500,7 @@ TEST_F(DBRangeDelTest, ValidUniversalSubcompactionBoundaries) {
|
||||
1 /* input_level */, 2 /* output_level */, CompactRangeOptions(),
|
||||
nullptr /* begin */, nullptr /* end */, true /* exclusive */,
|
||||
true /* disallow_trivial_move */,
|
||||
port::kMaxUint64 /* max_file_num_to_ignore */, "" /*trim_ts*/));
|
||||
port::kMaxUint64 /* max_file_num_to_ignore */));
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
|
||||
+11
-195
@@ -3,26 +3,18 @@
|
||||
// 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 DBRateLimiterOnReadTest
|
||||
class DBRateLimiterTest
|
||||
: public DBTestBase,
|
||||
public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> {
|
||||
public:
|
||||
explicit DBRateLimiterOnReadTest()
|
||||
: DBTestBase("db_rate_limiter_on_read_test", /*env_do_fsync=*/false),
|
||||
DBRateLimiterTest()
|
||||
: DBTestBase("db_rate_limiter_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())) {}
|
||||
@@ -97,20 +89,20 @@ std::string GetTestNameSuffix(
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
INSTANTIATE_TEST_CASE_P(DBRateLimiterOnReadTest, DBRateLimiterOnReadTest,
|
||||
INSTANTIATE_TEST_CASE_P(DBRateLimiterTest, DBRateLimiterTest,
|
||||
::testing::Combine(::testing::Bool(), ::testing::Bool(),
|
||||
::testing::Bool()),
|
||||
GetTestNameSuffix);
|
||||
#else // ROCKSDB_LITE
|
||||
// Cannot use direct I/O in lite mode.
|
||||
INSTANTIATE_TEST_CASE_P(DBRateLimiterOnReadTest, DBRateLimiterOnReadTest,
|
||||
INSTANTIATE_TEST_CASE_P(DBRateLimiterTest, DBRateLimiterTest,
|
||||
::testing::Combine(::testing::Values(false),
|
||||
::testing::Bool(),
|
||||
::testing::Bool()),
|
||||
GetTestNameSuffix);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_P(DBRateLimiterOnReadTest, Get) {
|
||||
TEST_P(DBRateLimiterTest, Get) {
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
return;
|
||||
}
|
||||
@@ -138,7 +130,7 @@ TEST_P(DBRateLimiterOnReadTest, Get) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBRateLimiterOnReadTest, NewMultiGet) {
|
||||
TEST_P(DBRateLimiterTest, NewMultiGet) {
|
||||
// The new void-returning `MultiGet()` APIs use `MultiRead()`, which does not
|
||||
// yet support rate limiting.
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
@@ -169,7 +161,7 @@ TEST_P(DBRateLimiterOnReadTest, NewMultiGet) {
|
||||
ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
}
|
||||
|
||||
TEST_P(DBRateLimiterOnReadTest, OldMultiGet) {
|
||||
TEST_P(DBRateLimiterTest, OldMultiGet) {
|
||||
// The old `vector<Status>`-returning `MultiGet()` APIs use `Read()`, which
|
||||
// supports rate limiting.
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
@@ -201,7 +193,7 @@ TEST_P(DBRateLimiterOnReadTest, OldMultiGet) {
|
||||
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
}
|
||||
|
||||
TEST_P(DBRateLimiterOnReadTest, Iterator) {
|
||||
TEST_P(DBRateLimiterTest, Iterator) {
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
return;
|
||||
}
|
||||
@@ -231,7 +223,7 @@ TEST_P(DBRateLimiterOnReadTest, Iterator) {
|
||||
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
|
||||
TEST_P(DBRateLimiterOnReadTest, VerifyChecksum) {
|
||||
TEST_P(DBRateLimiterTest, VerifyChecksum) {
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
return;
|
||||
}
|
||||
@@ -245,7 +237,7 @@ TEST_P(DBRateLimiterOnReadTest, VerifyChecksum) {
|
||||
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
}
|
||||
|
||||
TEST_P(DBRateLimiterOnReadTest, VerifyFileChecksums) {
|
||||
TEST_P(DBRateLimiterTest, VerifyFileChecksums) {
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
return;
|
||||
}
|
||||
@@ -261,182 +253,6 @@ TEST_P(DBRateLimiterOnReadTest, 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) {
|
||||
|
||||
+125
-130
@@ -17,11 +17,11 @@
|
||||
#include "options/options_helper.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/iostats_context.h"
|
||||
#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,9 +410,6 @@ 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
|
||||
@@ -591,11 +588,6 @@ 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
|
||||
@@ -6909,18 +6901,122 @@ TEST_F(DBTest2, LastLevelStatistics) {
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_COUNT));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, CheckpointFileTemperature) {
|
||||
class NoLinkTestFS : public FileTemperatureTestFS {
|
||||
using FileTemperatureTestFS::FileTemperatureTestFS;
|
||||
class FileTemperatureTestFS : public FileSystemWrapper {
|
||||
public:
|
||||
explicit FileTemperatureTestFS(SpecialEnv* env)
|
||||
: FileSystemWrapper(env->GetFileSystem()) {}
|
||||
|
||||
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
|
||||
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) {
|
||||
return IOStatus::NotSupported();
|
||||
}
|
||||
};
|
||||
auto test_fs = std::make_shared<NoLinkTestFS>(env_->GetFileSystem());
|
||||
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_);
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, test_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.bottommost_temperature = Temperature::kWarm;
|
||||
@@ -6950,7 +7046,7 @@ TEST_F(DBTest2, CheckpointFileTemperature) {
|
||||
temperatures.emplace(info.file_number, info.temperature);
|
||||
}
|
||||
|
||||
test_fs->PopRequestedSstFileTemperatures();
|
||||
test_fs->ClearRequestedFileTemperatures();
|
||||
Checkpoint* checkpoint;
|
||||
ASSERT_OK(Checkpoint::Create(db_, &checkpoint));
|
||||
ASSERT_OK(
|
||||
@@ -6958,121 +7054,20 @@ TEST_F(DBTest2, CheckpointFileTemperature) {
|
||||
|
||||
// checking src file src_temperature hints: 2 sst files: 1 sst is kWarm,
|
||||
// another is kUnknown
|
||||
std::vector<std::pair<uint64_t, Temperature>> requested_temps;
|
||||
test_fs->PopRequestedSstFileTemperatures(&requested_temps);
|
||||
// Two requests
|
||||
ASSERT_EQ(requested_temps.size(), 2);
|
||||
std::set<uint64_t> distinct_requests;
|
||||
for (const auto& requested_temp : requested_temps) {
|
||||
// Matching manifest temperatures
|
||||
ASSERT_EQ(temperatures.at(requested_temp.first), requested_temp.second);
|
||||
distinct_requests.insert(requested_temp.first);
|
||||
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;
|
||||
}
|
||||
}
|
||||
// Each request to distinct file
|
||||
ASSERT_EQ(distinct_requests.size(), requested_temps.size());
|
||||
|
||||
ASSERT_TRUE(has_only_one_warm_sst);
|
||||
delete checkpoint;
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, FileTemperatureManifestFixup) {
|
||||
auto test_fs = std::make_shared<FileTemperatureTestFS>(env_->GetFileSystem());
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, test_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.bottommost_temperature = Temperature::kWarm;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.env = env.get();
|
||||
std::vector<std::string> cfs = {/*"default",*/ "test1", "test2"};
|
||||
CreateAndReopenWithCF(cfs, options);
|
||||
// Needed for later re-opens (weird)
|
||||
cfs.insert(cfs.begin(), kDefaultColumnFamilyName);
|
||||
|
||||
// Generate a bottommost file in all CFs
|
||||
for (int cf = 0; cf < 3; ++cf) {
|
||||
ASSERT_OK(Put(cf, "a", "val"));
|
||||
ASSERT_OK(Put(cf, "c", "val"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
ASSERT_OK(Put(cf, "b", "val"));
|
||||
ASSERT_OK(Put(cf, "d", "val"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// verify
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kWarm), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kHot), 0);
|
||||
|
||||
// Generate a non-bottommost file in all CFs
|
||||
for (int cf = 0; cf < 3; ++cf) {
|
||||
ASSERT_OK(Put(cf, "e", "val"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
}
|
||||
|
||||
// re-verify
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kWarm), 0);
|
||||
// Not supported: ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kHot), 0);
|
||||
|
||||
// Now change FS temperature on bottommost file(s) to kCold
|
||||
std::map<uint64_t, Temperature> current_temps;
|
||||
test_fs->CopyCurrentSstFileTemperatures(¤t_temps);
|
||||
for (auto e : current_temps) {
|
||||
if (e.second == Temperature::kWarm) {
|
||||
test_fs->OverrideSstFileTemperature(e.first, Temperature::kCold);
|
||||
}
|
||||
}
|
||||
// Metadata not yet updated
|
||||
ASSERT_EQ(Get("a"), "val");
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
|
||||
// Update with Close and UpdateManifestForFilesState, but first save cf
|
||||
// descriptors
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (size_t i = 0; i < handles_.size(); ++i) {
|
||||
ColumnFamilyDescriptor cfdescriptor;
|
||||
// GetDescriptor is not implemented for ROCKSDB_LITE
|
||||
handles_[i]->GetDescriptor(&cfdescriptor).PermitUncheckedError();
|
||||
column_families.push_back(cfdescriptor);
|
||||
}
|
||||
Close();
|
||||
experimental::UpdateManifestForFilesStateOptions update_opts;
|
||||
update_opts.update_temperatures = true;
|
||||
|
||||
ASSERT_OK(experimental::UpdateManifestForFilesState(
|
||||
options, dbname_, column_families, update_opts));
|
||||
|
||||
// Re-open and re-verify after update
|
||||
ReopenWithColumnFamilies(cfs, options);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
// Not supported: ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kWarm), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kHot), 0);
|
||||
|
||||
// Change kUnknown to kHot
|
||||
test_fs->CopyCurrentSstFileTemperatures(¤t_temps);
|
||||
for (auto e : current_temps) {
|
||||
if (e.second == Temperature::kUnknown) {
|
||||
test_fs->OverrideSstFileTemperature(e.first, Temperature::kHot);
|
||||
}
|
||||
}
|
||||
|
||||
// Update with Close and UpdateManifestForFilesState
|
||||
Close();
|
||||
ASSERT_OK(experimental::UpdateManifestForFilesState(
|
||||
options, dbname_, column_families, update_opts));
|
||||
|
||||
// Re-open and re-verify after update
|
||||
ReopenWithColumnFamilies(cfs, options);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kWarm), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kHot), 0);
|
||||
|
||||
Close();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// WAL recovery mode is WALRecoveryMode::kPointInTimeRecovery.
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -24,15 +23,12 @@
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "file/filename.h"
|
||||
#include "rocksdb/advanced_options.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/io_status.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
@@ -691,121 +687,6 @@ 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 {
|
||||
IOStatus s = target()->NewSequentialFile(fname, opts, result, dbg);
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(GetFileName(fname), &number, &type) &&
|
||||
type == kTableFile) {
|
||||
MutexLock lock(&mu_);
|
||||
requested_sst_file_temperatures_.emplace_back(number, opts.temperature);
|
||||
if (s.ok()) {
|
||||
*result = WrapWithTemperature<FSSequentialFileOwnerWrapper>(
|
||||
number, std::move(*result));
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus NewRandomAccessFile(const std::string& fname,
|
||||
const FileOptions& opts,
|
||||
std::unique_ptr<FSRandomAccessFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
IOStatus s = target()->NewRandomAccessFile(fname, opts, result, dbg);
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(GetFileName(fname), &number, &type) &&
|
||||
type == kTableFile) {
|
||||
MutexLock lock(&mu_);
|
||||
requested_sst_file_temperatures_.emplace_back(number, opts.temperature);
|
||||
if (s.ok()) {
|
||||
*result = WrapWithTemperature<FSRandomAccessFileOwnerWrapper>(
|
||||
number, std::move(*result));
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void PopRequestedSstFileTemperatures(
|
||||
std::vector<std::pair<uint64_t, Temperature>>* out = nullptr) {
|
||||
MutexLock lock(&mu_);
|
||||
if (out) {
|
||||
*out = std::move(requested_sst_file_temperatures_);
|
||||
assert(requested_sst_file_temperatures_.empty());
|
||||
} else {
|
||||
requested_sst_file_temperatures_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
IOStatus NewWritableFile(const std::string& fname, const FileOptions& opts,
|
||||
std::unique_ptr<FSWritableFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(GetFileName(fname), &number, &type) &&
|
||||
type == kTableFile) {
|
||||
MutexLock lock(&mu_);
|
||||
current_sst_file_temperatures_[number] = opts.temperature;
|
||||
}
|
||||
return target()->NewWritableFile(fname, opts, result, dbg);
|
||||
}
|
||||
|
||||
void CopyCurrentSstFileTemperatures(std::map<uint64_t, Temperature>* out) {
|
||||
MutexLock lock(&mu_);
|
||||
*out = current_sst_file_temperatures_;
|
||||
}
|
||||
|
||||
void OverrideSstFileTemperature(uint64_t number, Temperature temp) {
|
||||
MutexLock lock(&mu_);
|
||||
current_sst_file_temperatures_[number] = temp;
|
||||
}
|
||||
|
||||
protected:
|
||||
port::Mutex mu_;
|
||||
std::vector<std::pair<uint64_t, Temperature>>
|
||||
requested_sst_file_temperatures_;
|
||||
std::map<uint64_t, Temperature> current_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;
|
||||
}
|
||||
|
||||
template <class FileOwnerWrapperT, /*inferred*/ class FileT>
|
||||
std::unique_ptr<FileT> WrapWithTemperature(uint64_t number,
|
||||
std::unique_ptr<FileT>&& t) {
|
||||
class FileWithTemp : public FileOwnerWrapperT {
|
||||
public:
|
||||
FileWithTemp(FileTemperatureTestFS* fs, uint64_t number,
|
||||
std::unique_ptr<FileT>&& t)
|
||||
: FileOwnerWrapperT(std::move(t)), fs_(fs), number_(number) {}
|
||||
|
||||
Temperature GetTemperature() const override {
|
||||
MutexLock lock(&fs_->mu_);
|
||||
return fs_->current_sst_file_temperatures_[number_];
|
||||
}
|
||||
|
||||
private:
|
||||
FileTemperatureTestFS* fs_;
|
||||
uint64_t number_;
|
||||
};
|
||||
return std::make_unique<FileWithTemp>(this, number, std::move(t));
|
||||
}
|
||||
};
|
||||
|
||||
class OnFileDeletionListener : public EventListener {
|
||||
public:
|
||||
OnFileDeletionListener() : matched_count_(0), expected_file_name_("") {}
|
||||
|
||||
@@ -1491,6 +1491,93 @@ TEST_F(DBWALTest, kPointInTimeRecoveryCFConsistency) {
|
||||
ASSERT_NOK(TryReopenWithColumnFamilies({"default", "one", "two"}, options));
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RaceInstallFlushResultsWithWalObsoletion) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
// The following make sure there are two bg flush threads.
|
||||
options.max_background_jobs = 8;
|
||||
|
||||
const std::string cf1_name("cf1");
|
||||
CreateAndReopenWithCF({cf1_name}, options);
|
||||
assert(handles_.size() == 2);
|
||||
|
||||
{
|
||||
dbfull()->TEST_LockMutex();
|
||||
ASSERT_LE(2, dbfull()->GetBGJobLimits().max_flushes);
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->PauseBackgroundWork());
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles_[1], "foo", "value"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "value"));
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable(false, true, handles_[1]));
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "value"));
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable(false, true, handles_[0]));
|
||||
|
||||
bool called = false;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
// This callback will be called when the first bg flush thread reaches the
|
||||
// point before entering the MANIFEST write queue after flushing the SST
|
||||
// file.
|
||||
// The purpose of the sync points here is to ensure both bg flush threads
|
||||
// finish computing `min_wal_number_to_keep` before any of them updates the
|
||||
// `log_number` for the column family that's being flushed.
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"MemTableList::TryInstallMemtableFlushResults:AfterComputeMinWalToKeep",
|
||||
[&](void* /*arg*/) {
|
||||
dbfull()->mutex()->AssertHeld();
|
||||
if (!called) {
|
||||
// We are the first bg flush thread in the MANIFEST write queue.
|
||||
// We set up the dependency between sync points for two threads that
|
||||
// will be executing the same code.
|
||||
// For the interleaving of events, see
|
||||
// https://github.com/facebook/rocksdb/pull/9715.
|
||||
// bg flush thread1 will release the db mutex while in the MANIFEST
|
||||
// write queue. In the meantime, bg flush thread2 locks db mutex and
|
||||
// computes the min_wal_number_to_keep (before thread1 writes to
|
||||
// MANIFEST thus before cf1->log_number is updated). Bg thread2 joins
|
||||
// the MANIFEST write queue afterwards and bg flush thread1 proceeds
|
||||
// with writing to MANIFEST.
|
||||
called = true;
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"VersionSet::LogAndApply:WriteManifestStart",
|
||||
"DBWALTest::RaceInstallFlushResultsWithWalObsoletion:BgFlush2"},
|
||||
{"DBWALTest::RaceInstallFlushResultsWithWalObsoletion:BgFlush2",
|
||||
"VersionSet::LogAndApply:WriteManifest"},
|
||||
});
|
||||
} else {
|
||||
// The other bg flush thread has already been in the MANIFEST write
|
||||
// queue, and we are after.
|
||||
TEST_SYNC_POINT(
|
||||
"DBWALTest::RaceInstallFlushResultsWithWalObsoletion:BgFlush2");
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(dbfull()->ContinueBackgroundWork());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[0]));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[1]));
|
||||
|
||||
ASSERT_TRUE(called);
|
||||
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
DB* db1 = nullptr;
|
||||
Status s = DB::OpenForReadOnly(options, dbname_, &db1);
|
||||
ASSERT_OK(s);
|
||||
assert(db1);
|
||||
delete db1;
|
||||
}
|
||||
|
||||
// Test scope:
|
||||
// - We expect to open data store under all circumstances
|
||||
// - We expect only data upto the point where the first error was encountered
|
||||
|
||||
@@ -704,77 +704,6 @@ TEST_F(DBBasicTestWithTimestamp, SimpleIterate) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, TrimHistoryTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
auto check_value_by_ts = [](DB* db, Slice key, std::string readTs,
|
||||
Status status, std::string checkValue) {
|
||||
ReadOptions ropts;
|
||||
Slice ts = readTs;
|
||||
ropts.timestamp = &ts;
|
||||
std::string value;
|
||||
Status s = db->Get(ropts, key, &value);
|
||||
ASSERT_TRUE(s == status);
|
||||
if (s.ok()) {
|
||||
ASSERT_EQ(checkValue, value);
|
||||
}
|
||||
};
|
||||
// Construct data of different versions with different ts
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "k1", Timestamp(2, 0), "v1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "k1", Timestamp(4, 0), "v2"));
|
||||
ASSERT_OK(db_->Delete(WriteOptions(), "k1", Timestamp(5, 0)));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "k1", Timestamp(6, 0), "v3"));
|
||||
check_value_by_ts(db_, "k1", Timestamp(7, 0), Status::OK(), "v3");
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
DBOptions db_options(options);
|
||||
|
||||
// Trim data whose version > Timestamp(5, 0), read(k1, ts(7)) <- NOT_FOUND.
|
||||
ASSERT_OK(DB::OpenAndTrimHistory(db_options, dbname_, column_families,
|
||||
&handles_, &db_, Timestamp(5, 0)));
|
||||
check_value_by_ts(db_, "k1", Timestamp(7, 0), Status::NotFound(), "");
|
||||
Close();
|
||||
|
||||
// Trim data whose timestamp > Timestamp(4, 0), read(k1, ts(7)) <- v2
|
||||
ASSERT_OK(DB::OpenAndTrimHistory(db_options, dbname_, column_families,
|
||||
&handles_, &db_, Timestamp(4, 0)));
|
||||
check_value_by_ts(db_, "k1", Timestamp(7, 0), Status::OK(), "v2");
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, OpenAndTrimHistoryInvalidOptionTest) {
|
||||
Destroy(last_options_);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
DBOptions db_options(options);
|
||||
|
||||
// OpenAndTrimHistory should not work with avoid_flush_during_recovery
|
||||
db_options.avoid_flush_during_recovery = true;
|
||||
ASSERT_TRUE(DB::OpenAndTrimHistory(db_options, dbname_, column_families,
|
||||
&handles_, &db_, Timestamp(0, 0))
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBasicTestWithTimestamp, GetTimestampTableProperties) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
@@ -207,13 +207,6 @@ inline Slice ExtractTimestampFromUserKey(const Slice& user_key, size_t ts_sz) {
|
||||
return Slice(user_key.data() + user_key.size() - ts_sz, ts_sz);
|
||||
}
|
||||
|
||||
inline Slice ExtractTimestampFromKey(const Slice& internal_key, size_t ts_sz) {
|
||||
const size_t key_size = internal_key.size();
|
||||
assert(key_size >= kNumInternalBytes + ts_sz);
|
||||
return Slice(internal_key.data() + key_size - ts_sz - kNumInternalBytes,
|
||||
ts_sz);
|
||||
}
|
||||
|
||||
inline uint64_t ExtractInternalKeyFooter(const Slice& internal_key) {
|
||||
assert(internal_key.size() >= kNumInternalBytes);
|
||||
const size_t n = internal_key.size();
|
||||
|
||||
+4
-11
@@ -272,8 +272,8 @@ STATIC_AVOID_DESTRUCTION(const Status, kOkStatus){Status::OK()};
|
||||
// This can also get called as part of a recovery operation. In that case, we
|
||||
// also track the error separately in recovery_error_ so we can tell in the
|
||||
// end whether recovery succeeded or not
|
||||
const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
BackgroundErrorReason reason) {
|
||||
const Status& ErrorHandler::SetBGError(const Status& bg_err,
|
||||
BackgroundErrorReason reason) {
|
||||
db_mutex_->AssertHeld();
|
||||
if (bg_err.ok()) {
|
||||
return kOkStatus;
|
||||
@@ -382,12 +382,9 @@ const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
// c) all other errors are mapped to hard error.
|
||||
// 3) for other cases, SetBGError(const Status& bg_err, BackgroundErrorReason
|
||||
// reason) will be called to handle other error cases.
|
||||
const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
const Status& ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
BackgroundErrorReason reason) {
|
||||
db_mutex_->AssertHeld();
|
||||
Status tmp_status = bg_status;
|
||||
IOStatus bg_io_err = status_to_io_status(std::move(tmp_status));
|
||||
|
||||
if (bg_io_err.ok()) {
|
||||
return kOkStatus;
|
||||
}
|
||||
@@ -486,11 +483,7 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
}
|
||||
// HandleKnownErrors() will use recovery_error_, so ignore
|
||||
// recovery_io_error_.
|
||||
// TODO: Do some refactoring and use only one recovery_error_
|
||||
recovery_io_error_.PermitUncheckedError();
|
||||
return HandleKnownErrors(new_bg_io_err, reason);
|
||||
return SetBGError(new_bg_io_err, reason);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -53,6 +53,9 @@ class ErrorHandler {
|
||||
|
||||
const Status& SetBGError(const Status& bg_err, BackgroundErrorReason reason);
|
||||
|
||||
const Status& SetBGError(const IOStatus& bg_io_err,
|
||||
BackgroundErrorReason reason);
|
||||
|
||||
Status GetBGError() const { return bg_error_; }
|
||||
|
||||
Status GetRecoveryError() const { return recovery_error_; }
|
||||
@@ -109,8 +112,6 @@ class ErrorHandler {
|
||||
// The pointer of DB statistics.
|
||||
std::shared_ptr<Statistics> bg_error_stats_;
|
||||
|
||||
const Status& HandleKnownErrors(const Status& bg_err,
|
||||
BackgroundErrorReason reason);
|
||||
Status OverrideNoSpaceError(const Status& bg_error, bool* auto_recovery);
|
||||
void RecoverFromNoSpace();
|
||||
const Status& StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
|
||||
|
||||
@@ -2468,210 +2468,6 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableErrorAbortRecovery) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FlushReadError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener =
|
||||
std::make_shared<ErrorHandlerFSListener>();
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.statistics = CreateDBStatistics();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put(Key(0), "val"));
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeOutputValidation", [&](void*) {
|
||||
IOStatus st = IOStatus::IOError();
|
||||
st.SetRetryable(true);
|
||||
st.SetScope(IOStatus::IOErrorScope::kIOErrorScopeFile);
|
||||
fault_fs_->SetFilesystemActive(false, st);
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeDeleteFile",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(true, IOStatus::OK()); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
ASSERT_EQ(listener->WaitForRecovery(5000000), true);
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT));
|
||||
ASSERT_LE(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_COUNT));
|
||||
ASSERT_LE(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT));
|
||||
s = dbfull()->TEST_GetBGError();
|
||||
ASSERT_OK(s);
|
||||
|
||||
Reopen(GetDefaultOptions());
|
||||
ASSERT_EQ("val", Get(Key(0)));
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, AtomicFlushReadError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener =
|
||||
std::make_shared<ErrorHandlerFSListener>();
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.statistics = CreateDBStatistics();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
options.atomic_flush = true;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
ASSERT_OK(Put(0, Key(0), "val"));
|
||||
ASSERT_OK(Put(1, Key(0), "val"));
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeOutputValidation", [&](void*) {
|
||||
IOStatus st = IOStatus::IOError();
|
||||
st.SetRetryable(true);
|
||||
st.SetScope(IOStatus::IOErrorScope::kIOErrorScopeFile);
|
||||
fault_fs_->SetFilesystemActive(false, st);
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeDeleteFile",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(true, IOStatus::OK()); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush({0, 1});
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
ASSERT_EQ(listener->WaitForRecovery(5000000), true);
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT));
|
||||
ASSERT_LE(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_COUNT));
|
||||
ASSERT_LE(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT));
|
||||
s = dbfull()->TEST_GetBGError();
|
||||
ASSERT_OK(s);
|
||||
|
||||
TryReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"},
|
||||
GetDefaultOptions());
|
||||
ASSERT_EQ("val", Get(Key(0)));
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, AtomicFlushNoSpaceError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener =
|
||||
std::make_shared<ErrorHandlerFSListener>();
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.statistics = CreateDBStatistics();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(true);
|
||||
options.atomic_flush = true;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
ASSERT_OK(Put(0, Key(0), "val"));
|
||||
ASSERT_OK(Put(1, Key(0), "val"));
|
||||
SyncPoint::GetInstance()->SetCallBack("BuildTable:create_file", [&](void*) {
|
||||
IOStatus st = IOStatus::NoSpace();
|
||||
fault_fs_->SetFilesystemActive(false, st);
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeDeleteFile",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(true, IOStatus::OK()); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush({0, 1});
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
ASSERT_EQ(listener->WaitForRecovery(5000000), true);
|
||||
ASSERT_LE(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_ERROR_COUNT));
|
||||
ASSERT_LE(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT));
|
||||
s = dbfull()->TEST_GetBGError();
|
||||
ASSERT_OK(s);
|
||||
|
||||
TryReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"},
|
||||
GetDefaultOptions());
|
||||
ASSERT_EQ("val", Get(Key(0)));
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, CompactionReadRetryableErrorAutoRecover) {
|
||||
// In this test, in the first round of compaction, the FS is set to error.
|
||||
// So the first compaction fails due to retryable IO error and it is mapped
|
||||
// to soft error. Then, compaction is rescheduled, in the second round of
|
||||
// compaction, the FS is set to active and compaction is successful, so
|
||||
// the test will hit the CompactionJob::FinishCompactionOutputFile1 sync
|
||||
// point.
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener =
|
||||
std::make_shared<ErrorHandlerFSListener>();
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.listeners.emplace_back(listener);
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Status s;
|
||||
std::atomic<bool> fail_first(false);
|
||||
std::atomic<bool> fail_second(true);
|
||||
Random rnd(301);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(1024)));
|
||||
}
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
|
||||
listener->OverrideBGError(Status(error_msg, Status::Severity::kHardError));
|
||||
listener->EnableAutoRecovery(false);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::FlushMemTable:FlushMemTableFinished",
|
||||
"BackgroundCallCompaction:0"},
|
||||
{"CompactionJob::FinishCompactionOutputFile1",
|
||||
"CompactionWriteRetryableErrorAutoRecover0"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:Start",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(true); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BackgroundCallCompaction:0", [&](void*) { fail_first.store(true); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:2", [&](void*) {
|
||||
if (fail_first.load() && fail_second.load()) {
|
||||
fault_fs_->SetFilesystemActive(false, error_msg);
|
||||
fail_second.store(false);
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put(Key(1), "val"));
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
|
||||
s = dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(s);
|
||||
TEST_SYNC_POINT("CompactionWriteRetryableErrorAutoRecover0");
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
Reopen(GetDefaultOptions());
|
||||
}
|
||||
|
||||
class DBErrorHandlingFencingTest : public DBErrorHandlingFSTest,
|
||||
public testing::WithParamInterface<bool> {};
|
||||
|
||||
|
||||
+3
-2
@@ -95,8 +95,9 @@ void EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
jwriter << "cf_name" << cf_name << "job" << job_id << "event"
|
||||
<< "table_file_creation"
|
||||
<< "file_number" << fd.GetNumber() << "file_size"
|
||||
<< fd.GetFileSize() << "file_checksum" << file_checksum
|
||||
<< "file_checksum_func_name" << file_checksum_func_name;
|
||||
<< fd.GetFileSize() << "file_checksum"
|
||||
<< Slice(file_checksum).ToString(true) << "file_checksum_func_name"
|
||||
<< file_checksum_func_name;
|
||||
|
||||
// table_properties
|
||||
{
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include "rocksdb/experimental.h"
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/version_util.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace experimental {
|
||||
@@ -48,103 +46,5 @@ Status SuggestCompactRange(DB* db, const Slice* begin, const Slice* end) {
|
||||
return SuggestCompactRange(db, db->DefaultColumnFamily(), begin, end);
|
||||
}
|
||||
|
||||
Status UpdateManifestForFilesState(
|
||||
const DBOptions& db_opts, const std::string& db_name,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
const UpdateManifestForFilesStateOptions& opts) {
|
||||
OfflineManifestWriter w(db_opts, db_name);
|
||||
Status s = w.Recover(column_families);
|
||||
|
||||
size_t files_updated = 0;
|
||||
size_t cfs_updated = 0;
|
||||
auto fs = db_opts.env->GetFileSystem();
|
||||
|
||||
for (auto cfd : *w.Versions().GetColumnFamilySet()) {
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
assert(cfd);
|
||||
|
||||
if (cfd->IsDropped() || !cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto* current = cfd->current();
|
||||
assert(current);
|
||||
|
||||
const auto* vstorage = current->storage_info();
|
||||
assert(vstorage);
|
||||
|
||||
VersionEdit edit;
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
|
||||
/* SST files */
|
||||
for (int level = 0; level < cfd->NumberLevels(); level++) {
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
const auto& level_files = vstorage->LevelFiles(level);
|
||||
|
||||
for (const auto& lf : level_files) {
|
||||
assert(lf);
|
||||
|
||||
uint64_t number = lf->fd.GetNumber();
|
||||
std::string fname =
|
||||
TableFileName(w.IOptions().db_paths, number, lf->fd.GetPathId());
|
||||
|
||||
std::unique_ptr<FSSequentialFile> f;
|
||||
FileOptions fopts;
|
||||
fopts.temperature = lf->temperature;
|
||||
|
||||
IOStatus file_ios =
|
||||
fs->NewSequentialFile(fname, fopts, &f, /*dbg*/ nullptr);
|
||||
if (file_ios.ok()) {
|
||||
if (opts.update_temperatures) {
|
||||
Temperature temp = f->GetTemperature();
|
||||
if (temp != Temperature::kUnknown && temp != lf->temperature) {
|
||||
// Current state inconsistent with manifest
|
||||
++files_updated;
|
||||
edit.DeleteFile(level, number);
|
||||
edit.AddFile(level, number, lf->fd.GetPathId(),
|
||||
lf->fd.GetFileSize(), lf->smallest, lf->largest,
|
||||
lf->fd.smallest_seqno, lf->fd.largest_seqno,
|
||||
lf->marked_for_compaction, temp,
|
||||
lf->oldest_blob_file_number,
|
||||
lf->oldest_ancester_time, lf->file_creation_time,
|
||||
lf->file_checksum, lf->file_checksum_func_name,
|
||||
lf->min_timestamp, lf->max_timestamp);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s = file_ios;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok() && edit.NumEntries() > 0) {
|
||||
s = w.LogAndApply(cfd, &edit);
|
||||
if (s.ok()) {
|
||||
++cfs_updated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cfs_updated > 0) {
|
||||
ROCKS_LOG_INFO(db_opts.info_log,
|
||||
"UpdateManifestForFilesState: updated %zu files in %zu CFs",
|
||||
files_updated, cfs_updated);
|
||||
} else if (s.ok()) {
|
||||
ROCKS_LOG_INFO(db_opts.info_log,
|
||||
"UpdateManifestForFilesState: no updates needed");
|
||||
}
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(db_opts.info_log, "UpdateManifestForFilesState failed: %s",
|
||||
s.ToString().c_str());
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+11
-25
@@ -136,6 +136,7 @@ FlushJob::FlushJob(
|
||||
}
|
||||
|
||||
FlushJob::~FlushJob() {
|
||||
io_status_.PermitUncheckedError();
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
}
|
||||
|
||||
@@ -169,20 +170,8 @@ void FlushJob::PickMemTable() {
|
||||
db_mutex_->AssertHeld();
|
||||
assert(!pick_memtable_called);
|
||||
pick_memtable_called = true;
|
||||
|
||||
// Maximum "NextLogNumber" of the memtables to flush.
|
||||
// When mempurge feature is turned off, this variable is useless
|
||||
// because the memtables are implicitly sorted by increasing order of creation
|
||||
// time. Therefore mems_->back()->GetNextLogNumber() is already equal to
|
||||
// max_next_log_number. However when Mempurge is on, the memtables are no
|
||||
// longer sorted by increasing order of creation time. Therefore this variable
|
||||
// becomes necessary because mems_->back()->GetNextLogNumber() is no longer
|
||||
// necessarily equal to max_next_log_number.
|
||||
uint64_t max_next_log_number = 0;
|
||||
|
||||
// Save the contents of the earliest memtable as a new Table
|
||||
cfd_->imm()->PickMemtablesToFlush(max_memtable_id_, &mems_,
|
||||
&max_next_log_number);
|
||||
cfd_->imm()->PickMemtablesToFlush(max_memtable_id_, &mems_);
|
||||
if (mems_.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -197,7 +186,7 @@ void FlushJob::PickMemTable() {
|
||||
edit_->SetPrevLogNumber(0);
|
||||
// SetLogNumber(log_num) indicates logs with number smaller than log_num
|
||||
// will no longer be picked up for recovery.
|
||||
edit_->SetLogNumber(max_next_log_number);
|
||||
edit_->SetLogNumber(mems_.back()->GetNextLogNumber());
|
||||
edit_->SetColumnFamily(cfd_->GetID());
|
||||
|
||||
// path 0 for level 0 file.
|
||||
@@ -289,13 +278,17 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker, FileMetaData* file_meta,
|
||||
} else if (write_manifest_) {
|
||||
TEST_SYNC_POINT("FlushJob::InstallResults");
|
||||
// Replace immutable memtable with the generated Table
|
||||
IOStatus tmp_io_s;
|
||||
s = cfd_->imm()->TryInstallMemtableFlushResults(
|
||||
cfd_, mutable_cf_options_, mems_, prep_tracker, versions_, db_mutex_,
|
||||
meta_.fd.GetNumber(), &job_context_->memtables_to_free, db_directory_,
|
||||
log_buffer_, &committed_flush_jobs_info_,
|
||||
log_buffer_, &committed_flush_jobs_info_, &tmp_io_s,
|
||||
!(mempurge_s.ok()) /* write_edit : true if no mempurge happened (or if aborted),
|
||||
but 'false' if mempurge successful: no new min log number
|
||||
or new level 0 file path to write to manifest. */);
|
||||
if (!tmp_io_s.ok()) {
|
||||
io_status_ = tmp_io_s;
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok() && file_meta != nullptr) {
|
||||
@@ -576,7 +569,6 @@ Status FlushJob::MemPurge() {
|
||||
uint64_t new_mem_id = mems_[0]->GetID();
|
||||
|
||||
new_mem->SetID(new_mem_id);
|
||||
new_mem->SetNextLogNumber(mems_[0]->GetNextLogNumber());
|
||||
|
||||
// This addition will not trigger another flush, because
|
||||
// we do not call SchedulePendingFlush().
|
||||
@@ -823,12 +815,6 @@ Status FlushJob::WriteLevel0Table() {
|
||||
uint64_t total_num_entries = 0, total_num_deletes = 0;
|
||||
uint64_t total_data_size = 0;
|
||||
size_t total_memory_usage = 0;
|
||||
// Used for testing:
|
||||
uint64_t mems_size = mems_.size();
|
||||
(void)mems_size; // avoids unused variable error when
|
||||
// TEST_SYNC_POINT_CALLBACK not used.
|
||||
TEST_SYNC_POINT_CALLBACK("FlushJob::WriteLevel0Table:num_memtables",
|
||||
&mems_size);
|
||||
for (MemTable* m : mems_) {
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
@@ -921,9 +907,9 @@ Status FlushJob::WriteLevel0Table() {
|
||||
job_context_->job_id, Env::IO_HIGH, &table_properties_, write_hint,
|
||||
full_history_ts_low, blob_callback_, &num_input_entries,
|
||||
&memtable_payload_bytes, &memtable_garbage_bytes);
|
||||
// TODO: Cleanup io_status in BuildTable and table builders
|
||||
assert(!s.ok() || io_s.ok());
|
||||
io_s.PermitUncheckedError();
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
}
|
||||
if (num_input_entries != total_num_entries && s.ok()) {
|
||||
std::string msg = "Expected " + ToString(total_num_entries) +
|
||||
" entries in memtables, but read " +
|
||||
|
||||
@@ -93,6 +93,9 @@ class FlushJob {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Return the IO status
|
||||
IOStatus io_status() const { return io_status_; }
|
||||
|
||||
private:
|
||||
void ReportStartedFlush();
|
||||
void ReportFlushInputSize(const autovector<MemTable*>& mems);
|
||||
@@ -181,6 +184,7 @@ class FlushJob {
|
||||
Version* base_;
|
||||
bool pick_memtable_called;
|
||||
Env::Priority thread_pri_;
|
||||
IOStatus io_status_;
|
||||
|
||||
const std::shared_ptr<IOTracer> io_tracer_;
|
||||
SystemClock* clock_;
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "table/internal_iterator.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class HistoryTrimmingIterator : public InternalIterator {
|
||||
public:
|
||||
explicit HistoryTrimmingIterator(InternalIterator* input,
|
||||
const Comparator* cmp, const std::string& ts)
|
||||
: input_(input), filter_ts_(ts), cmp_(cmp) {
|
||||
assert(cmp_->timestamp_size() > 0 && !ts.empty());
|
||||
}
|
||||
|
||||
bool filter() const {
|
||||
if (!input_->Valid()) {
|
||||
return true;
|
||||
}
|
||||
Slice current_ts = ExtractTimestampFromKey(key(), cmp_->timestamp_size());
|
||||
return cmp_->CompareTimestamp(current_ts, Slice(filter_ts_)) <= 0;
|
||||
}
|
||||
|
||||
bool Valid() const override { return input_->Valid(); }
|
||||
|
||||
void SeekToFirst() override {
|
||||
input_->SeekToFirst();
|
||||
while (!filter()) {
|
||||
input_->Next();
|
||||
}
|
||||
}
|
||||
|
||||
void SeekToLast() override {
|
||||
input_->SeekToLast();
|
||||
while (!filter()) {
|
||||
input_->Prev();
|
||||
}
|
||||
}
|
||||
|
||||
void Seek(const Slice& target) override {
|
||||
input_->Seek(target);
|
||||
while (!filter()) {
|
||||
input_->Next();
|
||||
}
|
||||
}
|
||||
|
||||
void SeekForPrev(const Slice& target) override {
|
||||
input_->SeekForPrev(target);
|
||||
while (!filter()) {
|
||||
input_->Prev();
|
||||
}
|
||||
}
|
||||
|
||||
void Next() override {
|
||||
do {
|
||||
input_->Next();
|
||||
} while (!filter());
|
||||
}
|
||||
|
||||
void Prev() override {
|
||||
do {
|
||||
input_->Prev();
|
||||
} while (!filter());
|
||||
}
|
||||
|
||||
Slice key() const override { return input_->key(); }
|
||||
|
||||
Slice value() const override { return input_->value(); }
|
||||
|
||||
Status status() const override { return input_->status(); }
|
||||
|
||||
bool IsKeyPinned() const override { return input_->IsKeyPinned(); }
|
||||
|
||||
bool IsValuePinned() const override { return input_->IsValuePinned(); }
|
||||
|
||||
private:
|
||||
InternalIterator* input_;
|
||||
const std::string filter_ts_;
|
||||
const Comparator* const cmp_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -519,7 +519,7 @@ const std::unordered_map<std::string, DBPropertyInfo>
|
||||
{false, nullptr, &InternalStats::HandleLiveSstFilesSize, nullptr,
|
||||
nullptr}},
|
||||
{DB::Properties::kLiveSstFilesSizeAtTemperature,
|
||||
{false, &InternalStats::HandleLiveSstFilesSizeAtTemperature, nullptr,
|
||||
{true, &InternalStats::HandleLiveSstFilesSizeAtTemperature, nullptr,
|
||||
nullptr, nullptr}},
|
||||
{DB::Properties::kEstimatePendingCompactionBytes,
|
||||
{false, nullptr, &InternalStats::HandleEstimatePendingCompactionBytes,
|
||||
|
||||
+1
-4
@@ -37,16 +37,13 @@ struct SuperVersionContext {
|
||||
explicit SuperVersionContext(bool create_superversion = false)
|
||||
: new_superversion(create_superversion ? new SuperVersion() : nullptr) {}
|
||||
|
||||
explicit SuperVersionContext(SuperVersionContext&& other) noexcept
|
||||
explicit SuperVersionContext(SuperVersionContext&& other)
|
||||
: superversions_to_free(std::move(other.superversions_to_free)),
|
||||
#ifndef ROCKSDB_DISABLE_STALL_NOTIFICATION
|
||||
write_stall_notifications(std::move(other.write_stall_notifications)),
|
||||
#endif
|
||||
new_superversion(std::move(other.new_superversion)) {
|
||||
}
|
||||
// No copies
|
||||
SuperVersionContext(const SuperVersionContext& other) = delete;
|
||||
void operator=(const SuperVersionContext& other) = delete;
|
||||
|
||||
void NewSuperVersion() {
|
||||
new_superversion = std::unique_ptr<SuperVersion>(new SuperVersion());
|
||||
|
||||
+3
-13
@@ -340,9 +340,6 @@ 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);
|
||||
}
|
||||
@@ -465,13 +462,6 @@ 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) {
|
||||
@@ -533,10 +523,10 @@ TEST_F(EventListenerTest, DisableBGCompaction) {
|
||||
ASSERT_OK(db_->Flush(fo, handles_[1]));
|
||||
db_->GetColumnFamilyMetaData(handles_[1], &cf_meta);
|
||||
}
|
||||
// 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);
|
||||
// We don't want the listener executing during DBTestBase::Close() due to
|
||||
// race on handles_.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
}
|
||||
|
||||
class TestCompactionReasonListener : public EventListener {
|
||||
|
||||
+9
-77
@@ -10,7 +10,6 @@
|
||||
#include "db/log_reader.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "file/sequence_file_reader.h"
|
||||
#include "port/lang.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -42,14 +41,10 @@ Reader::Reader(std::shared_ptr<Logger> info_log,
|
||||
recycled_(false),
|
||||
first_record_read_(false),
|
||||
compression_type_(kNoCompression),
|
||||
compression_type_record_read_(false),
|
||||
uncompress_(nullptr) {}
|
||||
compression_type_record_read_(false) {}
|
||||
|
||||
Reader::~Reader() {
|
||||
delete[] backing_store_;
|
||||
if (uncompress_) {
|
||||
delete uncompress_;
|
||||
}
|
||||
}
|
||||
|
||||
// For kAbsoluteConsistency, on clean shutdown we don't expect any error
|
||||
@@ -63,9 +58,6 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
WALRecoveryMode wal_recovery_mode) {
|
||||
scratch->clear();
|
||||
record->clear();
|
||||
if (uncompress_) {
|
||||
uncompress_->Reset();
|
||||
}
|
||||
bool in_fragmented_record = false;
|
||||
// Record offset of the logical record that we're reading
|
||||
// 0 is a dummy value to make compilers happy
|
||||
@@ -243,7 +235,8 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
ReportCorruption(fragment.size(),
|
||||
"could not decode SetCompressionType record");
|
||||
} else {
|
||||
InitCompression(compression_record);
|
||||
compression_type_ = compression_record.GetCompressionType();
|
||||
compression_type_record_read_ = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -457,54 +450,17 @@ unsigned int Reader::ReadPhysicalRecord(Slice* result, size_t* drop_size) {
|
||||
|
||||
buffer_.remove_prefix(header_size + length);
|
||||
|
||||
if (!uncompress_ || type == kSetCompressionType) {
|
||||
*result = Slice(header + header_size, length);
|
||||
return type;
|
||||
} else {
|
||||
// Uncompress compressed records
|
||||
uncompressed_record_.clear();
|
||||
size_t uncompressed_size = 0;
|
||||
int remaining = 0;
|
||||
do {
|
||||
remaining = uncompress_->Uncompress(header + header_size, length,
|
||||
uncompressed_buffer_.get(),
|
||||
&uncompressed_size);
|
||||
if (remaining < 0) {
|
||||
buffer_.clear();
|
||||
return kBadRecord;
|
||||
}
|
||||
if (uncompressed_size > 0) {
|
||||
uncompressed_record_.append(uncompressed_buffer_.get(),
|
||||
uncompressed_size);
|
||||
}
|
||||
} while (remaining > 0 || uncompressed_size == kBlockSize);
|
||||
*result = Slice(uncompressed_record_);
|
||||
return type;
|
||||
}
|
||||
*result = Slice(header + header_size, length);
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize uncompress related fields
|
||||
void Reader::InitCompression(const CompressionTypeRecord& compression_record) {
|
||||
compression_type_ = compression_record.GetCompressionType();
|
||||
compression_type_record_read_ = true;
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
uncompress_ = StreamingUncompress::Create(
|
||||
compression_type_, compression_format_version, kBlockSize);
|
||||
assert(uncompress_ != nullptr);
|
||||
uncompressed_buffer_ = std::unique_ptr<char[]>(new char[kBlockSize]);
|
||||
assert(uncompressed_buffer_);
|
||||
}
|
||||
|
||||
bool FragmentBufferedReader::ReadRecord(Slice* record, std::string* scratch,
|
||||
WALRecoveryMode /*unused*/) {
|
||||
assert(record != nullptr);
|
||||
assert(scratch != nullptr);
|
||||
record->clear();
|
||||
scratch->clear();
|
||||
if (uncompress_) {
|
||||
uncompress_->Reset();
|
||||
}
|
||||
|
||||
uint64_t prospective_record_offset = 0;
|
||||
uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size();
|
||||
@@ -606,7 +562,7 @@ bool FragmentBufferedReader::ReadRecord(Slice* record, std::string* scratch,
|
||||
ReportCorruption(fragment.size(),
|
||||
"could not decode SetCompressionType record");
|
||||
} else {
|
||||
InitCompression(compression_record);
|
||||
compression_type_ = compression_record.GetCompressionType();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -744,33 +700,9 @@ bool FragmentBufferedReader::TryReadFragment(
|
||||
|
||||
buffer_.remove_prefix(header_size + length);
|
||||
|
||||
if (!uncompress_ || type == kSetCompressionType) {
|
||||
*fragment = Slice(header + header_size, length);
|
||||
*fragment_type_or_err = type;
|
||||
return true;
|
||||
} else {
|
||||
// Uncompress compressed records
|
||||
uncompressed_record_.clear();
|
||||
size_t uncompressed_size = 0;
|
||||
int remaining = 0;
|
||||
do {
|
||||
remaining = uncompress_->Uncompress(header + header_size, length,
|
||||
uncompressed_buffer_.get(),
|
||||
&uncompressed_size);
|
||||
if (remaining < 0) {
|
||||
buffer_.clear();
|
||||
*fragment_type_or_err = kBadRecord;
|
||||
return true;
|
||||
}
|
||||
if (uncompressed_size > 0) {
|
||||
uncompressed_record_.append(uncompressed_buffer_.get(),
|
||||
uncompressed_size);
|
||||
}
|
||||
} while (remaining > 0 || uncompressed_size == kBlockSize);
|
||||
*fragment = Slice(std::move(uncompressed_record_));
|
||||
*fragment_type_or_err = type;
|
||||
return true;
|
||||
}
|
||||
*fragment = Slice(header + header_size, length);
|
||||
*fragment_type_or_err = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace log
|
||||
|
||||
@@ -136,11 +136,6 @@ class Reader {
|
||||
CompressionType compression_type_;
|
||||
// Track whether the compression type record has been read or not.
|
||||
bool compression_type_record_read_;
|
||||
StreamingUncompress* uncompress_;
|
||||
// Reusable uncompressed output buffer
|
||||
std::unique_ptr<char[]> uncompressed_buffer_;
|
||||
// Reusable uncompressed record
|
||||
std::string uncompressed_record_;
|
||||
|
||||
// Extend record types with the following special values
|
||||
enum {
|
||||
@@ -172,8 +167,6 @@ class Reader {
|
||||
// buffer_ must be updated to remove the dropped bytes prior to invocation.
|
||||
void ReportCorruption(size_t bytes, const char* reason);
|
||||
void ReportDrop(size_t bytes, const Status& reason);
|
||||
|
||||
void InitCompression(const CompressionTypeRecord& compression_record);
|
||||
};
|
||||
|
||||
class FragmentBufferedReader : public Reader {
|
||||
|
||||
-124
@@ -17,7 +17,6 @@
|
||||
#include "util/coding.h"
|
||||
#include "util/crc32c.h"
|
||||
#include "util/random.h"
|
||||
#include "utilities/memory_allocators.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace log {
|
||||
@@ -904,11 +903,6 @@ class CompressionLogTest : public LogTest {
|
||||
};
|
||||
|
||||
TEST_P(CompressionLogTest, Empty) {
|
||||
CompressionType compression_type = std::get<2>(GetParam());
|
||||
if (!StreamingCompressionTypeSupported(compression_type)) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires support for compression type");
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(SetupTestEnv());
|
||||
const bool compression_enabled =
|
||||
std::get<2>(GetParam()) == kNoCompression ? false : true;
|
||||
@@ -918,130 +912,12 @@ TEST_P(CompressionLogTest, Empty) {
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
TEST_P(CompressionLogTest, ReadWrite) {
|
||||
CompressionType compression_type = std::get<2>(GetParam());
|
||||
if (!StreamingCompressionTypeSupported(compression_type)) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires support for compression type");
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(SetupTestEnv());
|
||||
Write("foo");
|
||||
Write("bar");
|
||||
Write("");
|
||||
Write("xxxx");
|
||||
ASSERT_EQ("foo", Read());
|
||||
ASSERT_EQ("bar", Read());
|
||||
ASSERT_EQ("", Read());
|
||||
ASSERT_EQ("xxxx", Read());
|
||||
ASSERT_EQ("EOF", Read());
|
||||
ASSERT_EQ("EOF", Read()); // Make sure reads at eof work
|
||||
}
|
||||
|
||||
TEST_P(CompressionLogTest, ManyBlocks) {
|
||||
CompressionType compression_type = std::get<2>(GetParam());
|
||||
if (!StreamingCompressionTypeSupported(compression_type)) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires support for compression type");
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(SetupTestEnv());
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
Write(NumberString(i));
|
||||
}
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
ASSERT_EQ(NumberString(i), Read());
|
||||
}
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
TEST_P(CompressionLogTest, Fragmentation) {
|
||||
CompressionType compression_type = std::get<2>(GetParam());
|
||||
if (!StreamingCompressionTypeSupported(compression_type)) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires support for compression type");
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(SetupTestEnv());
|
||||
Write("small");
|
||||
Write(BigString("medium", 50000));
|
||||
Write(BigString("large", 100000));
|
||||
ASSERT_EQ("small", Read());
|
||||
ASSERT_EQ(BigString("medium", 50000), Read());
|
||||
ASSERT_EQ(BigString("large", 100000), Read());
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Compression, CompressionLogTest,
|
||||
::testing::Combine(::testing::Values(0, 1), ::testing::Bool(),
|
||||
::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_pos;
|
||||
remaining = compress->Compress(input_buffer.c_str(), input_size,
|
||||
output_buffer, &output_pos);
|
||||
if (output_pos > 0) {
|
||||
std::string compressed_buffer;
|
||||
compressed_buffer.assign(output_buffer, output_pos);
|
||||
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_pos;
|
||||
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_pos);
|
||||
if (output_pos > 0) {
|
||||
std::string uncompressed_fragment;
|
||||
uncompressed_fragment.assign(uncompressed_output_buffer, output_pos);
|
||||
uncompressed_buffer += uncompressed_fragment;
|
||||
}
|
||||
} while (ret_val > 0 || output_pos == 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
|
||||
|
||||
|
||||
+10
-61
@@ -10,7 +10,6 @@
|
||||
#include "db/log_writer.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/coding.h"
|
||||
@@ -27,8 +26,7 @@ Writer::Writer(std::unique_ptr<WritableFileWriter>&& dest, uint64_t log_number,
|
||||
log_number_(log_number),
|
||||
recycle_log_files_(recycle_log_files),
|
||||
manual_flush_(manual_flush),
|
||||
compression_type_(compression_type),
|
||||
compress_(nullptr) {
|
||||
compression_type_(compression_type) {
|
||||
for (int i = 0; i <= kMaxRecordType; i++) {
|
||||
char t = static_cast<char>(i);
|
||||
type_crc_[i] = crc32c::Value(&t, 1);
|
||||
@@ -39,9 +37,6 @@ Writer::~Writer() {
|
||||
if (dest_) {
|
||||
WriteBuffer().PermitUncheckedError();
|
||||
}
|
||||
if (compress_) {
|
||||
delete compress_;
|
||||
}
|
||||
}
|
||||
|
||||
IOStatus Writer::WriteBuffer() { return dest_->Flush(); }
|
||||
@@ -55,8 +50,7 @@ IOStatus Writer::Close() {
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus Writer::AddRecord(const Slice& slice,
|
||||
Env::IOPriority rate_limiter_priority) {
|
||||
IOStatus Writer::AddRecord(const Slice& slice) {
|
||||
const char* ptr = slice.data();
|
||||
size_t left = slice.size();
|
||||
|
||||
@@ -69,12 +63,6 @@ IOStatus Writer::AddRecord(const Slice& slice,
|
||||
// zero-length record
|
||||
IOStatus s;
|
||||
bool begin = true;
|
||||
int compress_remaining = 0;
|
||||
bool compress_start = false;
|
||||
if (compress_) {
|
||||
compress_->Reset();
|
||||
compress_start = true;
|
||||
}
|
||||
do {
|
||||
const int64_t leftover = kBlockSize - block_offset_;
|
||||
assert(leftover >= 0);
|
||||
@@ -85,8 +73,7 @@ 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)),
|
||||
0 /* crc32c_checksum */, rate_limiter_priority);
|
||||
static_cast<size_t>(leftover)));
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -98,34 +85,10 @@ IOStatus Writer::AddRecord(const Slice& slice,
|
||||
assert(static_cast<int64_t>(kBlockSize - block_offset_) >= header_size);
|
||||
|
||||
const size_t avail = kBlockSize - block_offset_ - header_size;
|
||||
|
||||
// Compress the record if compression is enabled.
|
||||
// Compress() is called at least once (compress_start=true) and after the
|
||||
// previous generated compressed chunk is written out as one or more
|
||||
// physical records (left=0).
|
||||
if (compress_ && (compress_start || left == 0)) {
|
||||
compress_remaining = compress_->Compress(slice.data(), slice.size(),
|
||||
compressed_buffer_.get(), &left);
|
||||
|
||||
if (compress_remaining < 0) {
|
||||
// Set failure status
|
||||
s = IOStatus::IOError("Unexpected WAL compression error");
|
||||
s.SetDataLoss(true);
|
||||
break;
|
||||
} else if (left == 0) {
|
||||
// Nothing left to compress
|
||||
if (!compress_start) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
compress_start = false;
|
||||
ptr = compressed_buffer_.get();
|
||||
}
|
||||
|
||||
const size_t fragment_length = (left < avail) ? left : avail;
|
||||
|
||||
RecordType type;
|
||||
const bool end = (left == fragment_length && compress_remaining == 0);
|
||||
const bool end = (left == fragment_length);
|
||||
if (begin && end) {
|
||||
type = recycle_log_files_ ? kRecyclableFullType : kFullType;
|
||||
} else if (begin) {
|
||||
@@ -136,15 +99,15 @@ IOStatus Writer::AddRecord(const Slice& slice,
|
||||
type = recycle_log_files_ ? kRecyclableMiddleType : kMiddleType;
|
||||
}
|
||||
|
||||
s = EmitPhysicalRecord(type, ptr, fragment_length, rate_limiter_priority);
|
||||
s = EmitPhysicalRecord(type, ptr, fragment_length);
|
||||
ptr += fragment_length;
|
||||
left -= fragment_length;
|
||||
begin = false;
|
||||
} while (s.ok() && (left > 0 || compress_remaining > 0));
|
||||
} while (s.ok() && left > 0);
|
||||
|
||||
if (s.ok()) {
|
||||
if (!manual_flush_) {
|
||||
s = dest_->Flush(rate_limiter_priority);
|
||||
s = dest_->Flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,18 +132,6 @@ IOStatus Writer::AddCompressionTypeRecord() {
|
||||
if (!manual_flush_) {
|
||||
s = dest_->Flush();
|
||||
}
|
||||
// Initialize fields required for compression
|
||||
const size_t max_output_buffer_len =
|
||||
kBlockSize - (recycle_log_files_ ? kRecyclableHeaderSize : kHeaderSize);
|
||||
CompressionOptions opts;
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
compress_ = StreamingCompress::Create(compression_type_, opts,
|
||||
compression_format_version,
|
||||
max_output_buffer_len);
|
||||
assert(compress_ != nullptr);
|
||||
compressed_buffer_ =
|
||||
std::unique_ptr<char[]>(new char[max_output_buffer_len]);
|
||||
assert(compressed_buffer_);
|
||||
} else {
|
||||
// Disable compression if the record could not be added.
|
||||
compression_type_ = kNoCompression;
|
||||
@@ -190,8 +141,7 @@ IOStatus Writer::AddCompressionTypeRecord() {
|
||||
|
||||
bool Writer::TEST_BufferIsEmpty() { return dest_->TEST_BufferIsEmpty(); }
|
||||
|
||||
IOStatus Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n,
|
||||
Env::IOPriority rate_limiter_priority) {
|
||||
IOStatus Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) {
|
||||
assert(n <= 0xffff); // Must fit in two bytes
|
||||
|
||||
size_t header_size;
|
||||
@@ -230,10 +180,9 @@ 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), 0 /* crc32c_checksum */,
|
||||
rate_limiter_priority);
|
||||
IOStatus s = dest_->Append(Slice(buf, header_size));
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(Slice(ptr, n), payload_crc, rate_limiter_priority);
|
||||
s = dest_->Append(Slice(ptr, n), payload_crc);
|
||||
}
|
||||
block_offset_ += header_size + n;
|
||||
return s;
|
||||
|
||||
+2
-10
@@ -13,11 +13,9 @@
|
||||
|
||||
#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"
|
||||
#include "util/compression.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -83,8 +81,7 @@ class Writer {
|
||||
|
||||
~Writer();
|
||||
|
||||
IOStatus AddRecord(const Slice& slice,
|
||||
Env::IOPriority rate_limiter_priority = Env::IO_TOTAL);
|
||||
IOStatus AddRecord(const Slice& slice);
|
||||
IOStatus AddCompressionTypeRecord();
|
||||
|
||||
WritableFileWriter* file() { return dest_.get(); }
|
||||
@@ -109,9 +106,7 @@ class Writer {
|
||||
// record type stored in the header.
|
||||
uint32_t type_crc_[kMaxRecordType + 1];
|
||||
|
||||
IOStatus EmitPhysicalRecord(
|
||||
RecordType type, const char* ptr, size_t length,
|
||||
Env::IOPriority rate_limiter_priority = Env::IO_TOTAL);
|
||||
IOStatus EmitPhysicalRecord(RecordType type, const char* ptr, size_t length);
|
||||
|
||||
// If true, it does not flush after each write. Instead it relies on the upper
|
||||
// layer to manually does the flush by calling ::WriteBuffer()
|
||||
@@ -119,9 +114,6 @@ class Writer {
|
||||
|
||||
// Compression Type
|
||||
CompressionType compression_type_;
|
||||
StreamingCompress* compress_;
|
||||
// Reusable compressed output buffer
|
||||
std::unique_ptr<char[]> compressed_buffer_;
|
||||
};
|
||||
|
||||
} // namespace log
|
||||
|
||||
+29
-20
@@ -338,8 +338,7 @@ bool MemTableList::IsFlushPending() const {
|
||||
|
||||
// Returns the memtables that need to be flushed.
|
||||
void MemTableList::PickMemtablesToFlush(uint64_t max_memtable_id,
|
||||
autovector<MemTable*>* ret,
|
||||
uint64_t* max_next_log_number) {
|
||||
autovector<MemTable*>* ret) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_PICK_MEMTABLES_TO_FLUSH);
|
||||
const auto& memlist = current_->memlist_;
|
||||
@@ -350,7 +349,8 @@ void MemTableList::PickMemtablesToFlush(uint64_t max_memtable_id,
|
||||
// iterating through the memlist starting at the end, the vector<MemTable*>
|
||||
// ret is filled with memtables already sorted in increasing MemTable ID.
|
||||
// However, when the mempurge feature is activated, new memtables with older
|
||||
// IDs will be added to the memlist.
|
||||
// IDs will be added to the memlist. Therefore we std::sort(ret) at the end to
|
||||
// return a vector of memtables sorted by increasing memtable ID.
|
||||
for (auto it = memlist.rbegin(); it != memlist.rend(); ++it) {
|
||||
MemTable* m = *it;
|
||||
if (!atomic_flush && m->atomic_flush_seqno_ != kMaxSequenceNumber) {
|
||||
@@ -366,16 +366,21 @@ void MemTableList::PickMemtablesToFlush(uint64_t max_memtable_id,
|
||||
imm_flush_needed.store(false, std::memory_order_release);
|
||||
}
|
||||
m->flush_in_progress_ = true; // flushing will start very soon
|
||||
if (max_next_log_number) {
|
||||
*max_next_log_number =
|
||||
std::max(m->GetNextLogNumber(), *max_next_log_number);
|
||||
}
|
||||
ret->push_back(m);
|
||||
}
|
||||
}
|
||||
if (!atomic_flush || num_flush_not_started_ == 0) {
|
||||
flush_requested_ = false; // start-flush request is complete
|
||||
}
|
||||
|
||||
// Sort the list of memtables by increasing memtable ID.
|
||||
// This is useful when the mempurge feature is activated
|
||||
// and the memtables are not guaranteed to be sorted in
|
||||
// the memlist vector.
|
||||
std::sort(ret->begin(), ret->end(),
|
||||
[](const MemTable* m1, const MemTable* m2) -> bool {
|
||||
return m1->GetID() < m2->GetID();
|
||||
});
|
||||
}
|
||||
|
||||
void MemTableList::RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
@@ -407,7 +412,7 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer,
|
||||
std::list<std::unique_ptr<FlushJobInfo>>* committed_flush_jobs_info,
|
||||
bool write_edits) {
|
||||
IOStatus* io_s, bool write_edits) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_MEMTABLE_INSTALL_FLUSH_RESULTS);
|
||||
mu->AssertHeld();
|
||||
@@ -489,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,
|
||||
@@ -499,23 +504,26 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
min_wal_number_to_keep = PrecomputeMinLogNumberToKeep2PC(
|
||||
vset, *cfd, edit_list, memtables_to_flush, prep_tracker);
|
||||
|
||||
// We piggyback the information of earliest log file to keep in the
|
||||
// We piggyback the information of earliest log file to keep in the
|
||||
// manifest entry for the last file flushed.
|
||||
edit_list.back()->SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
} else {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, *cfd, edit_list);
|
||||
}
|
||||
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,
|
||||
@@ -529,6 +537,7 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
db_directory, /*new_descriptor_log=*/false,
|
||||
/*column_family_options=*/nullptr,
|
||||
manifest_write_cb);
|
||||
*io_s = vset->io_status();
|
||||
} else {
|
||||
// If write_edit is false (e.g: successful mempurge),
|
||||
// then remove old memtables, wake up manifest write queue threads,
|
||||
@@ -544,6 +553,7 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
// TODO(bjlemaire): explain full reason WakeUpWaitingManifestWriters
|
||||
// needed or investigate more.
|
||||
vset->WakeUpWaitingManifestWriters();
|
||||
*io_s = IOStatus::OK();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -798,15 +808,14 @@ Status InstallMemtableAtomicFlushResults(
|
||||
if (vset->db_options()->allow_2pc) {
|
||||
min_wal_number_to_keep = PrecomputeMinLogNumberToKeep2PC(
|
||||
vset, cfds, edit_lists, mems_list, prep_tracker);
|
||||
edit_lists.back().back()->SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
} else {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, cfds, edit_lists);
|
||||
}
|
||||
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);
|
||||
|
||||
+2
-3
@@ -253,8 +253,7 @@ class MemTableList {
|
||||
// Returns the earliest memtables that needs to be flushed. The returned
|
||||
// memtables are guaranteed to be in the ascending order of created time.
|
||||
void PickMemtablesToFlush(uint64_t max_memtable_id,
|
||||
autovector<MemTable*>* mems,
|
||||
uint64_t* max_next_log_number = nullptr);
|
||||
autovector<MemTable*>* mems);
|
||||
|
||||
// Reset status of the given memtable list back to pending state so that
|
||||
// they can get picked up again on the next round of flush.
|
||||
@@ -270,7 +269,7 @@ class MemTableList {
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer,
|
||||
std::list<std::unique_ptr<FlushJobInfo>>* committed_flush_jobs_info,
|
||||
bool write_edits = true);
|
||||
IOStatus* io_s, bool write_edits = true);
|
||||
|
||||
// New memtables are inserted at the front of the list.
|
||||
// Takes ownership of the referenced held on *m by the caller of Add().
|
||||
|
||||
@@ -124,7 +124,7 @@ class MemTableListTest : public testing::Test {
|
||||
std::list<std::unique_ptr<FlushJobInfo>> flush_jobs_info;
|
||||
Status s = list->TryInstallMemtableFlushResults(
|
||||
cfd, mutable_cf_options, m, &dummy_prep_tracker, &versions, &mutex,
|
||||
file_num, to_delete, nullptr, &log_buffer, &flush_jobs_info);
|
||||
file_num, to_delete, nullptr, &log_buffer, &flush_jobs_info, &io_s);
|
||||
EXPECT_OK(io_s);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -394,7 +394,7 @@ void VersionEditHandler::CheckIterationResult(const log::Reader& reader,
|
||||
if (s->ok()) {
|
||||
version_set_->GetColumnFamilySet()->UpdateMaxColumnFamily(
|
||||
version_edit_params_.max_column_family_);
|
||||
version_set_->MarkMinLogNumberToKeep2PC(
|
||||
version_set_->MarkMinLogNumberToKeep(
|
||||
version_edit_params_.min_log_number_to_keep_);
|
||||
version_set_->MarkFileNumberUsed(version_edit_params_.prev_log_number_);
|
||||
version_set_->MarkFileNumberUsed(version_edit_params_.log_number_);
|
||||
@@ -970,12 +970,11 @@ void DumpManifestHandler::CheckIterationResult(const log::Reader& reader,
|
||||
fprintf(stdout,
|
||||
"next_file_number %" PRIu64 " last_sequence %" PRIu64
|
||||
" prev_log_number %" PRIu64 " max_column_family %" PRIu32
|
||||
" min_log_number_to_keep "
|
||||
"%" PRIu64 "\n",
|
||||
" min_log_number_to_keep %" PRIu64 "\n",
|
||||
version_set_->current_next_file_number(),
|
||||
version_set_->LastSequence(), version_set_->prev_log_number(),
|
||||
version_set_->column_family_set_->GetMaxColumnFamily(),
|
||||
version_set_->min_log_number_to_keep_2pc());
|
||||
version_set_->min_log_number_to_keep());
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+7
-8
@@ -4141,7 +4141,7 @@ void VersionSet::Reset() {
|
||||
}
|
||||
db_id_.clear();
|
||||
next_file_number_.store(2);
|
||||
min_log_number_to_keep_2pc_.store(0);
|
||||
min_log_number_to_keep_.store(0);
|
||||
manifest_file_number_ = 0;
|
||||
options_file_number_ = 0;
|
||||
pending_manifest_file_number_ = 0;
|
||||
@@ -4610,8 +4610,7 @@ Status VersionSet::ProcessManifestWrites(
|
||||
}
|
||||
|
||||
if (last_min_log_number_to_keep != 0) {
|
||||
// Should only be set in 2PC mode.
|
||||
MarkMinLogNumberToKeep2PC(last_min_log_number_to_keep);
|
||||
MarkMinLogNumberToKeep(last_min_log_number_to_keep);
|
||||
}
|
||||
|
||||
for (int i = 0; i < static_cast<int>(versions.size()); ++i) {
|
||||
@@ -4965,7 +4964,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_2pc());
|
||||
column_family_set_->GetMaxColumnFamily(), min_log_number_to_keep());
|
||||
|
||||
for (auto cfd : *column_family_set_) {
|
||||
if (cfd->IsDropped()) {
|
||||
@@ -5378,9 +5377,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::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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5506,7 +5505,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_2pc();
|
||||
uint64_t min_log = min_log_number_to_keep();
|
||||
if (min_log != 0) {
|
||||
edit.SetMinLogNumberToKeep(min_log);
|
||||
}
|
||||
|
||||
+12
-7
@@ -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_2pc() const {
|
||||
return min_log_number_to_keep_2pc_.load();
|
||||
uint64_t min_log_number_to_keep() const {
|
||||
return min_log_number_to_keep_.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 MarkMinLogNumberToKeep2PC(uint64_t number);
|
||||
void MarkMinLogNumberToKeep(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,10 +1196,12 @@ 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.
|
||||
// deleted, although we still use `min_log_number_to_keep_` to determine when
|
||||
// to delete a WAL file.
|
||||
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
|
||||
@@ -1297,6 +1299,10 @@ 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 =
|
||||
@@ -1399,9 +1405,8 @@ 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 in 2PC mode. In non-2PC mode, this
|
||||
// number is ignored.
|
||||
std::atomic<uint64_t> min_log_number_to_keep_2pc_ = {0};
|
||||
// and is qualified for being deleted.
|
||||
std::atomic<uint64_t> min_log_number_to_keep_ = {0};
|
||||
uint64_t manifest_file_number_;
|
||||
uint64_t options_file_number_;
|
||||
uint64_t options_file_size_;
|
||||
|
||||
@@ -3424,6 +3424,7 @@ TEST_F(VersionSetTestMissingFiles, NoFileMissing) {
|
||||
}
|
||||
|
||||
TEST_F(VersionSetTestMissingFiles, MinLogNumberToKeep2PC) {
|
||||
db_options_.allow_2pc = true;
|
||||
NewDB();
|
||||
|
||||
SstInfo sst(100, kDefaultColumnFamilyName, "a");
|
||||
@@ -3435,12 +3436,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_2pc(), kMinWalNumberToKeep2PC);
|
||||
ASSERT_EQ(versions_->min_log_number_to_keep(), kMinWalNumberToKeep2PC);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
CreateNewManifest();
|
||||
ReopenDB();
|
||||
ASSERT_EQ(versions_->min_log_number_to_keep_2pc(), kMinWalNumberToKeep2PC);
|
||||
ASSERT_EQ(versions_->min_log_number_to_keep(), kMinWalNumberToKeep2PC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. 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 "db/version_set.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Instead of opening a `DB` to perform certain manifest updates, this
|
||||
// uses the underlying `VersionSet` API to read and modify the MANIFEST. This
|
||||
// allows us to use the user's real options, while not having to worry about
|
||||
// the DB persisting new SST files via flush/compaction or attempting to read/
|
||||
// compact files which may fail, particularly for the file we intend to remove
|
||||
// (the user may want to remove an already deleted file from MANIFEST).
|
||||
class OfflineManifestWriter {
|
||||
public:
|
||||
OfflineManifestWriter(const DBOptions& options, const std::string& db_path)
|
||||
: wc_(options.delayed_write_rate),
|
||||
wb_(options.db_write_buffer_size),
|
||||
immutable_db_options_(WithDbPath(options, db_path)),
|
||||
tc_(NewLRUCache(1 << 20 /* capacity */,
|
||||
options.table_cache_numshardbits)),
|
||||
versions_(db_path, &immutable_db_options_, sopt_, tc_.get(), &wb_, &wc_,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
|
||||
/*db_session_id*/ "") {}
|
||||
|
||||
Status Recover(const std::vector<ColumnFamilyDescriptor>& column_families) {
|
||||
return versions_.Recover(column_families);
|
||||
}
|
||||
|
||||
Status LogAndApply(ColumnFamilyData* cfd, VersionEdit* edit) {
|
||||
// Use `mutex` to imitate a locked DB mutex when calling `LogAndApply()`.
|
||||
InstrumentedMutex mutex;
|
||||
mutex.Lock();
|
||||
Status s = versions_.LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
edit, &mutex, nullptr /* db_directory */,
|
||||
false /* new_descriptor_log */);
|
||||
mutex.Unlock();
|
||||
return s;
|
||||
}
|
||||
|
||||
VersionSet& Versions() { return versions_; }
|
||||
const ImmutableDBOptions& IOptions() { return immutable_db_options_; }
|
||||
|
||||
private:
|
||||
WriteController wc_;
|
||||
WriteBufferManager wb_;
|
||||
ImmutableDBOptions immutable_db_options_;
|
||||
std::shared_ptr<Cache> tc_;
|
||||
EnvOptions sopt_;
|
||||
VersionSet versions_;
|
||||
|
||||
static ImmutableDBOptions WithDbPath(const DBOptions& options,
|
||||
const std::string& db_path) {
|
||||
ImmutableDBOptions rv(options);
|
||||
if (rv.db_paths.empty()) {
|
||||
// `VersionSet` expects options that have been through
|
||||
// `SanitizeOptions()`, which would sanitize an empty `db_paths`.
|
||||
rv.db_paths.emplace_back(db_path, 0 /* target_size */);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -800,7 +800,6 @@ 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),
|
||||
@@ -813,7 +812,6 @@ 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}};
|
||||
@@ -1004,7 +1002,6 @@ 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,
|
||||
@@ -1018,7 +1015,6 @@ 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,
|
||||
@@ -1119,7 +1115,6 @@ 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,
|
||||
@@ -1132,7 +1127,6 @@ 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}};
|
||||
@@ -2307,7 +2301,6 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
assert(db_);
|
||||
|
||||
if (recovering_log_number_ != 0) {
|
||||
db_->mutex()->AssertHeld();
|
||||
// during recovery we rebuild a hollow transaction
|
||||
// from all encountered prepare sections of the wal
|
||||
if (db_->allow_2pc() == false) {
|
||||
@@ -2338,7 +2331,6 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
assert((rebuilding_trx_ != nullptr) == (recovering_log_number_ != 0));
|
||||
|
||||
if (recovering_log_number_ != 0) {
|
||||
db_->mutex()->AssertHeld();
|
||||
assert(db_->allow_2pc());
|
||||
size_t batch_cnt =
|
||||
write_after_commit_
|
||||
@@ -2359,9 +2351,6 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
}
|
||||
|
||||
Status MarkNoop(bool empty_batch) override {
|
||||
if (recovering_log_number_ != 0) {
|
||||
db_->mutex()->AssertHeld();
|
||||
}
|
||||
// A hack in pessimistic transaction could result into a noop at the start
|
||||
// of the write batch, that should be ignored.
|
||||
if (!empty_batch) {
|
||||
|
||||
@@ -228,10 +228,6 @@ 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
|
||||
|
||||
+1
-23
@@ -1009,29 +1009,6 @@ 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) {
|
||||
@@ -1040,6 +1017,7 @@ 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,11 +471,6 @@ 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,7 +117,6 @@ 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;
|
||||
@@ -142,7 +141,6 @@ 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),
|
||||
@@ -165,7 +163,6 @@ 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()),
|
||||
|
||||
@@ -115,7 +115,6 @@ DECLARE_int32(level0_stop_writes_trigger);
|
||||
DECLARE_int32(block_size);
|
||||
DECLARE_int32(format_version);
|
||||
DECLARE_int32(index_block_restart_interval);
|
||||
DECLARE_bool(disable_auto_compactions);
|
||||
DECLARE_int32(max_background_compactions);
|
||||
DECLARE_int32(num_bottom_pri_threads);
|
||||
DECLARE_int32(compaction_thread_pool_adjust_interval);
|
||||
@@ -177,7 +176,6 @@ 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);
|
||||
@@ -275,13 +273,6 @@ DECLARE_int32(secondary_cache_fault_one_in);
|
||||
|
||||
DECLARE_int32(prepopulate_block_cache);
|
||||
|
||||
DECLARE_bool(two_write_queues);
|
||||
#ifndef ROCKSDB_LITE
|
||||
DECLARE_bool(use_only_the_last_commit_time_batch_for_recovery);
|
||||
DECLARE_uint64(wp_snapshot_cache_bits);
|
||||
DECLARE_uint64(wp_commit_cache_bits);
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
constexpr long KB = 1024;
|
||||
constexpr int kRandomValueMaxFactor = 3;
|
||||
constexpr int kValueMaxLen = 100;
|
||||
|
||||
@@ -225,10 +225,6 @@ DEFINE_int32(
|
||||
"Number of keys between restart points "
|
||||
"for delta encoding of keys in index block.");
|
||||
|
||||
DEFINE_bool(disable_auto_compactions,
|
||||
ROCKSDB_NAMESPACE::Options().disable_auto_compactions,
|
||||
"If true, RocksDB internally will not trigger compactions.");
|
||||
|
||||
DEFINE_int32(max_background_compactions,
|
||||
ROCKSDB_NAMESPACE::Options().max_background_compactions,
|
||||
"The maximum number of concurrent background compactions "
|
||||
@@ -554,12 +550,6 @@ 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.");
|
||||
@@ -894,22 +884,4 @@ DEFINE_int32(prepopulate_block_cache,
|
||||
"Options related to cache warming (see `enum "
|
||||
"PrepopulateBlockCache` in table.h)");
|
||||
|
||||
DEFINE_bool(two_write_queues, false,
|
||||
"Set to true to enable two write queues. Default: false");
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
DEFINE_bool(use_only_the_last_commit_time_batch_for_recovery, false,
|
||||
"If true, the commit-time write batch will not be immediately "
|
||||
"inserted into the memtables. Default: false");
|
||||
|
||||
DEFINE_uint64(
|
||||
wp_snapshot_cache_bits, 7ull,
|
||||
"Number of bits to represent write-prepared transaction db's snapshot "
|
||||
"cache. Default: 7 (128 entries)");
|
||||
|
||||
DEFINE_uint64(wp_commit_cache_bits, 23ull,
|
||||
"Number of bits to represent write-prepared transaction db's "
|
||||
"commit cache. Default: 23 (8M entries)");
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
#endif // GFLAGS
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "file/file_util.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "util/coding_lean.h"
|
||||
|
||||
@@ -38,31 +37,10 @@ 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(tmp_path, FileOptions(), &reader,
|
||||
/*dbg*/ nullptr);
|
||||
Status s =
|
||||
fs->NewSequentialFile(path_, FileOptions(), &reader, /*dbg*/ nullptr);
|
||||
if (s.ok()) {
|
||||
// Load from file
|
||||
std::string id(24U, '\0');
|
||||
@@ -82,57 +60,37 @@ 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(tmp_path, opts, /*dbg*/ nullptr);
|
||||
s = fs->DeleteFile(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(tmp_path, opts, /*dbg*/ nullptr);
|
||||
Status s2 = fs->FileExists(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());
|
||||
|
||||
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());
|
||||
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());
|
||||
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() {
|
||||
IOStatus s = data_file_writer_->Close();
|
||||
assert(s.ok());
|
||||
data_file_writer_->Close(IOOptions(), /*dbg*/ nullptr);
|
||||
}
|
||||
|
||||
void UniqueIdVerifier::VerifyNoWrite(const std::string& id) {
|
||||
@@ -154,13 +112,14 @@ void UniqueIdVerifier::Verify(const std::string& id) {
|
||||
if (id_set_.size() >= 4294967) {
|
||||
return;
|
||||
}
|
||||
IOStatus s = data_file_writer_->Append(Slice(id));
|
||||
IOStatus s =
|
||||
data_file_writer_->Append(Slice(id), IOOptions(), /*dbg*/ nullptr);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "Error writing to unique id file: %s\n",
|
||||
s.ToString().c_str());
|
||||
assert(false);
|
||||
}
|
||||
s = data_file_writer_->Flush();
|
||||
s = data_file_writer_->Flush(IOOptions(), /*dbg*/ nullptr);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "Error flushing unique id file: %s\n",
|
||||
s.ToString().c_str());
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#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"
|
||||
@@ -40,7 +39,7 @@ class UniqueIdVerifier {
|
||||
std::mutex mutex_;
|
||||
// IDs persisted to a hidden file inside DB dir
|
||||
std::string path_;
|
||||
std::unique_ptr<WritableFileWriter> data_file_writer_;
|
||||
std::unique_ptr<FSWritableFile> 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,9 +485,6 @@ 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;
|
||||
@@ -604,11 +601,8 @@ Status StressTest::NewTxn(WriteOptions& write_opts, Transaction** txn) {
|
||||
if (!FLAGS_use_txn) {
|
||||
return Status::InvalidArgument("NewTxn when FLAGS_use_txn is not set");
|
||||
}
|
||||
write_opts.disableWAL = FLAGS_disable_wal;
|
||||
static std::atomic<uint64_t> txn_id = {0};
|
||||
TransactionOptions txn_options;
|
||||
txn_options.use_only_the_last_commit_time_batch_for_recovery =
|
||||
FLAGS_use_only_the_last_commit_time_batch_for_recovery;
|
||||
txn_options.lock_timeout = 600000; // 10 min
|
||||
txn_options.deadlock_detect = true;
|
||||
*txn = txn_db_->BeginTransaction(write_opts, txn_options);
|
||||
@@ -646,9 +640,6 @@ 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;
|
||||
@@ -1451,11 +1442,6 @@ Status StressTest::TestBackupRestore(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (thread->rand.OneIn(2)) {
|
||||
backup_opts.schema_version = 1;
|
||||
} else {
|
||||
backup_opts.schema_version = 2;
|
||||
}
|
||||
BackupEngine* backup_engine = nullptr;
|
||||
std::string from = "a backup/restore operation";
|
||||
Status s = BackupEngine::Open(db_stress_env, backup_opts, &backup_engine);
|
||||
@@ -1463,11 +1449,11 @@ Status StressTest::TestBackupRestore(
|
||||
from = "BackupEngine::Open";
|
||||
}
|
||||
if (s.ok()) {
|
||||
if (backup_opts.schema_version >= 2 && thread->rand.OneIn(2)) {
|
||||
TEST_BackupMetaSchemaOptions test_opts;
|
||||
if (thread->rand.OneIn(2)) {
|
||||
TEST_FutureSchemaVersion2Options test_opts;
|
||||
test_opts.crc32c_checksums = thread->rand.OneIn(2) == 0;
|
||||
test_opts.file_sizes = thread->rand.OneIn(2) == 0;
|
||||
TEST_SetBackupMetaSchemaOptions(backup_engine, test_opts);
|
||||
TEST_EnableWriteFutureSchemaVersion2(backup_engine, test_opts);
|
||||
}
|
||||
CreateBackupOptions create_opts;
|
||||
if (FLAGS_disable_wal) {
|
||||
@@ -1758,8 +1744,6 @@ 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
|
||||
@@ -2161,28 +2145,6 @@ void StressTest::PrintEnv() const {
|
||||
fprintf(stdout, "Format version : %d\n", FLAGS_format_version);
|
||||
fprintf(stdout, "TransactionDB : %s\n",
|
||||
FLAGS_use_txn ? "true" : "false");
|
||||
|
||||
if (FLAGS_use_txn) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
fprintf(stdout, "Two write queues: : %s\n",
|
||||
FLAGS_two_write_queues ? "true" : "false");
|
||||
fprintf(stdout, "Write policy : %d\n",
|
||||
static_cast<int>(FLAGS_txn_write_policy));
|
||||
if (static_cast<uint64_t>(TxnDBWritePolicy::WRITE_PREPARED) ==
|
||||
FLAGS_txn_write_policy ||
|
||||
static_cast<uint64_t>(TxnDBWritePolicy::WRITE_UNPREPARED) ==
|
||||
FLAGS_txn_write_policy) {
|
||||
fprintf(stdout, "Snapshot cache bits : %d\n",
|
||||
static_cast<int>(FLAGS_wp_snapshot_cache_bits));
|
||||
fprintf(stdout, "Commit cache bits : %d\n",
|
||||
static_cast<int>(FLAGS_wp_commit_cache_bits));
|
||||
}
|
||||
fprintf(stdout, "last cwb for recovery : %s\n",
|
||||
FLAGS_use_only_the_last_commit_time_batch_for_recovery ? "true"
|
||||
: "false");
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
fprintf(stdout, "Stacked BlobDB : %s\n",
|
||||
FLAGS_use_blob_db ? "true" : "false");
|
||||
@@ -2346,7 +2308,6 @@ void StressTest::Open() {
|
||||
options_.memtable_prefix_bloom_size_ratio =
|
||||
FLAGS_memtable_prefix_bloom_size_ratio;
|
||||
options_.memtable_whole_key_filtering = FLAGS_memtable_whole_key_filtering;
|
||||
options_.disable_auto_compactions = FLAGS_disable_auto_compactions;
|
||||
options_.max_background_compactions = FLAGS_max_background_compactions;
|
||||
options_.max_background_flushes = FLAGS_max_background_flushes;
|
||||
options_.compaction_style =
|
||||
@@ -2735,13 +2696,7 @@ void StressTest::Open() {
|
||||
options_.unordered_write = true;
|
||||
options_.two_write_queues = true;
|
||||
txn_db_options.skip_concurrency_control = true;
|
||||
} else {
|
||||
options_.two_write_queues = FLAGS_two_write_queues;
|
||||
}
|
||||
txn_db_options.wp_snapshot_cache_bits =
|
||||
static_cast<size_t>(FLAGS_wp_snapshot_cache_bits);
|
||||
txn_db_options.wp_commit_cache_bits =
|
||||
static_cast<size_t>(FLAGS_wp_commit_cache_bits);
|
||||
s = TransactionDB::Open(options_, txn_db_options, FLAGS_db,
|
||||
cf_descriptors, &column_families_, &txn_db_);
|
||||
if (!s.ok()) {
|
||||
@@ -2799,11 +2754,7 @@ void StressTest::Open() {
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
// Secondary instance does not support write-prepared/write-unprepared
|
||||
// transactions, thus just disable secondary instance if we use
|
||||
// transaction.
|
||||
if (s.ok() && FLAGS_continuous_verification_interval > 0 &&
|
||||
!FLAGS_use_txn && !cmp_db_) {
|
||||
if (s.ok() && FLAGS_continuous_verification_interval > 0 && !cmp_db_) {
|
||||
Options tmp_opts;
|
||||
// TODO(yanqin) support max_open_files != -1 for secondary instance.
|
||||
tmp_opts.max_open_files = -1;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -196,7 +196,7 @@ class MultiOpsTxnsStressTest : public StressTest {
|
||||
|
||||
void FinishInitDb(SharedState*) override;
|
||||
|
||||
void ReopenAndPreloadDbIfNeeded(SharedState* shared);
|
||||
void ReopenAndPreloadDb(SharedState* shared);
|
||||
|
||||
bool IsStateTracked() const override { return false; }
|
||||
|
||||
@@ -262,10 +262,10 @@ class MultiOpsTxnsStressTest : public StressTest {
|
||||
const std::vector<int>& rand_column_families) override;
|
||||
|
||||
Status PrimaryKeyUpdateTxn(ThreadState* thread, uint32_t old_a,
|
||||
uint32_t old_a_pos, uint32_t new_a);
|
||||
uint32_t new_a);
|
||||
|
||||
Status SecondaryKeyUpdateTxn(ThreadState* thread, uint32_t old_c,
|
||||
uint32_t old_c_pos, uint32_t new_c);
|
||||
uint32_t new_c);
|
||||
|
||||
Status UpdatePrimaryIndexValueTxn(ThreadState* thread, uint32_t a,
|
||||
uint32_t b_delta);
|
||||
@@ -276,88 +276,16 @@ class MultiOpsTxnsStressTest : public StressTest {
|
||||
|
||||
void VerifyDb(ThreadState* thread) const override;
|
||||
|
||||
void ContinuouslyVerifyDb(ThreadState* thread) const override {
|
||||
VerifyDb(thread);
|
||||
}
|
||||
|
||||
protected:
|
||||
using KeySet = std::set<uint32_t>;
|
||||
class KeyGenerator {
|
||||
public:
|
||||
explicit KeyGenerator(uint32_t s, uint32_t low, uint32_t high,
|
||||
KeySet&& existing_uniq, KeySet&& non_existing_uniq)
|
||||
: rand_(s),
|
||||
low_(low),
|
||||
high_(high),
|
||||
existing_uniq_(std::move(existing_uniq)),
|
||||
non_existing_uniq_(std::move(non_existing_uniq)) {}
|
||||
~KeyGenerator() {
|
||||
assert(!existing_uniq_.empty());
|
||||
assert(!non_existing_uniq_.empty());
|
||||
}
|
||||
void FinishInit();
|
||||
uint32_t ChooseA(ThreadState* thread);
|
||||
|
||||
std::pair<uint32_t, uint32_t> ChooseExisting();
|
||||
void Replace(uint32_t old_val, uint32_t old_pos, uint32_t new_val);
|
||||
uint32_t Allocate();
|
||||
void UndoAllocation(uint32_t new_val);
|
||||
|
||||
std::string ToString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "[" << low_ << ", " << high_ << "): " << existing_.size()
|
||||
<< " elements, " << existing_uniq_.size() << " unique values, "
|
||||
<< non_existing_uniq_.size() << " unique non-existing values";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
Random rand_;
|
||||
uint32_t low_ = 0;
|
||||
uint32_t high_ = 0;
|
||||
std::vector<uint32_t> existing_{};
|
||||
KeySet existing_uniq_{};
|
||||
KeySet non_existing_uniq_{};
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
// Return <a, pos>
|
||||
std::pair<uint32_t, uint32_t> ChooseExistingA(ThreadState* thread);
|
||||
|
||||
uint32_t GenerateNextA(ThreadState* thread);
|
||||
|
||||
// Return <c, pos>
|
||||
std::pair<uint32_t, uint32_t> ChooseExistingC(ThreadState* thread);
|
||||
|
||||
uint32_t GenerateNextC(ThreadState* thread);
|
||||
|
||||
std::vector<std::unique_ptr<KeyGenerator>> key_gen_for_a_;
|
||||
std::vector<std::unique_ptr<KeyGenerator>> key_gen_for_c_;
|
||||
uint32_t GenerateNextA();
|
||||
|
||||
private:
|
||||
struct KeySpaces {
|
||||
uint32_t lb_a = 0;
|
||||
uint32_t ub_a = 0;
|
||||
uint32_t lb_c = 0;
|
||||
uint32_t ub_c = 0;
|
||||
void PreloadDb(SharedState* shared, size_t num_c);
|
||||
|
||||
explicit KeySpaces() = default;
|
||||
explicit KeySpaces(uint32_t _lb_a, uint32_t _ub_a, uint32_t _lb_c,
|
||||
uint32_t _ub_c)
|
||||
: lb_a(_lb_a), ub_a(_ub_a), lb_c(_lb_c), ub_c(_ub_c) {}
|
||||
|
||||
std::string EncodeTo() const;
|
||||
bool DecodeFrom(Slice data);
|
||||
};
|
||||
|
||||
void PersistKeySpacesDesc(const std::string& key_spaces_path, uint32_t lb_a,
|
||||
uint32_t ub_a, uint32_t lb_c, uint32_t ub_c);
|
||||
|
||||
KeySpaces ReadKeySpacesDesc(const std::string& key_spaces_path);
|
||||
|
||||
void PreloadDb(SharedState* shared, int threads, uint32_t lb_a, uint32_t ub_a,
|
||||
uint32_t lb_c, uint32_t ub_c);
|
||||
|
||||
void ScanExistingDb(SharedState* shared, int threads);
|
||||
// TODO (yanqin) encapsulate the selection of keys a separate class.
|
||||
std::atomic<uint32_t> next_a_{0};
|
||||
};
|
||||
|
||||
class InvariantChecker {
|
||||
|
||||
@@ -271,9 +271,6 @@ 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', '~> 225'
|
||||
gem 'github-pages', '~> 209'
|
||||
|
||||
gem "webrick", "~> 1.7"
|
||||
|
||||
+92
-110
@@ -1,7 +1,7 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (6.0.4.6)
|
||||
activesupport (6.0.3.4)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
@@ -14,78 +14,60 @@ GEM
|
||||
execjs
|
||||
coffee-script-source (1.11.1)
|
||||
colorator (1.1.0)
|
||||
commonmarker (0.23.4)
|
||||
concurrent-ruby (1.1.9)
|
||||
dnsruby (1.61.9)
|
||||
commonmarker (0.17.13)
|
||||
ruby-enum (~> 0.5)
|
||||
concurrent-ruby (1.1.7)
|
||||
dnsruby (1.61.5)
|
||||
simpleidn (~> 0.1)
|
||||
em-websocket (0.5.3)
|
||||
em-websocket (0.5.2)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0)
|
||||
ethon (0.15.0)
|
||||
ffi (>= 1.15.0)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
ethon (0.12.0)
|
||||
ffi (>= 1.3.0)
|
||||
eventmachine (1.2.7)
|
||||
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)
|
||||
execjs (2.7.0)
|
||||
faraday (1.3.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)
|
||||
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)
|
||||
ruby2_keywords
|
||||
faraday-net_http (1.0.0)
|
||||
ffi (1.14.2)
|
||||
forwardable-extended (2.6.0)
|
||||
gemoji (3.0.1)
|
||||
github-pages (225)
|
||||
github-pages-health-check (= 1.17.9)
|
||||
github-pages (209)
|
||||
github-pages-health-check (= 1.16.1)
|
||||
jekyll (= 3.9.0)
|
||||
jekyll-avatar (= 0.7.0)
|
||||
jekyll-coffeescript (= 1.1.1)
|
||||
jekyll-commonmark-ghpages (= 0.2.0)
|
||||
jekyll-commonmark-ghpages (= 0.1.6)
|
||||
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.3)
|
||||
jekyll-remote-theme (= 0.4.2)
|
||||
jekyll-sass-converter (= 1.5.2)
|
||||
jekyll-seo-tag (= 2.8.0)
|
||||
jekyll-seo-tag (= 2.6.1)
|
||||
jekyll-sitemap (= 1.4.0)
|
||||
jekyll-swiss (= 1.0.0)
|
||||
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-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-titles-from-headings (= 0.5.3)
|
||||
jemoji (= 0.12.0)
|
||||
kramdown (= 2.3.1)
|
||||
@@ -93,19 +75,19 @@ GEM
|
||||
liquid (= 4.0.3)
|
||||
mercenary (~> 0.3)
|
||||
minima (= 2.5.1)
|
||||
nokogiri (>= 1.12.5, < 2.0)
|
||||
rouge (= 3.26.0)
|
||||
nokogiri (>= 1.10.4, < 2.0)
|
||||
rouge (= 3.23.0)
|
||||
terminal-table (~> 1.4)
|
||||
github-pages-health-check (1.17.9)
|
||||
github-pages-health-check (1.16.1)
|
||||
addressable (~> 2.3)
|
||||
dnsruby (~> 1.60)
|
||||
octokit (~> 4.0)
|
||||
public_suffix (>= 3.0, < 5.0)
|
||||
public_suffix (~> 3.0)
|
||||
typhoeus (~> 1.3)
|
||||
html-pipeline (2.14.0)
|
||||
activesupport (>= 2)
|
||||
nokogiri (>= 1.4)
|
||||
http_parser.rb (0.8.0)
|
||||
http_parser.rb (0.6.0)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jekyll (3.9.0)
|
||||
@@ -126,12 +108,12 @@ GEM
|
||||
jekyll-coffeescript (1.1.1)
|
||||
coffee-script (~> 2.2)
|
||||
coffee-script-source (~> 1.11.1)
|
||||
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)
|
||||
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)
|
||||
rouge (>= 2.0, < 4.0)
|
||||
jekyll-default-layout (0.1.4)
|
||||
jekyll (~> 3.0)
|
||||
@@ -142,8 +124,6 @@ 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)
|
||||
@@ -156,57 +136,57 @@ GEM
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-relative-links (0.6.1)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-remote-theme (0.4.3)
|
||||
jekyll-remote-theme (0.4.2)
|
||||
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.8.0)
|
||||
jekyll (>= 3.8, < 5.0)
|
||||
jekyll-seo-tag (2.6.1)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-sitemap (1.4.0)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-swiss (1.0.0)
|
||||
jekyll-theme-architect (0.2.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 (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-cayman (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-theme-leap-day (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-dinky (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-theme-merlot (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-hacker (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-theme-midnight (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-leap-day (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-theme-minimal (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-merlot (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-theme-modernist (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
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-theme-primer (0.5.4)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-github-metadata (~> 2.9)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-slate (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-theme-slate (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-tactile (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-theme-tactile (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-time-machine (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-theme-time-machine (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-titles-from-headings (0.5.3)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
@@ -221,34 +201,36 @@ GEM
|
||||
kramdown-parser-gfm (1.1.0)
|
||||
kramdown (~> 2.0)
|
||||
liquid (4.0.3)
|
||||
listen (3.7.1)
|
||||
listen (3.4.0)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
mercenary (0.3.6)
|
||||
mini_portile2 (2.8.0)
|
||||
mini_portile2 (2.6.1)
|
||||
minima (2.5.1)
|
||||
jekyll (>= 3.5, < 5.0)
|
||||
jekyll-feed (~> 0.9)
|
||||
jekyll-seo-tag (~> 2.1)
|
||||
minitest (5.15.0)
|
||||
minitest (5.14.3)
|
||||
multipart-post (2.1.1)
|
||||
nokogiri (1.13.3)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
nokogiri (1.12.5)
|
||||
mini_portile2 (~> 2.6.1)
|
||||
racc (~> 1.4)
|
||||
octokit (4.22.0)
|
||||
octokit (4.20.0)
|
||||
faraday (>= 0.9)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (4.0.6)
|
||||
racc (1.6.0)
|
||||
rb-fsevent (0.11.1)
|
||||
public_suffix (3.1.1)
|
||||
racc (1.5.2)
|
||||
rb-fsevent (0.10.4)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rexml (3.2.5)
|
||||
rouge (3.26.0)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
rouge (3.23.0)
|
||||
ruby-enum (0.8.0)
|
||||
i18n
|
||||
ruby2_keywords (0.0.2)
|
||||
rubyzip (2.3.0)
|
||||
safe_yaml (1.0.5)
|
||||
sass (3.7.4)
|
||||
sass-listen (~> 4.0.0)
|
||||
@@ -258,7 +240,7 @@ GEM
|
||||
sawyer (0.8.2)
|
||||
addressable (>= 2.3.5)
|
||||
faraday (> 0.8, < 2.0)
|
||||
simpleidn (0.2.1)
|
||||
simpleidn (0.1.1)
|
||||
unf (~> 0.1.4)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
@@ -269,16 +251,16 @@ GEM
|
||||
thread_safe (~> 0.1)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8)
|
||||
unicode-display_width (1.8.0)
|
||||
unf_ext (0.0.7.7)
|
||||
unicode-display_width (1.7.0)
|
||||
webrick (1.7.0)
|
||||
zeitwerk (2.5.4)
|
||||
zeitwerk (2.4.2)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
github-pages (~> 225)
|
||||
github-pages (~> 209)
|
||||
webrick (~> 1.7)
|
||||
|
||||
BUNDLED WITH
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
<!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,13 +147,3 @@ 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
+2
-201
@@ -1256,7 +1256,7 @@ TEST_P(EnvPosixTestWithParam, MultiRead) {
|
||||
// Random Read
|
||||
Random rnd(301 + attempt);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UpdateResults:io_uring_result", [&](void* arg) {
|
||||
"PosixRandomAccessFile::MultiRead:io_uring_result", [&](void* arg) {
|
||||
if (attempt > 0) {
|
||||
// No failure in the first attempt.
|
||||
size_t& bytes_read = *static_cast<size_t*>(arg);
|
||||
@@ -1326,7 +1326,7 @@ TEST_F(EnvPosixTest, MultiReadNonAlignedLargeNum) {
|
||||
const int num_reads = rnd.Uniform(512) + 1;
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UpdateResults:io_uring_result", [&](void* arg) {
|
||||
"PosixRandomAccessFile::MultiRead:io_uring_result", [&](void* arg) {
|
||||
if (attempt > 5) {
|
||||
// Improve partial result rates in second half of the run to
|
||||
// cover the case of repeated partial results.
|
||||
@@ -3110,205 +3110,6 @@ 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, dbg, create_io_error, this](FSReadRequest _req) {
|
||||
if (!create_io_error) {
|
||||
_req.status = target()->Read(_req.offset, _req.len, opts,
|
||||
&(_req.result), _req.scratch, dbg);
|
||||
cb(_req, cb_arg);
|
||||
}
|
||||
};
|
||||
|
||||
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
-70
@@ -1043,76 +1043,6 @@ class PosixFileSystem : public FileSystem {
|
||||
}
|
||||
#endif // ROCKSDB_IOURING_PRESENT
|
||||
|
||||
// EXPERIMENTAL
|
||||
//
|
||||
// TODO akankshamahajan: Update Poll API to take into account min_completions
|
||||
// and returns if number of handles in io_handles (any order) completed is
|
||||
// equal to atleast min_completions.
|
||||
virtual IOStatus Poll(std::vector<void*>& io_handles,
|
||||
size_t /*min_completions*/) override {
|
||||
#if defined(ROCKSDB_IOURING_PRESENT)
|
||||
// io_uring_queue_init.
|
||||
struct io_uring* iu = nullptr;
|
||||
if (thread_local_io_urings_) {
|
||||
iu = static_cast<struct io_uring*>(thread_local_io_urings_->Get());
|
||||
}
|
||||
|
||||
// Init failed, platform doesn't support io_uring.
|
||||
if (iu == nullptr) {
|
||||
return IOStatus::NotSupported("Poll");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < io_handles.size(); i++) {
|
||||
// The request has been completed in earlier runs.
|
||||
if ((static_cast<Posix_IOHandle*>(io_handles[i]))->is_finished) {
|
||||
continue;
|
||||
}
|
||||
// Loop until IO for io_handles[i] is completed.
|
||||
while (true) {
|
||||
// io_uring_wait_cqe.
|
||||
struct io_uring_cqe* cqe = nullptr;
|
||||
ssize_t ret = io_uring_wait_cqe(iu, &cqe);
|
||||
if (ret) {
|
||||
// abort as it shouldn't be in indeterminate state and there is no
|
||||
// good way currently to handle this error.
|
||||
abort();
|
||||
}
|
||||
|
||||
// Step 3: Populate the request.
|
||||
assert(cqe != nullptr);
|
||||
Posix_IOHandle* posix_handle =
|
||||
static_cast<Posix_IOHandle*>(io_uring_cqe_get_data(cqe));
|
||||
assert(posix_handle->iu == iu);
|
||||
if (posix_handle->iu != iu) {
|
||||
return IOStatus::IOError("");
|
||||
}
|
||||
// Reset cqe data to catch any stray reuse of it
|
||||
static_cast<struct io_uring_cqe*>(cqe)->user_data = 0xd5d5d5d5d5d5d5d5;
|
||||
|
||||
FSReadRequest req;
|
||||
req.scratch = posix_handle->scratch;
|
||||
req.offset = posix_handle->offset;
|
||||
req.len = posix_handle->len;
|
||||
size_t finished_len = 0;
|
||||
UpdateResult(cqe, "", req.len, posix_handle->iov.iov_len,
|
||||
true /*async_read*/, finished_len, &req);
|
||||
posix_handle->is_finished = true;
|
||||
io_uring_cqe_seen(iu, cqe);
|
||||
posix_handle->cb(req, posix_handle->cb_arg);
|
||||
(void)finished_len;
|
||||
|
||||
if (static_cast<Posix_IOHandle*>(io_handles[i]) == posix_handle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return IOStatus::OK();
|
||||
#else
|
||||
(void)io_handles;
|
||||
return IOStatus::NotSupported("Poll");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ROCKSDB_IOURING_PRESENT)
|
||||
// io_uring instance
|
||||
std::unique_ptr<ThreadLocalPtr> thread_local_io_urings_;
|
||||
|
||||
Vendored
+3
-36
@@ -110,45 +110,12 @@ 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;
|
||||
}
|
||||
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;
|
||||
return FileSystemWrapper::NewDirectory(status_and_enc_path.second, options,
|
||||
result, dbg);
|
||||
}
|
||||
|
||||
IOStatus RemapFileSystem::FileExists(const std::string& fname,
|
||||
@@ -326,7 +293,7 @@ IOStatus RemapFileSystem::GetAbsolutePath(const std::string& db_path,
|
||||
const IOOptions& options,
|
||||
std::string* output_path,
|
||||
IODebugContext* dbg) {
|
||||
auto status_and_enc_path = EncodePathWithNewBasename(db_path);
|
||||
auto status_and_enc_path = EncodePath(db_path);
|
||||
if (!status_and_enc_path.first.ok()) {
|
||||
return status_and_enc_path.first;
|
||||
}
|
||||
|
||||
Vendored
+38
-96
@@ -744,31 +744,47 @@ IOStatus PosixRandomAccessFile::MultiRead(FSReadRequest* reqs,
|
||||
wrap_cache.erase(wrap_check);
|
||||
|
||||
FSReadRequest* req = req_wrap->req;
|
||||
UpdateResult(cqe, filename_, req->len, req_wrap->iov.iov_len,
|
||||
false /*async_read*/, req_wrap->finished_len, req);
|
||||
int32_t res = cqe->res;
|
||||
if (res == 0) {
|
||||
/// cqe->res == 0 can means EOF, or can mean partial results. See
|
||||
// comment
|
||||
// https://github.com/facebook/rocksdb/pull/6441#issuecomment-589843435
|
||||
// Fall back to pread in this case.
|
||||
if (use_direct_io() && !IsSectorAligned(req_wrap->finished_len,
|
||||
GetRequiredBufferAlignment())) {
|
||||
// Bytes reads don't fill sectors. Should only happen at the end
|
||||
// of the file.
|
||||
req->result = Slice(req->scratch, req_wrap->finished_len);
|
||||
if (cqe->res < 0) {
|
||||
req->result = Slice(req->scratch, 0);
|
||||
req->status = IOError("Req failed", filename_, cqe->res);
|
||||
} else {
|
||||
size_t bytes_read = static_cast<size_t>(cqe->res);
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"PosixRandomAccessFile::MultiRead:io_uring_result", &bytes_read);
|
||||
if (bytes_read == req_wrap->iov.iov_len) {
|
||||
req->result = Slice(req->scratch, req->len);
|
||||
req->status = IOStatus::OK();
|
||||
} else if (bytes_read == 0) {
|
||||
// cqe->res == 0 can means EOF, or can mean partial results. See
|
||||
// comment
|
||||
// https://github.com/facebook/rocksdb/pull/6441#issuecomment-589843435
|
||||
// Fall back to pread in this case.
|
||||
if (use_direct_io() &&
|
||||
!IsSectorAligned(req_wrap->finished_len,
|
||||
GetRequiredBufferAlignment())) {
|
||||
// Bytes reads don't fill sectors. Should only happen at the end
|
||||
// of the file.
|
||||
req->result = Slice(req->scratch, req_wrap->finished_len);
|
||||
req->status = IOStatus::OK();
|
||||
} else {
|
||||
Slice tmp_slice;
|
||||
req->status =
|
||||
Read(req->offset + req_wrap->finished_len,
|
||||
req->len - req_wrap->finished_len, options, &tmp_slice,
|
||||
req->scratch + req_wrap->finished_len, dbg);
|
||||
req->result =
|
||||
Slice(req->scratch, req_wrap->finished_len + tmp_slice.size());
|
||||
}
|
||||
} else if (bytes_read < req_wrap->iov.iov_len) {
|
||||
assert(bytes_read > 0);
|
||||
assert(bytes_read + req_wrap->finished_len < req->len);
|
||||
req_wrap->finished_len += bytes_read;
|
||||
incomplete_rq_list.push_back(req_wrap);
|
||||
} else {
|
||||
Slice tmp_slice;
|
||||
req->status =
|
||||
Read(req->offset + req_wrap->finished_len,
|
||||
req->len - req_wrap->finished_len, options, &tmp_slice,
|
||||
req->scratch + req_wrap->finished_len, dbg);
|
||||
req->result =
|
||||
Slice(req->scratch, req_wrap->finished_len + tmp_slice.size());
|
||||
req->result = Slice(req->scratch, 0);
|
||||
req->status = IOError("Req returned more bytes than requested",
|
||||
filename_, cqe->res);
|
||||
}
|
||||
} else if (res > 0 && res < static_cast<int32_t>(req_wrap->iov.iov_len)) {
|
||||
incomplete_rq_list.push_back(req_wrap);
|
||||
}
|
||||
io_uring_cqe_seen(iu, cqe);
|
||||
}
|
||||
@@ -856,80 +872,6 @@ IOStatus PosixRandomAccessFile::InvalidateCache(size_t offset, size_t length) {
|
||||
#endif
|
||||
}
|
||||
|
||||
IOStatus PosixRandomAccessFile::ReadAsync(
|
||||
FSReadRequest& req, const IOOptions& /*opts*/,
|
||||
std::function<void(const FSReadRequest&, void*)> cb, void* cb_arg,
|
||||
void** io_handle, IOHandleDeleter* del_fn, IODebugContext* /*dbg*/) {
|
||||
if (use_direct_io()) {
|
||||
assert(IsSectorAligned(req.offset, GetRequiredBufferAlignment()));
|
||||
assert(IsSectorAligned(req.len, GetRequiredBufferAlignment()));
|
||||
assert(IsSectorAligned(req.scratch, GetRequiredBufferAlignment()));
|
||||
}
|
||||
|
||||
#if defined(ROCKSDB_IOURING_PRESENT)
|
||||
// io_uring_queue_init.
|
||||
struct io_uring* iu = nullptr;
|
||||
if (thread_local_io_urings_) {
|
||||
iu = static_cast<struct io_uring*>(thread_local_io_urings_->Get());
|
||||
if (iu == nullptr) {
|
||||
iu = CreateIOUring();
|
||||
if (iu != nullptr) {
|
||||
thread_local_io_urings_->Reset(iu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Init failed, platform doesn't support io_uring.
|
||||
if (iu == nullptr) {
|
||||
return IOStatus::NotSupported("ReadAsync");
|
||||
}
|
||||
|
||||
// Allocate io_handle.
|
||||
IOHandleDeleter deletefn = [](void* args) -> void {
|
||||
delete (static_cast<Posix_IOHandle*>(args));
|
||||
args = nullptr;
|
||||
};
|
||||
|
||||
Posix_IOHandle* posix_handle = new Posix_IOHandle();
|
||||
*io_handle = static_cast<void*>(posix_handle);
|
||||
*del_fn = deletefn;
|
||||
|
||||
// Initialize Posix_IOHandle.
|
||||
posix_handle->iu = iu;
|
||||
posix_handle->iov.iov_base = posix_handle->scratch;
|
||||
posix_handle->iov.iov_len = posix_handle->len;
|
||||
posix_handle->cb = cb;
|
||||
posix_handle->cb_arg = cb_arg;
|
||||
posix_handle->offset = req.offset;
|
||||
posix_handle->len = req.len;
|
||||
posix_handle->scratch = req.scratch;
|
||||
|
||||
// Step 3: io_uring_sqe_set_data
|
||||
struct io_uring_sqe* sqe;
|
||||
sqe = io_uring_get_sqe(iu);
|
||||
|
||||
io_uring_prep_readv(sqe, fd_, &posix_handle->iov, 1, posix_handle->offset);
|
||||
|
||||
io_uring_sqe_set_data(sqe, posix_handle);
|
||||
|
||||
// Step 4: io_uring_submit
|
||||
ssize_t ret = io_uring_submit(iu);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "io_uring_submit error: %ld\n", long(ret));
|
||||
return IOStatus::IOError("io_uring_submit() requested but returned " +
|
||||
ToString(ret));
|
||||
}
|
||||
return IOStatus::OK();
|
||||
#else
|
||||
(void)req;
|
||||
(void)cb;
|
||||
(void)cb_arg;
|
||||
(void)io_handle;
|
||||
(void)del_fn;
|
||||
return IOStatus::NotSupported("ReadAsync");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* PosixMmapReadableFile
|
||||
*
|
||||
|
||||
Vendored
-59
@@ -13,17 +13,14 @@
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/io_status.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
@@ -52,57 +49,6 @@ class PosixHelper {
|
||||
size_t* size);
|
||||
};
|
||||
|
||||
#if defined(ROCKSDB_IOURING_PRESENT)
|
||||
struct Posix_IOHandle {
|
||||
struct iovec iov;
|
||||
struct io_uring* iu;
|
||||
std::function<void(const FSReadRequest&, void*)> cb;
|
||||
void* cb_arg;
|
||||
uint64_t offset;
|
||||
size_t len;
|
||||
char* scratch;
|
||||
bool is_finished = false;
|
||||
};
|
||||
|
||||
inline void UpdateResult(struct io_uring_cqe* cqe, const std::string& file_name,
|
||||
size_t len, size_t iov_len, bool async_read,
|
||||
size_t& finished_len, FSReadRequest* req) {
|
||||
if (cqe->res < 0) {
|
||||
req->result = Slice(req->scratch, 0);
|
||||
req->status = IOError("Req failed", file_name, cqe->res);
|
||||
} else {
|
||||
size_t bytes_read = static_cast<size_t>(cqe->res);
|
||||
TEST_SYNC_POINT_CALLBACK("UpdateResults::io_uring_result", &bytes_read);
|
||||
if (bytes_read == iov_len) {
|
||||
req->result = Slice(req->scratch, req->len);
|
||||
req->status = IOStatus::OK();
|
||||
} else if (bytes_read == 0) {
|
||||
if (async_read) {
|
||||
// No bytes read. It can means EOF.
|
||||
req->result = Slice(req->scratch, 0);
|
||||
req->status = IOStatus::OK();
|
||||
}
|
||||
} else if (bytes_read < iov_len) {
|
||||
assert(bytes_read > 0);
|
||||
if (async_read) {
|
||||
req->result = Slice(req->scratch, bytes_read);
|
||||
req->status = IOStatus::OK();
|
||||
} else {
|
||||
assert(bytes_read + finished_len < len);
|
||||
finished_len += bytes_read;
|
||||
}
|
||||
} else {
|
||||
req->result = Slice(req->scratch, 0);
|
||||
req->status = IOError("Req returned more bytes than requested", file_name,
|
||||
cqe->res);
|
||||
}
|
||||
}
|
||||
#ifdef NDEBUG
|
||||
(void)len;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef OS_LINUX
|
||||
// Files under a specific directory have the same logical block size.
|
||||
// This class caches the logical block size for the specified directories to
|
||||
@@ -264,11 +210,6 @@ class PosixRandomAccessFile : public FSRandomAccessFile {
|
||||
virtual size_t GetRequiredBufferAlignment() const override {
|
||||
return logical_sector_size_;
|
||||
}
|
||||
// EXPERIMENTAL
|
||||
virtual 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;
|
||||
};
|
||||
|
||||
class PosixWritableFile : public FSWritableFile {
|
||||
|
||||
@@ -139,4 +139,5 @@
|
||||
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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user