mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
131
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33ce358130 | ||
|
|
351d212777 | ||
|
|
668067e0bf | ||
|
|
463f9fd9f2 | ||
|
|
ca5d60fd69 | ||
|
|
c50a2b68bb | ||
|
|
fe68fbcd7f | ||
|
|
57ff2b2492 | ||
|
|
551ba21e9b | ||
|
|
9967c3255d | ||
|
|
2850ccb96b | ||
|
|
3bb3142b7e | ||
|
|
6e4113e92d | ||
|
|
0be850a000 | ||
|
|
e46972d7a4 | ||
|
|
b09e27b207 | ||
|
|
0788cb8a80 | ||
|
|
768ef1fad4 | ||
|
|
f6841d1e68 | ||
|
|
60a0172096 | ||
|
|
b7cd1fd662 | ||
|
|
6c267a3217 | ||
|
|
f9f7ad702c | ||
|
|
83b99db98a | ||
|
|
988357696d | ||
|
|
e929bde2bf | ||
|
|
9a64ebde0c | ||
|
|
11a259a5f0 | ||
|
|
29c65d8bff | ||
|
|
9758482360 | ||
|
|
3381e4d787 | ||
|
|
805ac7c887 | ||
|
|
ab6ba62eb1 | ||
|
|
f081d145cf | ||
|
|
4f7d3a0cb2 | ||
|
|
3cc76aae83 | ||
|
|
80c9eec6b6 | ||
|
|
7183422b17 | ||
|
|
ca2413545c | ||
|
|
4e425887e7 | ||
|
|
8b84390517 | ||
|
|
fd95bc8f5a | ||
|
|
08dc5cacd9 | ||
|
|
29ec7aaa51 | ||
|
|
820a30f0d2 | ||
|
|
f2d03736a7 | ||
|
|
f340a2eccc | ||
|
|
78c83ac1ec | ||
|
|
190bb0bd24 | ||
|
|
fdc2970d37 | ||
|
|
d55655a423 | ||
|
|
c8aafdba33 | ||
|
|
1601da4049 | ||
|
|
34d8f03af4 | ||
|
|
05996cd497 | ||
|
|
25837eeee5 | ||
|
|
c6cfbc2919 | ||
|
|
d27b47f439 | ||
|
|
9d490593d0 | ||
|
|
4bdfb7e7da | ||
|
|
2dcfc54752 | ||
|
|
504ff4ed55 | ||
|
|
e3a91ec1e3 | ||
|
|
58420b7c60 | ||
|
|
945fcbe820 | ||
|
|
02bce9b1af | ||
|
|
85910fb575 | ||
|
|
96305d9bb4 | ||
|
|
82586e293e | ||
|
|
873f7fe535 | ||
|
|
37a26591c7 | ||
|
|
ab1fb6cf8e | ||
|
|
6403642c02 | ||
|
|
de376be2ba | ||
|
|
adb750fdf4 | ||
|
|
20d051a00e | ||
|
|
7d80ea4544 | ||
|
|
eaa4f9d23b | ||
|
|
fccc881894 | ||
|
|
0119a8c78b | ||
|
|
9727956436 | ||
|
|
09175119d2 | ||
|
|
20d065d940 | ||
|
|
0c533e61bc | ||
|
|
6efdbe85e3 | ||
|
|
7b2b4b7c53 | ||
|
|
f2a8ee8ff2 | ||
|
|
7208116105 | ||
|
|
11631c0609 | ||
|
|
a00391c729 | ||
|
|
1d94aeea44 | ||
|
|
8dc3d77b59 | ||
|
|
09cd25f763 | ||
|
|
5bc8abc0ec | ||
|
|
f91f6bd78e | ||
|
|
7c9e50e37d | ||
|
|
2ea356d0be | ||
|
|
77af042413 | ||
|
|
acab405fc1 | ||
|
|
9a9a403a89 | ||
|
|
83026c7db2 | ||
|
|
06d4f569a8 | ||
|
|
b42bf48310 | ||
|
|
024194420c | ||
|
|
7c9b580681 | ||
|
|
fc2cf7ead2 | ||
|
|
df7a3a7168 | ||
|
|
2a0886b9a7 | ||
|
|
9c4b94b9e7 | ||
|
|
35e1c6c402 | ||
|
|
8cb2bfa233 | ||
|
|
0e3e349369 | ||
|
|
0102b1769b | ||
|
|
ef67339175 | ||
|
|
36600d8fa0 | ||
|
|
13d865f6f1 | ||
|
|
947a63400f | ||
|
|
f49d76b7ad | ||
|
|
1428e950bd | ||
|
|
e3b7dd7b56 | ||
|
|
9d1a071194 | ||
|
|
72c3887167 | ||
|
|
b2815b6b46 | ||
|
|
6c0e55a2a9 | ||
|
|
0560544e86 | ||
|
|
613e1a9a38 | ||
|
|
bcda3bda04 | ||
|
|
9998478c64 | ||
|
|
c368c6afe8 | ||
|
|
1614345a52 | ||
|
|
c237022831 |
No files matched your search
@@ -4,8 +4,8 @@ runs:
|
||||
steps:
|
||||
- name: Install Maven
|
||||
run: |
|
||||
wget --no-check-certificate https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz
|
||||
tar zxf apache-maven-3.9.6-bin.tar.gz
|
||||
echo "export M2_HOME=$(pwd)/apache-maven-3.9.6" >> $GITHUB_ENV
|
||||
echo "$(pwd)/apache-maven-3.9.6/bin" >> $GITHUB_PATH
|
||||
wget --no-check-certificate https://archive.apache.org/dist/maven/maven-3/3.9.11/binaries/apache-maven-3.9.11-bin.tar.gz
|
||||
tar zxf apache-maven-3.9.11-bin.tar.gz
|
||||
echo "export M2_HOME=$(pwd)/apache-maven-3.9.11" >> $GITHUB_ENV
|
||||
echo "$(pwd)/apache-maven-3.9.11/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
@@ -38,7 +38,7 @@ runs:
|
||||
$env:Path = $env:JAVA_HOME + ";" + $env:Path
|
||||
mkdir build
|
||||
cd build
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DJNI=1 ..
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DXPRESS=1 -DJNI=1 ..
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
cd ..
|
||||
echo "Building with VS version: $Env:CMAKE_GENERATOR"
|
||||
|
||||
@@ -27,18 +27,6 @@ jobs:
|
||||
git config --global --add safe.directory /__w/rocksdb/rocksdb
|
||||
tools/check_format_compatible.sh
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-run-microbench:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: DEBUG_LEVEL=0 make -j32 run_microbench
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-non-shm:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
@@ -91,15 +79,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/windows-build-steps"
|
||||
build-windows-vs2022:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: windows-2022
|
||||
env:
|
||||
CMAKE_GENERATOR: Visual Studio 17 2022
|
||||
CMAKE_PORTABLE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/windows-build-steps"
|
||||
build-linux-arm-test-full:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
@@ -110,3 +89,59 @@ jobs:
|
||||
- run: sudo apt-get update && sudo apt-get install -y build-essential libgflags-dev
|
||||
- run: make V=1 J=4 -j4 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-examples:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Build examples
|
||||
run: make V=1 -j4 static_lib && cd examples && make V=1 -j4
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-fuzzers:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Build rocksdb lib
|
||||
run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j4 static_lib
|
||||
- name: Build fuzzers
|
||||
run: cd fuzz && make sst_file_writer_fuzzer db_fuzzer db_map_fuzzer
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-gcc-11-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: LIB_MODE=static CC=gcc-11 CXX=g++-11 V=1 make -j32 all microbench
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-folly-lite-no-test:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY_LITE=1 -DWITH_GFLAGS=1 .. && make V=1 -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
@@ -52,6 +52,14 @@ jobs:
|
||||
run: make check-buck-targets
|
||||
- name: Simple source code checks
|
||||
run: make check-sources
|
||||
- name: Sanity check check_format_compatible.sh
|
||||
run: |-
|
||||
export TEST_TMPDIR=/dev/shm/rocksdb
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
git reset --hard
|
||||
git config --global --add safe.directory /__w/rocksdb/rocksdb
|
||||
SANITY_CHECK=1 LONG_TEST=1 tools/check_format_compatible.sh
|
||||
# ========================= Linux With Tests ======================== #
|
||||
build-linux:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
@@ -101,22 +109,6 @@ jobs:
|
||||
- uses: "./.github/actions/build-folly"
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make V=1 -j20 && ctest -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-folly-lite-no-test:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY_LITE=1 -DWITH_GFLAGS=1 .. && make V=1 -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-make-with-folly:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
@@ -190,7 +182,7 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: ENCRYPTED_ENV=1 ROCKSDB_DISABLE_SNAPPY=1 ROCKSDB_DISABLE_ZLIB=1 ROCKSDB_DISABLE_BZIP=1 ROCKSDB_DISABLE_LZ4=1 ROCKSDB_DISABLE_ZSTD=1 make V=1 J=32 -j32 check
|
||||
- run: "./sst_dump --help | grep -E -q 'Supported compression types: kNoCompression$' # Verify no compiled in compression\n"
|
||||
- run: "./sst_dump --help | grep -E -q 'Supported built-in compression types: kNoCompression$' # Verify no compiled in compression\n"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================== Linux No Test Runs ======================= #
|
||||
build-linux-release:
|
||||
@@ -234,34 +226,6 @@ jobs:
|
||||
- run: apt-get remove -y libgflags-dev
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j16 static_lib tools db_bench
|
||||
- run: if ./db_stress --version; then false; else true; fi
|
||||
build-examples:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Build examples
|
||||
run: make V=1 -j4 static_lib && cd examples && make V=1 -j4
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-fuzzers:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Build rocksdb lib
|
||||
run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j4 static_lib
|
||||
- name: Build fuzzers
|
||||
run: cd fuzz && make sst_file_writer_fuzzer db_fuzzer db_map_fuzzer
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
@@ -309,18 +273,7 @@ jobs:
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: CC=gcc-10 CXX=g++-10 V=1 ROCKSDB_CXX_STANDARD=c++20 make -j32 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-gcc-11-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: LIB_MODE=static CC=gcc-11 CXX=g++-11 V=1 make -j32 all microbench
|
||||
- uses: "./.github/actions/post-steps"
|
||||
|
||||
# ======================== Linux Other Checks ======================= #
|
||||
build-linux-clang10-clang-analyze:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
@@ -461,11 +414,11 @@ jobs:
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================== Windows with Tests ======================= #
|
||||
# NOTE: some windows jobs are in "nightly" to save resources
|
||||
build-windows-vs2019:
|
||||
build-windows-vs2022:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: windows-2019
|
||||
runs-on: windows-2022
|
||||
env:
|
||||
CMAKE_GENERATOR: Visual Studio 16 2019
|
||||
CMAKE_GENERATOR: Visual Studio 17 2022
|
||||
CMAKE_PORTABLE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
@@ -476,7 +429,7 @@ jobs:
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: evolvedbinary/rocksjava:centos6_x64-be
|
||||
image: evolvedbinary/rocksjava:centos7_x64-be
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
# The docker image is intentionally based on an OS that has an older GLIBC version.
|
||||
@@ -504,7 +457,7 @@ jobs:
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: evolvedbinary/rocksjava:centos6_x64-be
|
||||
image: evolvedbinary/rocksjava:centos7_x64-be
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
# The docker image is intentionally based on an OS that has an older GLIBC version.
|
||||
@@ -599,7 +552,7 @@ jobs:
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: evolvedbinary/rocksjava:rockylinux8_x64-be
|
||||
image: evolvedbinary/rocksjava:alpine3_x64-be
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
|
||||
@@ -88,6 +88,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"db/memtable_list.cc",
|
||||
"db/merge_helper.cc",
|
||||
"db/merge_operator.cc",
|
||||
"db/multi_scan.cc",
|
||||
"db/output_validator.cc",
|
||||
"db/periodic_task_scheduler.cc",
|
||||
"db/range_del_aggregator.cc",
|
||||
@@ -249,6 +250,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"trace_replay/trace_record_result.cc",
|
||||
"trace_replay/trace_replay.cc",
|
||||
"util/async_file_reader.cc",
|
||||
"util/auto_tune_compressor.cc",
|
||||
"util/build_version.cc",
|
||||
"util/cleanable.cc",
|
||||
"util/coding.cc",
|
||||
@@ -267,6 +269,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"util/random.cc",
|
||||
"util/rate_limiter.cc",
|
||||
"util/ribbon_config.cc",
|
||||
"util/simple_mixed_compressor.cc",
|
||||
"util/slice.cc",
|
||||
"util/status.cc",
|
||||
"util/stderr_logger.cc",
|
||||
@@ -4709,6 +4712,12 @@ cpp_unittest_wrapper(name="compressed_secondary_cache_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="compression_test",
|
||||
srcs=["util/compression_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="configurable_test",
|
||||
srcs=["options/configurable_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
|
||||
@@ -721,6 +721,7 @@ set(SOURCES
|
||||
db/memtable_list.cc
|
||||
db/merge_helper.cc
|
||||
db/merge_operator.cc
|
||||
db/multi_scan.cc
|
||||
db/output_validator.cc
|
||||
db/periodic_task_scheduler.cc
|
||||
db/range_del_aggregator.cc
|
||||
@@ -874,11 +875,13 @@ set(SOURCES
|
||||
trace_replay/trace_record.cc
|
||||
trace_replay/trace_replay.cc
|
||||
util/async_file_reader.cc
|
||||
util/auto_tune_compressor.cc
|
||||
util/cleanable.cc
|
||||
util/coding.cc
|
||||
util/compaction_job_stats_impl.cc
|
||||
util/comparator.cc
|
||||
util/compression.cc
|
||||
util/simple_mixed_compressor.cc
|
||||
util/compression_context_cache.cc
|
||||
util/concurrent_task_limiter_impl.cc
|
||||
util/crc32c.cc
|
||||
@@ -1456,6 +1459,7 @@ if(WITH_TESTS)
|
||||
util/autovector_test.cc
|
||||
util/bloom_test.cc
|
||||
util/coding_test.cc
|
||||
util/compression_test.cc
|
||||
util/crc32c_test.cc
|
||||
util/defer_test.cc
|
||||
util/dynamic_bloom_test.cc
|
||||
|
||||
+72
@@ -1,6 +1,78 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 10.5.0 (07/18/2025)
|
||||
### Public API Changes
|
||||
* DB option skip_checking_sst_file_sizes_on_db_open is deprecated, in favor of validating file size in parallel in a thread pool, when db is opened. When DB is opened, with paranoid check enabled, a file with the wrong size would fail the DB open. With paranoid check disabled, the DB open would succeed, the column family with the corrupted file would not be read or write, while the other healthy column families could be read and write normally. When max_open_files option is not set to -1, only a subset of the files will be opened and checked. The rest of the files will be opened and checked when they are accessed.
|
||||
|
||||
### Behavior Changes
|
||||
* PessimisticTransaction::GetWaitingTxns now returns waiting transaction information even if the current transaction has timed out. This allows the information to be surfaced to users for debugging purposes once it is known that the timeout has occured.
|
||||
* A new API GetFileSize is added to FSRandomAccessFile interface class. It uses fstat vs stat on the posix implementation which is more efficient. Caller could use it to get file size faster. This function might be required in the future for FileSystem implementation outside of the RocksDB code base.
|
||||
* RocksDB now triggers eligible compactions every 12 hours when periodic compaction is configured. This solves a limitation of the compaction trigger mechanism, which would only trigger compaction after specific events like flush, compaction, or SetOptions.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in BackupEngine that can crash backup due to a null FSWritableFile passed to WritableFileWriter.
|
||||
* Fix DB::NewMultiScan iterator to respect the scan upper bound specified in ScanOptions
|
||||
|
||||
### Performance Improvements
|
||||
* Optimized MultiScan using BlockBasedTable to coalesce I/Os and prefetch all data blocks.
|
||||
|
||||
## 10.4.0 (06/20/2025)
|
||||
### New Features
|
||||
* Add a new CF option `memtable_avg_op_scan_flush_trigger` that supports triggering memtable flush when an iterator scans through an expensive range of keys, with the average number of skipped keys from the active memtable exceeding the threshold.
|
||||
* Vector based memtable now supports concurrent writers (DBOptions::allow_concurrent_memtable_write) #13675.
|
||||
* Add new experimental `TransactionOptions::large_txn_commit_optimize_byte_threshold` to enable optimizations for large transaction commit by transaction batch data size.
|
||||
* Add a new option `CompactionOptionsUniversal::reduce_file_locking` and if it's true, auto universal compaction picking will adjust to minimize locking of input files when bottom priority compactions are waiting to run. This can increase the likelihood of existing L0s being selected for compaction, thereby improving write stall and reducing read regression.
|
||||
* Add new `format_version=7` to aid experimental support of custom compression algorithms with CompressionManager and block-based table. This format version includes changing the format of `TableProperties::compression_name`.
|
||||
|
||||
### Public API Changes
|
||||
* Change NewExternalTableFactory to return a unique_ptr instead of shared_ptr.
|
||||
* Add an optional min file size requirement for deletion triggered compaction. It can be specified when creating `CompactOnDeletionCollectorFactory`.
|
||||
|
||||
### Behavior Changes
|
||||
* `TransactionOptions::large_txn_commit_optimize_threshold` now has default value 0 for disabled. `TransactionDBOptions::txn_commit_bypass_memtable_threshold` now has no effect on transactions.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug where CreateColumnFamilyWithImport() could miss the SST file for the memtable flush it triggered. The exported CF then may not contain the updates in the memtable when CreateColumnFamilyWithImport() is called.
|
||||
* Fix iterator operations returning NotImplemented status if disallow_memtable_writes and paranoid_memory_checks CF options are both set.
|
||||
* Fixed handling of file checksums in IngestExternalFile() to allow providing checksums using recognized but not necessarily the DB's preferred checksum function, to ease migration between checksum functions.
|
||||
|
||||
## 10.3.0 (05/17/2025)
|
||||
### New Features
|
||||
* Add new experimental `CompactionOptionsFIFO::allow_trivial_copy_when_change_temperature` along with `CompactionOptionsFIFO::trivial_copy_buffer_size` to allow optimizing FIFO compactions with tiering when kChangeTemperature to move files from source tier FileSystem to another tier FileSystem via trivial and direct copying raw sst file instead of reading thru the content of the SST file then rebuilding the table files.
|
||||
* Add a new field to Compaction Stats in LOG files for the pre-compression size written to each level.
|
||||
* Add new experimental `TransactionOptions::large_txn_commit_optimize_threshold` to enable optimizations for large transaction commit with per transaction threshold. `TransactionDBOptions::txn_commit_bypass_memtable_threshold` is deprecated in favor of this transaction option.
|
||||
* [internal team use only] Allow an application-defined `request_id` to be passed to RocksDB and propagated to the filesystem via IODebugContext
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug where transaction lock upgrade can incorrectly fail with a Deadlock status. This happens when a transaction has a non-zero timeout and tries to upgrade a shared lock that is also held by another transaction.
|
||||
* Pass wrapped WritableFileWriter pointer to ExternalTableBuilder so that the file checksum can be correctly calculated and returned by SstFileWriter for external table files.
|
||||
* Fix an infinite-loop bug in transaction locking. This can happen if a transaction reaches lock limit and its time out expires before it attempts to wait for it.
|
||||
* Fixed a potential data race with `CompressionOptions::parallel_threads > 1` and a `TablePropertiesCollector` overriding `BlockAdd()`.
|
||||
|
||||
## 10.2.0 (04/21/2025)
|
||||
### New Features
|
||||
* Provide histogram stats `COMPACTION_PREFETCH_BYTES` to measure number of bytes for RocksDB's prefetching (as opposed to file
|
||||
system's prefetch) on SST file during compaction read
|
||||
* A new API DB::GetNewestUserDefinedTimestamp is added to return the newest user defined timestamp seen in a column family
|
||||
* Introduce API `IngestWriteBatchWithIndex()` for ingesting updates into DB while bypassing memtable writes. This improves performance when writing a large write batch to the DB.
|
||||
* Add a new CF option `memtable_op_scan_flush_trigger` that triggers a flush of the memtable if an iterator's Seek()/Next() scans over a certain number of invisible entries from the memtable.
|
||||
|
||||
### Public API Changes
|
||||
* AdvancedColumnFamilyOptions.max_write_buffer_number_to_maintain is deleted. It's deprecated since introduction of a better option max_write_buffer_size_to_maintain since RocksDB 6.5.0.
|
||||
* Deprecated API `DB::MaxMemCompactionLevel()`.
|
||||
* Deprecated `ReadOptions::ignore_range_deletions`.
|
||||
* Deprecated API `experimental::PromoteL0()`.
|
||||
* Added arbitrary string map for additional options to be overriden for remote compactions
|
||||
* The fail_if_options_file_error option in DBOptions has been removed. The behavior now is to always return failure in any API that fails to persist the OPTIONS file.
|
||||
|
||||
### Behavior Changes
|
||||
* Make stats `PREFETCH_BYTES_USEFUL`, `PREFETCH_HITS`, `PREFETCH_BYTES` only account for prefetching during user initiated scan
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in Posix file system that the FSWritableFile created via `FileSystem::ReopenWritableFile` internally does not track the correct file size.
|
||||
* Fix a bug where tail size of remote compaction output is not persisted in primary db's manifest
|
||||
|
||||
## 10.1.0 (03/24/2025)
|
||||
### New Features
|
||||
* Added a new `DBOptions.calculate_sst_write_lifetime_hint_set` setting that allows to customize which compaction styles SST write lifetime hint calculation is allowed on. Today RocksDB supports only two modes `kCompactionStyleLevel` and `kCompactionStyleUniversal`.
|
||||
|
||||
@@ -1491,6 +1491,9 @@ db_test: $(OBJ_DIR)/db/db_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
db_test2: $(OBJ_DIR)/db/db_test2.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
compression_test: $(OBJ_DIR)/util/compression_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_logical_block_size_cache_test: $(OBJ_DIR)/db/db_logical_block_size_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -2144,14 +2147,14 @@ ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.8
|
||||
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
|
||||
BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
|
||||
SNAPPY_VER ?= 1.2.1
|
||||
SNAPPY_SHA256 ?= 736aeb64d86566d2236ddffa2865ee5d7a82d26c9016b36218fcc27ea4f09f86
|
||||
SNAPPY_VER ?= 1.2.2
|
||||
SNAPPY_SHA256 ?= 90f74bc1fbf78a6c56b3c4a082a05103b3a56bb17bca1a27e052ea11723292dc
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
LZ4_VER ?= 1.9.4
|
||||
LZ4_SHA256 ?= 0b0e3aa07c8c063ddf40b082bdf7e37a1562bda40a0ff5272957f3e987e0e54b
|
||||
LZ4_VER ?= 1.10.0
|
||||
LZ4_SHA256 ?= 537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b
|
||||
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
|
||||
ZSTD_VER ?= 1.5.5
|
||||
ZSTD_SHA256 ?= 98e9c3d949d1b924e28e01eccb7deed865eefebf25c2f21c702e5cd5b63b85e1
|
||||
ZSTD_VER ?= 1.5.7
|
||||
ZSTD_SHA256 ?= 37d7284556b20954e56e1ca85b80226768902e2edabd3b649e9e72c0c9012ee3
|
||||
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
CURL_SSL_OPTS ?= --tlsv1
|
||||
|
||||
@@ -2372,27 +2375,27 @@ rocksdbjavastaticreleasedocker: rocksdbjavastaticosx rocksdbjavastaticdockerx86
|
||||
|
||||
rocksdbjavastaticdockerx86:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x86-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_x86-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_x86-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerx86_64:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_x64-be --platform linux/amd64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerppc64le:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_ppc64le-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_ppc64le-be --platform linux/ppc64le --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerarm64v8:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_arm64v8-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_arm64v8-be --platform linux/aarch64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockers390x:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_s390x-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_s390x-be --platform linux/s390x --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerriscv64:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_riscv64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_riscv64-be --platform linux/riscv64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerx86musl:
|
||||
mkdir -p java/target
|
||||
@@ -2400,19 +2403,19 @@ rocksdbjavastaticdockerx86musl:
|
||||
|
||||
rocksdbjavastaticdockerx86_64musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x64-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_x64-musl-be --platform linux/amd64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerppc64lemusl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_ppc64le-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_ppc64le-musl-be --platform linux/ppc64le --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerarm64v8musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_arm64v8-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_arm64v8-musl-be --platform linux/aarch64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockers390xmusl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_s390x-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_s390x-musl-be --platform linux/s390x --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentral
|
||||
|
||||
@@ -2467,8 +2470,8 @@ jtest_run:
|
||||
jtest: rocksdbjava
|
||||
cd java;$(MAKE) sample test
|
||||
|
||||
jpmd: rocksdbjava rocksdbjavageneratepom
|
||||
cd java;$(MAKE) pmd
|
||||
jpmd: rocksdbjavageneratepom
|
||||
cd java;$(MAKE) java java_test pmd
|
||||
|
||||
jdb_bench:
|
||||
cd java;$(MAKE) db_bench;
|
||||
@@ -2489,11 +2492,13 @@ checkout_folly:
|
||||
fi
|
||||
@# Pin to a particular version for public CI, so that PR authors don't
|
||||
@# need to worry about folly breaking our integration. Update periodically
|
||||
cd third-party/folly && git reset --hard 78286282478e1ae05b2e8cbcf0e2139eab283bea
|
||||
@# NOTE: this hack is required for clang in some cases
|
||||
perl -pi -e 's/int rv = syscall/int rv = (int)syscall/' third-party/folly/folly/detail/Futex.cpp
|
||||
@# NOTE: this hack is required for gcc in some cases
|
||||
perl -pi -e 's/(__has_include.<experimental.memory_resource>.)/__cpp_rtti && $$1/' third-party/folly/folly/memory/MemoryResource.h
|
||||
cd third-party/folly && git reset --hard e95383b7c8b5b1e46cf47acf2f317d54f93c8268
|
||||
@# Apparently missing include
|
||||
perl -pi -e 's/(#include <atomic>)/$$1\n#include <cstring>/' third-party/folly/folly/lang/Exception.h
|
||||
@# Warning-as-error on memcpy
|
||||
perl -pi -e 's/memcpy.&ptr/memcpy((void*)&ptr/' third-party/folly/folly/lang/Exception.cpp
|
||||
@# const mismatch
|
||||
perl -pi -e 's/: environ/: (const char**)(environ)/' third-party/folly/folly/Subprocess.cpp
|
||||
@# NOTE: boost source will be needed for any build including `USE_FOLLY_LITE` builds as those depend on boost headers
|
||||
cd third-party/folly && $(PYTHON) build/fbcode_builder/getdeps.py fetch boost
|
||||
|
||||
|
||||
@@ -118,6 +118,9 @@ fi
|
||||
# fi
|
||||
set -e
|
||||
|
||||
# Exclude third-party from formatting
|
||||
EXCLUDE=':!third-party/'
|
||||
|
||||
uncommitted_code=`git diff HEAD`
|
||||
|
||||
# If there's no uncommitted changes, we assume user are doing post-commit
|
||||
@@ -137,11 +140,11 @@ then
|
||||
# should be relevant for formatting fixes.
|
||||
FORMAT_UPSTREAM_MERGE_BASE="$(git merge-base "$FORMAT_UPSTREAM" HEAD)"
|
||||
# Get the differences
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" -- $EXCLUDE | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
echo "Checking format of changes not yet in $FORMAT_UPSTREAM..."
|
||||
else
|
||||
# Check the format of uncommitted lines,
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
diffs=$(git diff -U0 HEAD -- $EXCLUDE | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
echo "Checking format of uncommitted changes..."
|
||||
fi
|
||||
|
||||
@@ -187,9 +190,9 @@ fi
|
||||
# Do in-place format adjustment.
|
||||
if [ -z "$uncommitted_code" ]
|
||||
then
|
||||
git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -i -p 1
|
||||
git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" -- $EXCLUDE | $CLANG_FORMAT_DIFF -i -p 1
|
||||
else
|
||||
git diff -U0 HEAD | $CLANG_FORMAT_DIFF -i -p 1
|
||||
git diff -U0 HEAD -- $EXCLUDE | $CLANG_FORMAT_DIFF -i -p 1
|
||||
fi
|
||||
echo "Files reformatted!"
|
||||
|
||||
|
||||
Vendored
+42
-38
@@ -24,7 +24,14 @@ CompressedSecondaryCache::CompressedSecondaryCache(
|
||||
cache_res_mgr_(std::make_shared<ConcurrentCacheReservationManager>(
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache_))),
|
||||
disable_cache_(opts.capacity == 0) {}
|
||||
disable_cache_(opts.capacity == 0) {
|
||||
auto mgr =
|
||||
GetBuiltinCompressionManager(cache_options_.compress_format_version);
|
||||
compressor_ = mgr->GetCompressor(cache_options_.compression_opts,
|
||||
cache_options_.compression_type);
|
||||
decompressor_ =
|
||||
mgr->GetDecompressorOptimizeFor(cache_options_.compression_type);
|
||||
}
|
||||
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() = default;
|
||||
|
||||
@@ -73,15 +80,16 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
ptr = reinterpret_cast<CacheAllocationPtr*>(handle_value);
|
||||
handle_value_charge = cache_->GetCharge(lru_handle);
|
||||
data_ptr = ptr->get();
|
||||
data_ptr = GetVarint32Ptr(data_ptr, data_ptr + 1,
|
||||
static_cast<uint32_t*>(&type_32));
|
||||
const char* limit = ptr->get() + handle_value_charge;
|
||||
data_ptr =
|
||||
GetVarint32Ptr(data_ptr, limit, static_cast<uint32_t*>(&type_32));
|
||||
type = static_cast<CompressionType>(type_32);
|
||||
data_ptr = GetVarint32Ptr(data_ptr, data_ptr + 1,
|
||||
static_cast<uint32_t*>(&source_32));
|
||||
data_ptr =
|
||||
GetVarint32Ptr(data_ptr, limit, static_cast<uint32_t*>(&source_32));
|
||||
source = static_cast<CacheTier>(source_32);
|
||||
uint64_t data_size = 0;
|
||||
data_ptr = GetVarint64Ptr(data_ptr, ptr->get() + handle_value_charge,
|
||||
static_cast<uint64_t*>(&data_size));
|
||||
data_ptr =
|
||||
GetVarint64Ptr(data_ptr, limit, static_cast<uint64_t*>(&data_size));
|
||||
assert(handle_value_charge > data_size);
|
||||
handle_value_charge = data_size;
|
||||
}
|
||||
@@ -97,25 +105,24 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
kNoCompression, CacheTier::kVolatileTier,
|
||||
create_context, allocator, &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 =
|
||||
UncompressData(uncompression_info, (char*)data_ptr,
|
||||
handle_value_charge, &uncompressed_size,
|
||||
cache_options_.compress_format_version, allocator);
|
||||
|
||||
if (!uncompressed) {
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/true);
|
||||
return nullptr;
|
||||
// TODO: can we work some magic with create_cb, which might be based on
|
||||
// custom compression, to decompress without an extra copy in create_cb?
|
||||
Decompressor::Args args;
|
||||
args.compressed_data = Slice(data_ptr, handle_value_charge);
|
||||
args.compression_type = cache_options_.compression_type;
|
||||
s = decompressor_->ExtractUncompressedSize(args);
|
||||
assert(s.ok());
|
||||
if (s.ok()) {
|
||||
auto uncompressed = std::make_unique<char[]>(args.uncompressed_size);
|
||||
s = decompressor_->DecompressBlock(args, uncompressed.get());
|
||||
assert(s.ok());
|
||||
if (s.ok()) {
|
||||
s = helper->create_cb(
|
||||
Slice(uncompressed.get(), args.uncompressed_size), kNoCompression,
|
||||
CacheTier::kVolatileTier, create_context, allocator, &value,
|
||||
&charge);
|
||||
}
|
||||
}
|
||||
s = helper->create_cb(Slice(uncompressed.get(), uncompressed_size),
|
||||
kNoCompression, CacheTier::kVolatileTier,
|
||||
create_context, allocator, &value, &charge);
|
||||
}
|
||||
} else {
|
||||
// The item was not compressed by us. Let the helper create_cb
|
||||
@@ -198,20 +205,17 @@ Status CompressedSecondaryCache::InsertInternal(
|
||||
type == kNoCompression &&
|
||||
!cache_options_.do_not_compress_roles.Contains(helper->role)) {
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_uncompressed_bytes, data_size);
|
||||
CompressionContext compression_context(cache_options_.compression_type,
|
||||
cache_options_.compression_opts);
|
||||
uint64_t sample_for_compression{0};
|
||||
CompressionInfo compression_info(
|
||||
cache_options_.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.");
|
||||
CompressionType to_type = kNoCompression;
|
||||
s = compressor_->CompressBlock(val, &compressed_val, &to_type,
|
||||
nullptr /*working_area*/);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
// TODO: allow values not compressed when there's no size savings?
|
||||
assert(to_type == cache_options_.compression_type);
|
||||
if (to_type != cache_options_.compression_type) {
|
||||
return Status::Corruption("Failed to compress value.");
|
||||
}
|
||||
|
||||
val = Slice(compressed_val);
|
||||
|
||||
Vendored
+2
@@ -145,6 +145,8 @@ class CompressedSecondaryCache : public SecondaryCache {
|
||||
const Cache::CacheItemHelper* GetHelper(bool enable_custom_split_merge) const;
|
||||
std::shared_ptr<Cache> cache_;
|
||||
CompressedSecondaryCacheOptions cache_options_;
|
||||
std::unique_ptr<Compressor> compressor_;
|
||||
std::shared_ptr<Decompressor> decompressor_;
|
||||
mutable port::Mutex capacity_mutex_;
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr_;
|
||||
bool disable_cache_;
|
||||
|
||||
Vendored
+14
-5
@@ -121,7 +121,17 @@ CacheWithSecondaryAdapter::~CacheWithSecondaryAdapter() {
|
||||
assert(s.ok());
|
||||
assert(placeholder_usage_ == 0);
|
||||
assert(reserved_usage_ == 0);
|
||||
assert(pri_cache_res_->GetTotalMemoryUsed() == sec_capacity);
|
||||
bool pri_cache_res_mismatch =
|
||||
pri_cache_res_->GetTotalMemoryUsed() != sec_capacity;
|
||||
if (pri_cache_res_mismatch) {
|
||||
fprintf(stderr,
|
||||
"~CacheWithSecondaryAdapter: Primary cache reservation: "
|
||||
"%zu, Secondary cache capacity: %zu, "
|
||||
"Secondary cache reserved: %zu\n",
|
||||
pri_cache_res_->GetTotalMemoryUsed(), sec_capacity,
|
||||
sec_reserved_);
|
||||
assert(!pri_cache_res_mismatch);
|
||||
}
|
||||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
@@ -479,12 +489,10 @@ const char* CacheWithSecondaryAdapter::Name() const {
|
||||
// as well. At the moment, we don't have a good way of handling the case
|
||||
// where the new capacity < total cache reservations.
|
||||
void CacheWithSecondaryAdapter::SetCapacity(size_t capacity) {
|
||||
size_t sec_capacity = static_cast<size_t>(
|
||||
capacity * (distribute_cache_res_ ? sec_cache_res_ratio_ : 0.0));
|
||||
size_t old_sec_capacity = 0;
|
||||
|
||||
if (distribute_cache_res_) {
|
||||
MutexLock m(&cache_res_mutex_);
|
||||
size_t sec_capacity = static_cast<size_t>(capacity * sec_cache_res_ratio_);
|
||||
size_t old_sec_capacity = 0;
|
||||
|
||||
Status s = secondary_cache_->GetCapacity(old_sec_capacity);
|
||||
if (!s.ok()) {
|
||||
@@ -603,6 +611,7 @@ Status CacheWithSecondaryAdapter::UpdateCacheReservationRatio(
|
||||
// cache utilization (increase in capacity - increase in share of cache
|
||||
// reservation)
|
||||
// 3. Increase secondary cache capacity
|
||||
assert(new_sec_reserved >= sec_reserved_);
|
||||
s = secondary_cache_->Deflate(new_sec_reserved - sec_reserved_);
|
||||
assert(s.ok());
|
||||
s = pri_cache_res_->UpdateCacheReservation(
|
||||
|
||||
@@ -55,7 +55,7 @@ void ArenaWrappedDBIter::Init(
|
||||
db_iter_ = DBIter::NewIter(
|
||||
env, read_options_, ioptions, mutable_cf_options,
|
||||
ioptions.user_comparator, /*internal_iter=*/nullptr, version, sequence,
|
||||
read_callback, cfh, expose_blob_index, active_mem, &arena_);
|
||||
read_callback, active_mem, cfh, expose_blob_index, &arena_);
|
||||
|
||||
sv_number_ = version_number;
|
||||
allow_refresh_ = allow_refresh;
|
||||
|
||||
@@ -267,10 +267,9 @@ Status BlobFileBuilder::CompressBlobIfNeeded(
|
||||
// TODO: allow user CompressionOptions, including max_compressed_bytes_per_kb
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(blob_compression_type_, opts);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
blob_compression_type_, sample_for_compression);
|
||||
blob_compression_type_);
|
||||
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
|
||||
@@ -279,8 +278,8 @@ Status BlobFileBuilder::CompressBlobIfNeeded(
|
||||
{
|
||||
StopWatch stop_watch(immutable_options_->clock, immutable_options_->stats,
|
||||
BLOB_DB_COMPRESSION_MICROS);
|
||||
success =
|
||||
CompressData(*blob, info, compression_format_version, compressed_blob);
|
||||
success = OLD_CompressData(*blob, info, compression_format_version,
|
||||
compressed_blob);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
|
||||
@@ -405,10 +405,9 @@ TEST_F(BlobFileBuilderTest, Compression) {
|
||||
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(kSnappyCompression, opts);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
kSnappyCompression, sample_for_compression);
|
||||
kSnappyCompression);
|
||||
|
||||
std::string compressed_value;
|
||||
ASSERT_TRUE(Snappy_Compress(info, uncompressed_value.data(),
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -250,7 +250,8 @@ Status BlobFileReader::ReadFromFile(const RandomAccessFileReader* file_reader,
|
||||
Status s;
|
||||
|
||||
IOOptions io_options;
|
||||
s = file_reader->PrepareIOOptions(read_options, io_options);
|
||||
IODebugContext dbg;
|
||||
s = file_reader->PrepareIOOptions(read_options, io_options, &dbg);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -259,13 +260,13 @@ Status BlobFileReader::ReadFromFile(const RandomAccessFileReader* file_reader,
|
||||
constexpr char* scratch = nullptr;
|
||||
|
||||
s = file_reader->Read(io_options, read_offset, read_size, slice, scratch,
|
||||
aligned_buf);
|
||||
aligned_buf, &dbg);
|
||||
} else {
|
||||
buf->reset(new char[read_size]);
|
||||
constexpr AlignedBuf* aligned_scratch = nullptr;
|
||||
|
||||
s = file_reader->Read(io_options, read_offset, read_size, slice, buf->get(),
|
||||
aligned_scratch);
|
||||
aligned_scratch, &dbg);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
@@ -334,7 +335,8 @@ Status BlobFileReader::GetBlob(
|
||||
constexpr bool for_compaction = true;
|
||||
|
||||
IOOptions io_options;
|
||||
s = file_reader_->PrepareIOOptions(read_options, io_options);
|
||||
IODebugContext dbg;
|
||||
s = file_reader_->PrepareIOOptions(read_options, io_options, &dbg);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -463,10 +465,11 @@ void BlobFileReader::MultiGetBlob(
|
||||
PERF_COUNTER_ADD(blob_read_count, num_blobs);
|
||||
PERF_COUNTER_ADD(blob_read_byte, total_len);
|
||||
IOOptions opts;
|
||||
s = file_reader_->PrepareIOOptions(read_options, opts);
|
||||
IODebugContext dbg;
|
||||
s = file_reader_->PrepareIOOptions(read_options, opts, &dbg);
|
||||
if (s.ok()) {
|
||||
s = file_reader_->MultiRead(opts, read_reqs.data(), read_reqs.size(),
|
||||
direct_io ? &aligned_buf : nullptr);
|
||||
direct_io ? &aligned_buf : nullptr, &dbg);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
for (auto& req : read_reqs) {
|
||||
@@ -602,9 +605,9 @@ Status BlobFileReader::UncompressBlobIfNeeded(
|
||||
{
|
||||
PERF_TIMER_GUARD(blob_decompress_time);
|
||||
StopWatch stop_watch(clock, statistics, BLOB_DB_DECOMPRESSION_MICROS);
|
||||
output = UncompressData(info, value_slice.data(), value_slice.size(),
|
||||
&uncompressed_size, compression_format_version,
|
||||
allocator);
|
||||
output = OLD_UncompressData(info, value_slice.data(), value_slice.size(),
|
||||
&uncompressed_size, compression_format_version,
|
||||
allocator);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
|
||||
@@ -75,15 +75,14 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
} else {
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(compression, opts);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
compression, sample_for_compression);
|
||||
compression);
|
||||
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
ASSERT_TRUE(CompressData(blobs[i], info, compression_format_version,
|
||||
&compressed_blobs[i]));
|
||||
ASSERT_TRUE(OLD_CompressData(blobs[i], info, compression_format_version,
|
||||
&compressed_blobs[i]));
|
||||
blobs_to_write[i] = compressed_blobs[i];
|
||||
blob_sizes[i] = compressed_blobs[i].size();
|
||||
}
|
||||
|
||||
@@ -77,15 +77,14 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
} else {
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(compression, opts);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
compression, sample_for_compression);
|
||||
compression);
|
||||
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
ASSERT_TRUE(CompressData(blobs[i], info, compression_format_version,
|
||||
&compressed_blobs[i]));
|
||||
ASSERT_TRUE(OLD_CompressData(blobs[i], info, compression_format_version,
|
||||
&compressed_blobs[i]));
|
||||
blobs_to_write[i] = compressed_blobs[i];
|
||||
blob_sizes[i] = compressed_blobs[i].size();
|
||||
}
|
||||
|
||||
+16
-6
@@ -74,8 +74,8 @@ Status BuildTable(
|
||||
EventLogger* event_logger, int job_id, TableProperties* table_properties,
|
||||
Env::WriteLifeTimeHint write_hint, const std::string* full_history_ts_low,
|
||||
BlobFileCompletionCallback* blob_callback, Version* version,
|
||||
uint64_t* num_input_entries, uint64_t* memtable_payload_bytes,
|
||||
uint64_t* memtable_garbage_bytes) {
|
||||
uint64_t* memtable_payload_bytes, uint64_t* memtable_garbage_bytes,
|
||||
InternalStats::CompactionStats* flush_stats) {
|
||||
assert((tboptions.column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
tboptions.column_family_name.empty());
|
||||
@@ -218,8 +218,7 @@ Status BuildTable(
|
||||
const Slice& key = c_iter.key();
|
||||
const Slice& value = c_iter.value();
|
||||
ParsedInternalKey ikey = c_iter.ikey();
|
||||
key_after_flush_buf.assign(key.data(), key.size());
|
||||
Slice key_after_flush = key_after_flush_buf;
|
||||
Slice key_after_flush = key;
|
||||
Slice value_after_flush = value;
|
||||
|
||||
if (ikey.type == kTypeValuePreferredSeqno) {
|
||||
@@ -237,6 +236,7 @@ Status BuildTable(
|
||||
std::min(smallest_preferred_seqno, preferred_seqno);
|
||||
} else {
|
||||
// Cannot get a useful preferred seqno, convert it to a kTypeValue.
|
||||
key_after_flush_buf.assign(key.data(), key.size());
|
||||
UpdateInternalKey(&key_after_flush_buf, ikey.sequence, kTypeValue);
|
||||
ikey = ParsedInternalKey(ikey.user_key, ikey.sequence, kTypeValue);
|
||||
key_after_flush = key_after_flush_buf;
|
||||
@@ -253,6 +253,10 @@ Status BuildTable(
|
||||
}
|
||||
builder->Add(key_after_flush, value_after_flush);
|
||||
|
||||
if (flush_stats) {
|
||||
flush_stats->num_output_records++;
|
||||
}
|
||||
|
||||
s = meta->UpdateBoundaries(key_after_flush, value_after_flush,
|
||||
ikey.sequence, ikey.type);
|
||||
if (!s.ok()) {
|
||||
@@ -284,6 +288,9 @@ Status BuildTable(
|
||||
auto tombstone = range_del_it->Tombstone();
|
||||
std::pair<InternalKey, Slice> kv = tombstone.Serialize();
|
||||
builder->Add(kv.first.Encode(), kv.second);
|
||||
if (flush_stats) {
|
||||
flush_stats->num_output_records++;
|
||||
}
|
||||
InternalKey tombstone_end = tombstone.SerializeEndKey();
|
||||
meta->UpdateBoundariesForRange(kv.first, tombstone_end, tombstone.seq_,
|
||||
tboptions.internal_comparator);
|
||||
@@ -305,9 +312,9 @@ Status BuildTable(
|
||||
|
||||
TEST_SYNC_POINT("BuildTable:BeforeFinishBuildTable");
|
||||
const bool empty = builder->IsEmpty();
|
||||
if (num_input_entries != nullptr) {
|
||||
if (flush_stats) {
|
||||
assert(c_iter.HasNumInputEntryScanned());
|
||||
*num_input_entries =
|
||||
flush_stats->num_input_records =
|
||||
c_iter.NumInputEntryScanned() + num_unfragmented_tombstones;
|
||||
}
|
||||
if (!s.ok() || empty) {
|
||||
@@ -334,6 +341,9 @@ Status BuildTable(
|
||||
}
|
||||
|
||||
if (s.ok() && !empty) {
|
||||
if (flush_stats) {
|
||||
flush_stats->bytes_written_pre_comp = builder->PreCompressionSize();
|
||||
}
|
||||
uint64_t file_size = builder->FileSize();
|
||||
meta->fd.file_size = file_size;
|
||||
meta->tail_size = builder->GetTailSize();
|
||||
|
||||
+5
-4
@@ -10,6 +10,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/seqno_to_time_mapping.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
@@ -34,7 +35,6 @@ class SnapshotChecker;
|
||||
class TableCache;
|
||||
class TableBuilder;
|
||||
class WritableFileWriter;
|
||||
class InternalStats;
|
||||
class BlobFileCompletionCallback;
|
||||
|
||||
// Convenience function for NewTableBuilder on the embedded table_factory.
|
||||
@@ -49,6 +49,7 @@ TableBuilder* NewTableBuilder(const TableBuilderOptions& tboptions,
|
||||
//
|
||||
// @param column_family_name Name of the column family that is also identified
|
||||
// by column_family_id, or empty string if unknown.
|
||||
// @param flush_stats treat flush as level 0 compaction in internal stats
|
||||
Status BuildTable(
|
||||
const std::string& dbname, VersionSet* versions,
|
||||
const ImmutableDBOptions& db_options, const TableBuilderOptions& tboptions,
|
||||
@@ -69,8 +70,8 @@ Status BuildTable(
|
||||
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET,
|
||||
const std::string* full_history_ts_low = nullptr,
|
||||
BlobFileCompletionCallback* blob_callback = nullptr,
|
||||
Version* version = nullptr, uint64_t* num_input_entries = nullptr,
|
||||
uint64_t* memtable_payload_bytes = nullptr,
|
||||
uint64_t* memtable_garbage_bytes = nullptr);
|
||||
Version* version = nullptr, uint64_t* memtable_payload_bytes = nullptr,
|
||||
uint64_t* memtable_garbage_bytes = nullptr,
|
||||
InternalStats::CompactionStats* flush_stats = nullptr);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -24,12 +24,14 @@
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/rate_limiter.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/sst_file_manager.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table.h"
|
||||
@@ -49,6 +51,7 @@
|
||||
#include "util/stderr_logger.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
using ROCKSDB_NAMESPACE::BackgroundErrorReason;
|
||||
using ROCKSDB_NAMESPACE::BackupEngine;
|
||||
using ROCKSDB_NAMESPACE::BackupEngineOptions;
|
||||
using ROCKSDB_NAMESPACE::BackupID;
|
||||
@@ -65,7 +68,9 @@ using ROCKSDB_NAMESPACE::ColumnFamilyMetaData;
|
||||
using ROCKSDB_NAMESPACE::ColumnFamilyOptions;
|
||||
using ROCKSDB_NAMESPACE::CompactionFilter;
|
||||
using ROCKSDB_NAMESPACE::CompactionFilterFactory;
|
||||
using ROCKSDB_NAMESPACE::CompactionJobInfo;
|
||||
using ROCKSDB_NAMESPACE::CompactionOptionsFIFO;
|
||||
using ROCKSDB_NAMESPACE::CompactionReason;
|
||||
using ROCKSDB_NAMESPACE::CompactRangeOptions;
|
||||
using ROCKSDB_NAMESPACE::Comparator;
|
||||
using ROCKSDB_NAMESPACE::CompressionType;
|
||||
@@ -76,8 +81,11 @@ using ROCKSDB_NAMESPACE::DBOptions;
|
||||
using ROCKSDB_NAMESPACE::DbPath;
|
||||
using ROCKSDB_NAMESPACE::Env;
|
||||
using ROCKSDB_NAMESPACE::EnvOptions;
|
||||
using ROCKSDB_NAMESPACE::EventListener;
|
||||
using ROCKSDB_NAMESPACE::ExternalFileIngestionInfo;
|
||||
using ROCKSDB_NAMESPACE::FileLock;
|
||||
using ROCKSDB_NAMESPACE::FilterPolicy;
|
||||
using ROCKSDB_NAMESPACE::FlushJobInfo;
|
||||
using ROCKSDB_NAMESPACE::FlushOptions;
|
||||
using ROCKSDB_NAMESPACE::HistogramData;
|
||||
using ROCKSDB_NAMESPACE::HyperClockCacheOptions;
|
||||
@@ -90,6 +98,7 @@ using ROCKSDB_NAMESPACE::Logger;
|
||||
using ROCKSDB_NAMESPACE::LRUCacheOptions;
|
||||
using ROCKSDB_NAMESPACE::MemoryAllocator;
|
||||
using ROCKSDB_NAMESPACE::MemoryUtil;
|
||||
using ROCKSDB_NAMESPACE::MemTableInfo;
|
||||
using ROCKSDB_NAMESPACE::MergeOperator;
|
||||
using ROCKSDB_NAMESPACE::NewBloomFilterPolicy;
|
||||
using ROCKSDB_NAMESPACE::NewCompactOnDeletionCollectorFactory;
|
||||
@@ -113,10 +122,12 @@ using ROCKSDB_NAMESPACE::Slice;
|
||||
using ROCKSDB_NAMESPACE::SliceParts;
|
||||
using ROCKSDB_NAMESPACE::SliceTransform;
|
||||
using ROCKSDB_NAMESPACE::Snapshot;
|
||||
using ROCKSDB_NAMESPACE::SstFileManager;
|
||||
using ROCKSDB_NAMESPACE::SstFileMetaData;
|
||||
using ROCKSDB_NAMESPACE::SstFileWriter;
|
||||
using ROCKSDB_NAMESPACE::Status;
|
||||
using ROCKSDB_NAMESPACE::StderrLogger;
|
||||
using ROCKSDB_NAMESPACE::SubcompactionJobInfo;
|
||||
using ROCKSDB_NAMESPACE::TablePropertiesCollectorFactory;
|
||||
using ROCKSDB_NAMESPACE::Transaction;
|
||||
using ROCKSDB_NAMESPACE::TransactionDB;
|
||||
@@ -130,6 +141,8 @@ using ROCKSDB_NAMESPACE::WriteBatch;
|
||||
using ROCKSDB_NAMESPACE::WriteBatchWithIndex;
|
||||
using ROCKSDB_NAMESPACE::WriteBufferManager;
|
||||
using ROCKSDB_NAMESPACE::WriteOptions;
|
||||
using ROCKSDB_NAMESPACE::WriteStallCondition;
|
||||
using ROCKSDB_NAMESPACE::WriteStallInfo;
|
||||
|
||||
using std::unordered_set;
|
||||
using std::vector;
|
||||
@@ -139,6 +152,9 @@ extern "C" {
|
||||
struct rocksdb_t {
|
||||
DB* rep;
|
||||
};
|
||||
struct rocksdb_status_ptr_t {
|
||||
Status* rep;
|
||||
};
|
||||
struct rocksdb_backup_engine_t {
|
||||
BackupEngine* rep;
|
||||
};
|
||||
@@ -226,6 +242,9 @@ struct rocksdb_cache_t {
|
||||
struct rocksdb_write_buffer_manager_t {
|
||||
std::shared_ptr<WriteBufferManager> rep;
|
||||
};
|
||||
struct rocksdb_sst_file_manager_t {
|
||||
std::shared_ptr<SstFileManager> rep;
|
||||
};
|
||||
struct rocksdb_livefiles_t {
|
||||
std::vector<LiveFileMetaData> rep;
|
||||
};
|
||||
@@ -292,6 +311,28 @@ struct rocksdb_compactionfiltercontext_t {
|
||||
CompactionFilter::Context rep;
|
||||
};
|
||||
|
||||
struct rocksdb_flushjobinfo_t {
|
||||
FlushJobInfo rep;
|
||||
};
|
||||
struct rocksdb_writestallcondition_t {
|
||||
WriteStallCondition rep;
|
||||
};
|
||||
struct rocksdb_writestallinfo_t {
|
||||
WriteStallInfo rep;
|
||||
};
|
||||
struct rocksdb_memtableinfo_t {
|
||||
MemTableInfo rep;
|
||||
};
|
||||
struct rocksdb_compactionjobinfo_t {
|
||||
CompactionJobInfo rep;
|
||||
};
|
||||
struct rocksdb_subcompactionjobinfo_t {
|
||||
SubcompactionJobInfo rep;
|
||||
};
|
||||
struct rocksdb_externalfileingestioninfo_t {
|
||||
ExternalFileIngestionInfo rep;
|
||||
};
|
||||
|
||||
struct rocksdb_statistics_histogram_data_t {
|
||||
rocksdb_statistics_histogram_data_t() : rep() {}
|
||||
HistogramData rep;
|
||||
@@ -884,6 +925,10 @@ void rocksdb_backup_engine_options_destroy(
|
||||
delete options;
|
||||
}
|
||||
|
||||
void rocksdb_status_ptr_get_error(rocksdb_status_ptr_t* status, char** errptr) {
|
||||
SaveError(errptr, *(status->rep));
|
||||
}
|
||||
|
||||
rocksdb_checkpoint_t* rocksdb_checkpoint_object_create(rocksdb_t* db,
|
||||
char** errptr) {
|
||||
Checkpoint* checkpoint;
|
||||
@@ -2627,6 +2672,16 @@ rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base_readopts(
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_iterator_t* base_iterator,
|
||||
const rocksdb_readoptions_t* options) {
|
||||
rocksdb_iterator_t* result = new rocksdb_iterator_t;
|
||||
result->rep =
|
||||
wbwi->rep->NewIteratorWithBase(base_iterator->rep, &options->rep);
|
||||
delete base_iterator;
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base_cf(
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_iterator_t* base_iterator,
|
||||
rocksdb_column_family_handle_t* column_family) {
|
||||
@@ -2637,6 +2692,17 @@ rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base_cf(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base_cf_readopts(
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_iterator_t* base_iterator,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const rocksdb_readoptions_t* options) {
|
||||
rocksdb_iterator_t* result = new rocksdb_iterator_t;
|
||||
result->rep = wbwi->rep->NewIteratorWithBase(
|
||||
column_family->rep, base_iterator->rep, &options->rep);
|
||||
delete base_iterator;
|
||||
return result;
|
||||
}
|
||||
|
||||
char* rocksdb_writebatch_wi_get_from_batch(rocksdb_writebatch_wi_t* wbwi,
|
||||
const rocksdb_options_t* options,
|
||||
const char* key, size_t keylen,
|
||||
@@ -2696,6 +2762,23 @@ char* rocksdb_writebatch_wi_get_from_batch_and_db(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_pinnableslice_t* rocksdb_writebatch_wi_get_pinned_from_batch_and_db(
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options, const char* key, size_t keylen,
|
||||
char** errptr) {
|
||||
rocksdb_pinnableslice_t* v = new (rocksdb_pinnableslice_t);
|
||||
Status s = wbwi->rep->GetFromBatchAndDB(db->rep, options->rep,
|
||||
Slice(key, keylen), &v->rep);
|
||||
if (!s.ok()) {
|
||||
delete (v);
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
char* rocksdb_writebatch_wi_get_from_batch_and_db_cf(
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
@@ -2717,6 +2800,24 @@ char* rocksdb_writebatch_wi_get_from_batch_and_db_cf(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_pinnableslice_t* rocksdb_writebatch_wi_get_pinned_from_batch_and_db_cf(
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, char** errptr) {
|
||||
rocksdb_pinnableslice_t* v = new (rocksdb_pinnableslice_t);
|
||||
Status s = wbwi->rep->GetFromBatchAndDB(
|
||||
db->rep, options->rep, column_family->rep, Slice(key, keylen), &v->rep);
|
||||
if (!s.ok()) {
|
||||
delete (v);
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void rocksdb_write_writebatch_wi(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_writebatch_wi_t* wbwi, char** errptr) {
|
||||
@@ -2930,6 +3031,361 @@ void rocksdb_block_based_options_set_unpartitioned_pinning_tier(
|
||||
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
|
||||
}
|
||||
|
||||
/* FlushJobInfo */
|
||||
|
||||
const char* rocksdb_flushjobinfo_cf_name(const rocksdb_flushjobinfo_t* info,
|
||||
size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_flushjobinfo_file_path(const rocksdb_flushjobinfo_t* info,
|
||||
size_t* size) {
|
||||
*size = info->rep.file_path.size();
|
||||
return info->rep.file_path.data();
|
||||
}
|
||||
|
||||
unsigned char rocksdb_flushjobinfo_triggered_writes_slowdown(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.triggered_writes_slowdown;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_flushjobinfo_triggered_writes_stop(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.triggered_writes_stop;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_flushjobinfo_largest_seqno(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.largest_seqno;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_flushjobinfo_smallest_seqno(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.smallest_seqno;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_flushjobinfo_flush_reason(const rocksdb_flushjobinfo_t* info) {
|
||||
return static_cast<uint32_t>(info->rep.flush_reason);
|
||||
}
|
||||
|
||||
void rocksdb_reset_status(rocksdb_status_ptr_t* status_ptr) {
|
||||
auto ptr = status_ptr->rep;
|
||||
*ptr = Status::OK();
|
||||
}
|
||||
|
||||
/* CompactionJobInfo */
|
||||
|
||||
void rocksdb_compactionjobinfo_status(const rocksdb_compactionjobinfo_t* info,
|
||||
char** errptr) {
|
||||
SaveError(errptr, info->rep.status);
|
||||
}
|
||||
|
||||
const char* rocksdb_compactionjobinfo_cf_name(
|
||||
const rocksdb_compactionjobinfo_t* info, size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
size_t rocksdb_compactionjobinfo_input_files_count(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.input_files.size();
|
||||
}
|
||||
|
||||
const char* rocksdb_compactionjobinfo_input_file_at(
|
||||
const rocksdb_compactionjobinfo_t* info, size_t pos, size_t* size) {
|
||||
assert(info != nullptr);
|
||||
assert(pos < info->rep.input_files.size());
|
||||
|
||||
const std::string& path = info->rep.input_files[pos];
|
||||
*size = path.size();
|
||||
return path.data();
|
||||
}
|
||||
|
||||
size_t rocksdb_compactionjobinfo_output_files_count(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.output_files.size();
|
||||
}
|
||||
|
||||
const char* rocksdb_compactionjobinfo_output_file_at(
|
||||
const rocksdb_compactionjobinfo_t* info, size_t pos, size_t* size) {
|
||||
assert(info != nullptr);
|
||||
assert(pos < info->rep.output_files.size());
|
||||
|
||||
const std::string& path = info->rep.output_files[pos];
|
||||
*size = path.size();
|
||||
return path.data();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_elapsed_micros(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.elapsed_micros;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_num_corrupt_keys(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_corrupt_keys;
|
||||
}
|
||||
|
||||
int rocksdb_compactionjobinfo_base_input_level(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.base_input_level;
|
||||
}
|
||||
|
||||
int rocksdb_compactionjobinfo_output_level(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.output_level;
|
||||
}
|
||||
|
||||
size_t rocksdb_compactionjobinfo_num_input_files(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_input_files;
|
||||
}
|
||||
|
||||
size_t rocksdb_compactionjobinfo_num_input_files_at_output_level(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_input_files_at_output_level;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_input_records(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_input_records;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_output_records(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_output_records;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_total_input_bytes(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.total_input_bytes;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_total_output_bytes(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.total_output_bytes;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_compactionjobinfo_compaction_reason(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return static_cast<uint32_t>(info->rep.compaction_reason);
|
||||
}
|
||||
|
||||
/* SubcompactionJobInfo */
|
||||
|
||||
void rocksdb_subcompactionjobinfo_status(
|
||||
const rocksdb_subcompactionjobinfo_t* info, char** errptr) {
|
||||
SaveError(errptr, info->rep.status);
|
||||
}
|
||||
|
||||
const char* rocksdb_subcompactionjobinfo_cf_name(
|
||||
const rocksdb_subcompactionjobinfo_t* info, size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_subcompactionjobinfo_thread_id(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return info->rep.thread_id;
|
||||
}
|
||||
|
||||
int rocksdb_subcompactionjobinfo_base_input_level(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return info->rep.base_input_level;
|
||||
}
|
||||
|
||||
int rocksdb_subcompactionjobinfo_output_level(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return info->rep.output_level;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_subcompactionjobinfo_compaction_reason(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return static_cast<uint32_t>(info->rep.compaction_reason);
|
||||
}
|
||||
|
||||
/* ExternalFileIngestionInfo */
|
||||
|
||||
const char* rocksdb_externalfileingestioninfo_cf_name(
|
||||
const rocksdb_externalfileingestioninfo_t* info, size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_externalfileingestioninfo_internal_file_path(
|
||||
const rocksdb_externalfileingestioninfo_t* info, size_t* size) {
|
||||
*size = info->rep.internal_file_path.size();
|
||||
return info->rep.internal_file_path.data();
|
||||
}
|
||||
|
||||
/* External write stall info */
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_writestallinfo_cf_name(
|
||||
const rocksdb_writestallinfo_t* info, size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
const rocksdb_writestallcondition_t* rocksdb_writestallinfo_cur(
|
||||
const rocksdb_writestallinfo_t* info) {
|
||||
return reinterpret_cast<const rocksdb_writestallcondition_t*>(
|
||||
&info->rep.condition.cur);
|
||||
}
|
||||
|
||||
const rocksdb_writestallcondition_t* rocksdb_writestallinfo_prev(
|
||||
const rocksdb_writestallinfo_t* info) {
|
||||
return reinterpret_cast<const rocksdb_writestallcondition_t*>(
|
||||
&info->rep.condition.prev);
|
||||
}
|
||||
|
||||
const char* rocksdb_memtableinfo_cf_name(const rocksdb_memtableinfo_t* info,
|
||||
size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_memtableinfo_first_seqno(const rocksdb_memtableinfo_t* info) {
|
||||
return info->rep.first_seqno;
|
||||
}
|
||||
uint64_t rocksdb_memtableinfo_earliest_seqno(
|
||||
const rocksdb_memtableinfo_t* info) {
|
||||
return info->rep.earliest_seqno;
|
||||
}
|
||||
uint64_t rocksdb_memtableinfo_num_entries(const rocksdb_memtableinfo_t* info) {
|
||||
return info->rep.num_entries;
|
||||
}
|
||||
uint64_t rocksdb_memtableinfo_num_deletes(const rocksdb_memtableinfo_t* info) {
|
||||
return info->rep.num_deletes;
|
||||
}
|
||||
|
||||
/* event listener */
|
||||
|
||||
struct rocksdb_eventlistener_t : public EventListener {
|
||||
void* state_{};
|
||||
void (*destructor_)(void*){};
|
||||
void (*on_flush_begin)(void*, rocksdb_t*, const rocksdb_flushjobinfo_t*){};
|
||||
void (*on_flush_completed)(void*, rocksdb_t*,
|
||||
const rocksdb_flushjobinfo_t*){};
|
||||
void (*on_compaction_begin)(void*, rocksdb_t*,
|
||||
const rocksdb_compactionjobinfo_t*){};
|
||||
void (*on_compaction_completed)(void*, rocksdb_t*,
|
||||
const rocksdb_compactionjobinfo_t*){};
|
||||
void (*on_subcompaction_begin)(void*,
|
||||
const rocksdb_subcompactionjobinfo_t*){};
|
||||
void (*on_subcompaction_completed)(void*,
|
||||
const rocksdb_subcompactionjobinfo_t*){};
|
||||
void (*on_external_file_ingested)(
|
||||
void*, rocksdb_t*, const rocksdb_externalfileingestioninfo_t*){};
|
||||
void (*on_background_error)(void*, uint32_t, rocksdb_status_ptr_t*){};
|
||||
void (*on_stall_conditions_changed)(void*, const rocksdb_writestallinfo_t*){};
|
||||
void (*on_memtable_sealed)(void*, const rocksdb_memtableinfo_t*){};
|
||||
|
||||
rocksdb_eventlistener_t() = default;
|
||||
|
||||
rocksdb_eventlistener_t(const rocksdb_eventlistener_t&) = delete;
|
||||
rocksdb_eventlistener_t& operator=(const rocksdb_eventlistener_t&) = delete;
|
||||
rocksdb_eventlistener_t(rocksdb_eventlistener_t&&) = delete;
|
||||
rocksdb_eventlistener_t& operator=(rocksdb_eventlistener_t&&) = delete;
|
||||
|
||||
void OnFlushBegin(DB* db, const FlushJobInfo& info) override {
|
||||
rocksdb_t c_db = {db};
|
||||
on_flush_begin(state_, &c_db,
|
||||
reinterpret_cast<const rocksdb_flushjobinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnFlushCompleted(DB* db, const FlushJobInfo& info) override {
|
||||
rocksdb_t c_db = {db};
|
||||
on_flush_completed(state_, &c_db,
|
||||
reinterpret_cast<const rocksdb_flushjobinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnCompactionBegin(DB* db, const CompactionJobInfo& info) override {
|
||||
rocksdb_t c_db = {db};
|
||||
on_compaction_begin(
|
||||
state_, &c_db,
|
||||
reinterpret_cast<const rocksdb_compactionjobinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnCompactionCompleted(DB* db, const CompactionJobInfo& info) override {
|
||||
rocksdb_t c_db = {db};
|
||||
on_compaction_completed(
|
||||
state_, &c_db,
|
||||
reinterpret_cast<const rocksdb_compactionjobinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnSubcompactionBegin(const SubcompactionJobInfo& info) override {
|
||||
on_subcompaction_begin(
|
||||
state_, reinterpret_cast<const rocksdb_subcompactionjobinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnSubcompactionCompleted(const SubcompactionJobInfo& info) override {
|
||||
on_subcompaction_completed(
|
||||
state_, reinterpret_cast<const rocksdb_subcompactionjobinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnExternalFileIngested(DB* db,
|
||||
const ExternalFileIngestionInfo& info) override {
|
||||
rocksdb_t c_db = {db};
|
||||
on_external_file_ingested(
|
||||
state_, &c_db,
|
||||
reinterpret_cast<const rocksdb_externalfileingestioninfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnBackgroundError(BackgroundErrorReason reason,
|
||||
Status* status) override {
|
||||
rocksdb_status_ptr_t* s = new rocksdb_status_ptr_t;
|
||||
s->rep = status;
|
||||
on_background_error(state_, static_cast<uint32_t>(reason), s);
|
||||
delete s;
|
||||
}
|
||||
|
||||
void OnStallConditionsChanged(const WriteStallInfo& info) override {
|
||||
on_stall_conditions_changed(
|
||||
state_, reinterpret_cast<const rocksdb_writestallinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnMemTableSealed(const MemTableInfo& info) override {
|
||||
on_memtable_sealed(state_,
|
||||
reinterpret_cast<const rocksdb_memtableinfo_t*>(&info));
|
||||
}
|
||||
|
||||
~rocksdb_eventlistener_t() override { destructor_(state_); }
|
||||
};
|
||||
|
||||
rocksdb_eventlistener_t* rocksdb_eventlistener_create(
|
||||
void* state_, void (*destructor_)(void*), on_flush_begin_cb on_flush_begin,
|
||||
on_flush_completed_cb on_flush_completed,
|
||||
on_compaction_begin_cb on_compaction_begin,
|
||||
on_compaction_completed_cb on_compaction_completed,
|
||||
on_subcompaction_begin_cb on_subcompaction_begin,
|
||||
on_subcompaction_completed_cb on_subcompaction_completed,
|
||||
on_external_file_ingested_cb on_external_file_ingested,
|
||||
on_background_error_cb on_background_error,
|
||||
on_stall_conditions_changed_cb on_stall_conditions_changed,
|
||||
on_memtable_sealed_cb on_memtable_sealed) {
|
||||
rocksdb_eventlistener_t* et = new rocksdb_eventlistener_t;
|
||||
et->state_ = state_;
|
||||
et->destructor_ = destructor_;
|
||||
et->on_flush_begin = on_flush_begin;
|
||||
et->on_flush_completed = on_flush_completed;
|
||||
et->on_compaction_begin = on_compaction_begin;
|
||||
et->on_compaction_completed = on_compaction_completed;
|
||||
et->on_subcompaction_begin = on_subcompaction_begin;
|
||||
et->on_subcompaction_completed = on_subcompaction_completed;
|
||||
et->on_external_file_ingested = on_external_file_ingested;
|
||||
et->on_background_error = on_background_error;
|
||||
et->on_stall_conditions_changed = on_stall_conditions_changed;
|
||||
et->on_memtable_sealed = on_memtable_sealed;
|
||||
return et;
|
||||
}
|
||||
|
||||
void rocksdb_eventlistener_destroy(rocksdb_eventlistener_t* t) { delete t; }
|
||||
|
||||
void rocksdb_options_add_eventlistener(rocksdb_options_t* opt,
|
||||
rocksdb_eventlistener_t* t) {
|
||||
opt->rep.listeners.emplace_back(std::shared_ptr<EventListener>(t));
|
||||
}
|
||||
|
||||
rocksdb_cuckoo_table_options_t* rocksdb_cuckoo_options_create() {
|
||||
return new rocksdb_cuckoo_table_options_t;
|
||||
}
|
||||
@@ -3305,6 +3761,16 @@ uint32_t rocksdb_options_get_memtable_op_scan_flush_trigger(
|
||||
return opt->rep.memtable_op_scan_flush_trigger;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_memtable_avg_op_scan_flush_trigger(
|
||||
rocksdb_options_t* opt, uint32_t n) {
|
||||
opt->rep.memtable_avg_op_scan_flush_trigger = n;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_options_get_memtable_avg_op_scan_flush_trigger(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.memtable_avg_op_scan_flush_trigger;
|
||||
}
|
||||
|
||||
void rocksdb_options_enable_statistics(rocksdb_options_t* opt) {
|
||||
opt->rep.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
}
|
||||
@@ -4332,6 +4798,8 @@ uint64_t rocksdb_perfcontext_metric(rocksdb_perfcontext_t* context,
|
||||
return rep->internal_recent_skipped_count;
|
||||
case rocksdb_internal_merge_count:
|
||||
return rep->internal_merge_count;
|
||||
case rocksdb_internal_merge_point_lookup_count:
|
||||
return rep->internal_merge_point_lookup_count;
|
||||
case rocksdb_get_snapshot_time:
|
||||
return rep->get_snapshot_time;
|
||||
case rocksdb_get_from_memtable_time:
|
||||
@@ -5239,6 +5707,67 @@ ROCKSDB_LIBRARY_API void rocksdb_write_buffer_manager_set_allow_stall(
|
||||
wbm->rep->SetAllowStall(new_allow_stall);
|
||||
}
|
||||
|
||||
rocksdb_sst_file_manager_t* rocksdb_sst_file_manager_create(
|
||||
rocksdb_env_t* env) {
|
||||
rocksdb_sst_file_manager_t* sfm = new rocksdb_sst_file_manager_t;
|
||||
sfm->rep.reset(ROCKSDB_NAMESPACE::NewSstFileManager(env->rep));
|
||||
return sfm;
|
||||
}
|
||||
|
||||
void rocksdb_sst_file_manager_destroy(rocksdb_sst_file_manager_t* sfm) {
|
||||
delete sfm;
|
||||
}
|
||||
|
||||
void rocksdb_sst_file_manager_set_max_allowed_space_usage(
|
||||
rocksdb_sst_file_manager_t* sfm, uint64_t max_allowed_space) {
|
||||
sfm->rep->SetMaxAllowedSpaceUsage(max_allowed_space);
|
||||
}
|
||||
|
||||
void rocksdb_sst_file_manager_set_compaction_buffer_size(
|
||||
rocksdb_sst_file_manager_t* sfm, uint64_t compaction_buffer_size) {
|
||||
sfm->rep->SetCompactionBufferSize(compaction_buffer_size);
|
||||
}
|
||||
|
||||
bool rocksdb_sst_file_manager_is_max_allowed_space_reached(
|
||||
rocksdb_sst_file_manager_t* sfm) {
|
||||
return sfm->rep->IsMaxAllowedSpaceReached();
|
||||
}
|
||||
|
||||
bool rocksdb_sst_file_manager_is_max_allowed_space_reached_including_compactions(
|
||||
rocksdb_sst_file_manager_t* sfm) {
|
||||
return sfm->rep->IsMaxAllowedSpaceReachedIncludingCompactions();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_sst_file_manager_get_total_size(
|
||||
rocksdb_sst_file_manager_t* sfm) {
|
||||
return sfm->rep->GetTotalSize();
|
||||
}
|
||||
|
||||
int64_t rocksdb_sst_file_manager_get_delete_rate_bytes_per_second(
|
||||
rocksdb_sst_file_manager_t* sfm) {
|
||||
return sfm->rep->GetDeleteRateBytesPerSecond();
|
||||
}
|
||||
|
||||
void rocksdb_sst_file_manager_set_delete_rate_bytes_per_second(
|
||||
rocksdb_sst_file_manager_t* sfm, int64_t delete_rate) {
|
||||
return sfm->rep->SetDeleteRateBytesPerSecond(delete_rate);
|
||||
}
|
||||
|
||||
double rocksdb_sst_file_manager_get_max_trash_db_ratio(
|
||||
rocksdb_sst_file_manager_t* sfm) {
|
||||
return sfm->rep->GetMaxTrashDBRatio();
|
||||
}
|
||||
|
||||
void rocksdb_sst_file_manager_set_max_trash_db_ratio(
|
||||
rocksdb_sst_file_manager_t* sfm, double ratio) {
|
||||
return sfm->rep->SetMaxTrashDBRatio(ratio);
|
||||
}
|
||||
|
||||
uint64_t rocksdb_sst_file_manager_get_total_trash_size(
|
||||
rocksdb_sst_file_manager_t* sfm) {
|
||||
return sfm->rep->GetTotalTrashSize();
|
||||
}
|
||||
|
||||
rocksdb_dbpath_t* rocksdb_dbpath_create(const char* path,
|
||||
uint64_t target_size) {
|
||||
rocksdb_dbpath_t* result = new rocksdb_dbpath_t;
|
||||
|
||||
+114
-12
@@ -103,6 +103,12 @@ static void CheckValue(char* err, const char* expected, char** actual,
|
||||
Free(actual);
|
||||
}
|
||||
|
||||
static void CheckPinnedValue(char* err, const char* expected,
|
||||
const char** actual, size_t actual_length) {
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, *actual, actual_length);
|
||||
}
|
||||
|
||||
static void CheckGet(rocksdb_t* db, const rocksdb_readoptions_t* options,
|
||||
const char* key, const char* expected) {
|
||||
char* err = NULL;
|
||||
@@ -1245,6 +1251,8 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(count == 3);
|
||||
size_t size;
|
||||
char* value;
|
||||
const char* pinned_value;
|
||||
rocksdb_pinnableslice_t* p;
|
||||
value = rocksdb_writebatch_wi_get_from_batch(wbi, options, "box", 3, &size,
|
||||
&err);
|
||||
CheckValue(err, "c", &value, size);
|
||||
@@ -1254,9 +1262,19 @@ int main(int argc, char** argv) {
|
||||
value = rocksdb_writebatch_wi_get_from_batch_and_db(wbi, db, roptions,
|
||||
"foo", 3, &size, &err);
|
||||
CheckValue(err, "hello", &value, size);
|
||||
p = rocksdb_writebatch_wi_get_pinned_from_batch_and_db(wbi, db, roptions,
|
||||
"foo", 3, &err);
|
||||
pinned_value = rocksdb_pinnableslice_value(p, &size);
|
||||
CheckPinnedValue(err, "hello", &pinned_value, size);
|
||||
rocksdb_pinnableslice_destroy(p);
|
||||
value = rocksdb_writebatch_wi_get_from_batch_and_db(wbi, db, roptions,
|
||||
"box", 3, &size, &err);
|
||||
CheckValue(err, "c", &value, size);
|
||||
p = rocksdb_writebatch_wi_get_pinned_from_batch_and_db(wbi, db, roptions,
|
||||
"box", 3, &err);
|
||||
pinned_value = rocksdb_pinnableslice_value(p, &size);
|
||||
CheckPinnedValue(err, "c", &pinned_value, size);
|
||||
rocksdb_pinnableslice_destroy(p);
|
||||
rocksdb_write_writebatch_wi(db, woptions, wbi, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
@@ -1362,6 +1380,46 @@ int main(int argc, char** argv) {
|
||||
rocksdb_writebatch_wi_destroy(wbi);
|
||||
}
|
||||
|
||||
StartPhase("wbwi_iter_readoptions");
|
||||
{
|
||||
rocksdb_readoptions_t* iter_roptions = rocksdb_readoptions_create();
|
||||
rocksdb_readoptions_set_iterate_lower_bound(iter_roptions, "boy", 3);
|
||||
rocksdb_readoptions_set_iterate_upper_bound(iter_roptions, "fool", 4);
|
||||
rocksdb_iterator_t* base_iter = rocksdb_create_iterator(db, iter_roptions);
|
||||
rocksdb_writebatch_wi_t* wbi = rocksdb_writebatch_wi_create(0, 1);
|
||||
rocksdb_writebatch_wi_put(wbi, "bar", 3, "b",
|
||||
1); // should get filtered out
|
||||
rocksdb_writebatch_wi_put(wbi, "cat", 3, "miau", 4);
|
||||
rocksdb_writebatch_wi_put(wbi, "gnu", 3, "muh",
|
||||
3); // should get filtered out
|
||||
rocksdb_iterator_t* iter =
|
||||
rocksdb_writebatch_wi_create_iterator_with_base_readopts(wbi, base_iter,
|
||||
iter_roptions);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_seek_to_first(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "cat", "miau");
|
||||
rocksdb_iter_next(iter);
|
||||
CheckIter(iter, "foo", "hello");
|
||||
rocksdb_iter_prev(iter);
|
||||
CheckIter(iter, "cat", "miau");
|
||||
rocksdb_iter_prev(iter);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_seek_to_last(iter);
|
||||
CheckIter(iter, "foo", "hello");
|
||||
rocksdb_iter_seek(iter, "b", 1);
|
||||
CheckIter(iter, "cat", "miau");
|
||||
rocksdb_iter_seek_for_prev(iter, "d", 1);
|
||||
CheckIter(iter, "cat", "miau");
|
||||
rocksdb_iter_seek_for_prev(iter, "fool", 3);
|
||||
CheckIter(iter, "foo", "hello");
|
||||
rocksdb_iter_get_error(iter, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_iter_destroy(iter);
|
||||
rocksdb_writebatch_wi_destroy(wbi);
|
||||
rocksdb_readoptions_destroy(iter_roptions);
|
||||
}
|
||||
|
||||
StartPhase("multiget");
|
||||
{
|
||||
const char* keys[3] = {"box", "foo", "notfound"};
|
||||
@@ -1792,6 +1850,35 @@ int main(int argc, char** argv) {
|
||||
rocksdb_flush_wal(db, 1, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// Test WriteBatchWithIndex iteration with Column Family
|
||||
rocksdb_writebatch_wi_t* wbwi = rocksdb_writebatch_wi_create(0, true);
|
||||
rocksdb_writebatch_wi_put_cf(wbwi, handles[1], "boat", 4, "row",
|
||||
3); // should be filtered out
|
||||
rocksdb_writebatch_wi_put_cf(wbwi, handles[1], "buffy", 5, "charmed", 7);
|
||||
rocksdb_writebatch_wi_put_cf(wbwi, handles[1], "bus", 3, "yellow",
|
||||
6); // should be filtered out
|
||||
rocksdb_readoptions_t* iter_roptions = rocksdb_readoptions_create();
|
||||
rocksdb_readoptions_set_iterate_lower_bound(iter_roptions, "bu", 2);
|
||||
rocksdb_readoptions_set_iterate_upper_bound(iter_roptions, "buffz", 5);
|
||||
rocksdb_iterator_t* base_iter =
|
||||
rocksdb_create_iterator_cf(db, iter_roptions, handles[1]);
|
||||
rocksdb_iterator_t* wbwi_iter =
|
||||
rocksdb_writebatch_wi_create_iterator_with_base_cf_readopts(
|
||||
wbwi, base_iter, handles[1], iter_roptions);
|
||||
|
||||
CheckCondition(!rocksdb_iter_valid(wbwi_iter));
|
||||
rocksdb_iter_seek_to_first(wbwi_iter);
|
||||
CheckCondition(rocksdb_iter_valid(wbwi_iter));
|
||||
CheckIter(wbwi_iter, "buff", "rocksdb");
|
||||
rocksdb_iter_next(wbwi_iter);
|
||||
CheckIter(wbwi_iter, "buffy", "charmed");
|
||||
rocksdb_iter_next(wbwi_iter);
|
||||
CheckCondition(!rocksdb_iter_valid(wbwi_iter));
|
||||
|
||||
rocksdb_iter_destroy(wbwi_iter);
|
||||
rocksdb_writebatch_wi_destroy(wbwi);
|
||||
rocksdb_readoptions_destroy(iter_roptions);
|
||||
|
||||
const char* keys[3] = {"box", "box", "barfooxx"};
|
||||
const rocksdb_column_family_handle_t* get_handles[3] = {
|
||||
handles[0], handles[1], handles[1]};
|
||||
@@ -2133,16 +2220,16 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(100 ==
|
||||
rocksdb_options_get_memtable_op_scan_flush_trigger(o));
|
||||
|
||||
rocksdb_options_set_memtable_avg_op_scan_flush_trigger(o, 150);
|
||||
CheckCondition(150 ==
|
||||
rocksdb_options_get_memtable_avg_op_scan_flush_trigger(o));
|
||||
|
||||
rocksdb_options_set_ttl(o, 5000);
|
||||
CheckCondition(5000 == rocksdb_options_get_ttl(o));
|
||||
|
||||
rocksdb_options_set_skip_stats_update_on_db_open(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(o, 1);
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number(o, 97);
|
||||
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(o));
|
||||
|
||||
@@ -2402,8 +2489,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(2.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(copy));
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(copy));
|
||||
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(copy));
|
||||
CheckCondition(23 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(copy));
|
||||
@@ -2576,6 +2661,12 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(100 ==
|
||||
rocksdb_options_get_memtable_op_scan_flush_trigger(o));
|
||||
|
||||
rocksdb_options_set_memtable_avg_op_scan_flush_trigger(copy, 900);
|
||||
CheckCondition(
|
||||
900 == rocksdb_options_get_memtable_avg_op_scan_flush_trigger(copy));
|
||||
CheckCondition(150 ==
|
||||
rocksdb_options_get_memtable_avg_op_scan_flush_trigger(o));
|
||||
|
||||
rocksdb_options_set_ttl(copy, 8000);
|
||||
CheckCondition(8000 == rocksdb_options_get_ttl(copy));
|
||||
CheckCondition(5000 == rocksdb_options_get_ttl(o));
|
||||
@@ -2584,12 +2675,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(0 == rocksdb_options_get_skip_stats_update_on_db_open(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(copy, 0);
|
||||
CheckCondition(
|
||||
0 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(copy));
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number(copy, 2000);
|
||||
CheckCondition(2000 == rocksdb_options_get_max_write_buffer_number(copy));
|
||||
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(o));
|
||||
@@ -4050,6 +4135,23 @@ int main(int argc, char** argv) {
|
||||
rocksdb_cache_destroy(lru);
|
||||
}
|
||||
|
||||
StartPhase("sst_file_manager");
|
||||
{
|
||||
rocksdb_sst_file_manager_t* sst_file_manager;
|
||||
sst_file_manager = rocksdb_sst_file_manager_create(env);
|
||||
rocksdb_sst_file_manager_set_delete_rate_bytes_per_second(sst_file_manager,
|
||||
1);
|
||||
rocksdb_sst_file_manager_set_max_trash_db_ratio(sst_file_manager, 0.75);
|
||||
|
||||
CheckCondition(1 ==
|
||||
rocksdb_sst_file_manager_get_delete_rate_bytes_per_second(
|
||||
sst_file_manager));
|
||||
CheckCondition(0.75 == rocksdb_sst_file_manager_get_max_trash_db_ratio(
|
||||
sst_file_manager));
|
||||
|
||||
rocksdb_sst_file_manager_destroy(sst_file_manager);
|
||||
}
|
||||
|
||||
StartPhase("cancel_all_background_work");
|
||||
rocksdb_cancel_all_background_work(db, 1);
|
||||
|
||||
|
||||
+55
-18
@@ -110,23 +110,48 @@ void GetInternalTblPropCollFactory(
|
||||
}
|
||||
}
|
||||
|
||||
Status CheckCompressionSupportedWithManager(
|
||||
CompressionType type, UnownedPtr<CompressionManager> mgr) {
|
||||
if (mgr) {
|
||||
if (!mgr->SupportsCompressionType(type)) {
|
||||
return Status::NotSupported("Compression type " +
|
||||
CompressionTypeToString(type) +
|
||||
" is not recognized/supported by this "
|
||||
"version of CompressionManager " +
|
||||
mgr->GetId());
|
||||
}
|
||||
} else {
|
||||
if (!CompressionTypeSupported(type)) {
|
||||
if (type <= kLastBuiltinCompression) {
|
||||
return Status::InvalidArgument("Compression type " +
|
||||
CompressionTypeToString(type) +
|
||||
" is not linked with the binary.");
|
||||
} else {
|
||||
return Status::NotSupported(
|
||||
"Compression type " + CompressionTypeToString(type) +
|
||||
" is not recognized/supported by built-in CompressionManager.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options) {
|
||||
if (!cf_options.compression_per_level.empty()) {
|
||||
for (size_t level = 0; level < cf_options.compression_per_level.size();
|
||||
++level) {
|
||||
if (!CompressionTypeSupported(cf_options.compression_per_level[level])) {
|
||||
return Status::InvalidArgument(
|
||||
"Compression type " +
|
||||
CompressionTypeToString(cf_options.compression_per_level[level]) +
|
||||
" is not linked with the binary.");
|
||||
Status s = CheckCompressionSupportedWithManager(
|
||||
cf_options.compression_per_level[level],
|
||||
cf_options.compression_manager.get());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!CompressionTypeSupported(cf_options.compression)) {
|
||||
return Status::InvalidArgument(
|
||||
"Compression type " +
|
||||
CompressionTypeToString(cf_options.compression) +
|
||||
" is not linked with the binary.");
|
||||
Status s = CheckCompressionSupportedWithManager(
|
||||
cf_options.compression, cf_options.compression_manager.get());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
if (cf_options.compression_opts.zstd_max_train_bytes > 0) {
|
||||
@@ -448,13 +473,21 @@ ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
|
||||
result.preclude_last_level_data_seconds = 0;
|
||||
}
|
||||
|
||||
if (read_only && result.memtable_op_scan_flush_trigger != 0) {
|
||||
ROCKS_LOG_WARN(db_options.info_log.get(),
|
||||
"option memtable_op_scan_flush_trigger is sanitized to "
|
||||
"0(disabled) for read only DB.");
|
||||
result.memtable_op_scan_flush_trigger = 0;
|
||||
if (read_only) {
|
||||
if (result.memtable_op_scan_flush_trigger) {
|
||||
ROCKS_LOG_WARN(db_options.info_log.get(),
|
||||
"option memtable_op_scan_flush_trigger is sanitized to "
|
||||
"0(disabled) for read only DB.");
|
||||
result.memtable_op_scan_flush_trigger = 0;
|
||||
}
|
||||
if (result.memtable_avg_op_scan_flush_trigger) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options.info_log.get(),
|
||||
"option memtable_avg_op_scan_flush_trigger is sanitized to "
|
||||
"0(disabled) for read only DB.");
|
||||
result.memtable_avg_op_scan_flush_trigger = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1209,10 +1242,12 @@ Compaction* ColumnFamilyData::PickCompaction(
|
||||
const MutableCFOptions& mutable_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, LogBuffer* log_buffer) {
|
||||
const SnapshotChecker* snapshot_checker, LogBuffer* log_buffer,
|
||||
bool require_max_output_level) {
|
||||
auto* result = compaction_picker_->PickCompaction(
|
||||
GetName(), mutable_options, mutable_db_options, existing_snapshots,
|
||||
snapshot_checker, current_->storage_info(), log_buffer);
|
||||
snapshot_checker, current_->storage_info(), log_buffer,
|
||||
require_max_output_level);
|
||||
if (result != nullptr) {
|
||||
result->FinalizeInputInfo(current_);
|
||||
}
|
||||
@@ -1598,6 +1633,8 @@ Status ColumnFamilyData::SetOptions(
|
||||
Status s = GetColumnFamilyOptionsFromMap(config_opts, cf_opts, options_map,
|
||||
&cf_opts);
|
||||
if (s.ok()) {
|
||||
// FIXME: we should call SanitizeOptions() too or consolidate it with
|
||||
// ValidateOptions().
|
||||
s = ValidateOptions(db_opts, cf_opts);
|
||||
}
|
||||
if (s.ok()) {
|
||||
|
||||
+2
-1
@@ -424,7 +424,8 @@ class ColumnFamilyData {
|
||||
const MutableCFOptions& mutable_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, LogBuffer* log_buffer);
|
||||
const SnapshotChecker* snapshot_checker, LogBuffer* log_buffer,
|
||||
bool require_max_output_level = false);
|
||||
|
||||
// Check if the passed range overlap with any running compactions.
|
||||
// REQUIRES: DB mutex held
|
||||
|
||||
@@ -284,9 +284,9 @@ Compaction::Compaction(
|
||||
CompressionOptions _compression_opts, Temperature _output_temperature,
|
||||
uint32_t _max_subcompactions, std::vector<FileMetaData*> _grandparents,
|
||||
std::optional<SequenceNumber> _earliest_snapshot,
|
||||
const SnapshotChecker* _snapshot_checker, bool _manual_compaction,
|
||||
const std::string& _trim_ts, double _score, bool _deletion_compaction,
|
||||
bool l0_files_might_overlap, CompactionReason _compaction_reason,
|
||||
const SnapshotChecker* _snapshot_checker,
|
||||
CompactionReason _compaction_reason, const std::string& _trim_ts,
|
||||
double _score, bool l0_files_might_overlap,
|
||||
BlobGarbageCollectionPolicy _blob_garbage_collection_policy,
|
||||
double _blob_garbage_collection_age_cutoff)
|
||||
: input_vstorage_(vstorage),
|
||||
@@ -304,7 +304,9 @@ Compaction::Compaction(
|
||||
output_compression_(_compression),
|
||||
output_compression_opts_(_compression_opts),
|
||||
output_temperature_(_output_temperature),
|
||||
deletion_compaction_(_deletion_compaction),
|
||||
deletion_compaction_(_compaction_reason == CompactionReason::kFIFOTtl ||
|
||||
_compaction_reason ==
|
||||
CompactionReason::kFIFOMaxSize),
|
||||
l0_files_might_overlap_(l0_files_might_overlap),
|
||||
inputs_(PopulateWithAtomicBoundaries(vstorage, std::move(_inputs))),
|
||||
grandparents_(std::move(_grandparents)),
|
||||
@@ -321,7 +323,8 @@ Compaction::Compaction(
|
||||
? false
|
||||
: IsBottommostLevel(output_level_, vstorage, inputs_)),
|
||||
is_full_compaction_(IsFullCompaction(vstorage, inputs_)),
|
||||
is_manual_compaction_(_manual_compaction),
|
||||
is_manual_compaction_(_compaction_reason ==
|
||||
CompactionReason::kManualCompaction),
|
||||
trim_ts_(_trim_ts),
|
||||
is_trivial_move_(false),
|
||||
compaction_reason_(_compaction_reason),
|
||||
@@ -349,9 +352,6 @@ Compaction::Compaction(
|
||||
immutable_options_, start_level_,
|
||||
output_level_)) {
|
||||
MarkFilesBeingCompacted(true);
|
||||
if (is_manual_compaction_) {
|
||||
compaction_reason_ = CompactionReason::kManualCompaction;
|
||||
}
|
||||
if (max_subcompactions_ == 0) {
|
||||
max_subcompactions_ = _mutable_db_options.max_subcompactions;
|
||||
}
|
||||
|
||||
@@ -94,10 +94,9 @@ class Compaction {
|
||||
std::vector<FileMetaData*> grandparents,
|
||||
std::optional<SequenceNumber> earliest_snapshot,
|
||||
const SnapshotChecker* snapshot_checker,
|
||||
bool manual_compaction = false, const std::string& trim_ts = "",
|
||||
double score = -1, bool deletion_compaction = false,
|
||||
CompactionReason compaction_reason,
|
||||
const std::string& trim_ts = "", double score = -1,
|
||||
bool l0_files_might_overlap = true,
|
||||
CompactionReason compaction_reason = CompactionReason::kUnknown,
|
||||
BlobGarbageCollectionPolicy blob_garbage_collection_policy =
|
||||
BlobGarbageCollectionPolicy::kUseDefault,
|
||||
double blob_garbage_collection_age_cutoff = -1);
|
||||
@@ -283,6 +282,13 @@ class Compaction {
|
||||
// are non-overlapping and can be trivially moved.
|
||||
bool is_trivial_move() const { return is_trivial_move_; }
|
||||
|
||||
bool is_trivial_copy_compaction() const {
|
||||
return immutable_options_.compaction_style == kCompactionStyleFIFO &&
|
||||
compaction_reason_ == CompactionReason::kChangeTemperature &&
|
||||
mutable_cf_options_.compaction_options_fifo
|
||||
.allow_trivial_copy_when_change_temperature;
|
||||
}
|
||||
|
||||
// How many total levels are there?
|
||||
int number_levels() const { return number_levels_; }
|
||||
|
||||
@@ -456,6 +462,11 @@ class Compaction {
|
||||
const int start_level,
|
||||
const int output_level);
|
||||
|
||||
static bool OutputToNonZeroMaxOutputLevel(int output_level,
|
||||
int max_output_level) {
|
||||
return output_level > 0 && output_level == max_output_level;
|
||||
}
|
||||
|
||||
// If some data cannot be safely migrated "up" the LSM tree due to a change
|
||||
// in the preclude_last_level_data_seconds setting, this indicates a sequence
|
||||
// number for the newest data that must be kept in the last level.
|
||||
|
||||
@@ -133,10 +133,7 @@ CompactionJob::CompactionJob(
|
||||
LogBuffer* log_buffer, FSDirectory* db_directory,
|
||||
FSDirectory* output_directory, FSDirectory* blob_output_directory,
|
||||
Statistics* stats, InstrumentedMutex* db_mutex,
|
||||
ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||
ErrorHandler* db_error_handler, JobContext* job_context,
|
||||
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
|
||||
bool paranoid_file_checks, bool measure_io_stats, const std::string& dbname,
|
||||
CompactionJobStats* compaction_job_stats, Env::Priority thread_pri,
|
||||
@@ -173,12 +170,7 @@ CompactionJob::CompactionJob(
|
||||
blob_output_directory_(blob_output_directory),
|
||||
db_mutex_(db_mutex),
|
||||
db_error_handler_(db_error_handler),
|
||||
existing_snapshots_(std::move(existing_snapshots)),
|
||||
earliest_snapshot_(existing_snapshots_.empty()
|
||||
? kMaxSequenceNumber
|
||||
: existing_snapshots_.at(0)),
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
earliest_snapshot_(job_context->GetEarliestSnapshotSequence()),
|
||||
job_context_(job_context),
|
||||
table_cache_(std::move(table_cache)),
|
||||
event_logger_(event_logger),
|
||||
@@ -193,6 +185,7 @@ CompactionJob::CompactionJob(
|
||||
bg_bottom_compaction_scheduled_(bg_bottom_compaction_scheduled) {
|
||||
assert(job_stats_ != nullptr);
|
||||
assert(log_buffer_ != nullptr);
|
||||
assert(job_context->snapshot_context_initialized);
|
||||
|
||||
const auto* cfd = compact_->compaction->column_family_data();
|
||||
ThreadStatusUtil::SetEnableTracking(db_options_.enable_thread_tracking);
|
||||
@@ -224,10 +217,9 @@ void CompactionJob::ReportStartedCompaction(Compaction* compaction) {
|
||||
ThreadStatus::COMPACTION_PROP_FLAGS,
|
||||
compaction->is_manual_compaction() +
|
||||
(compaction->deletion_compaction() << 1));
|
||||
|
||||
auto total_input_bytes = compaction->CalculateTotalInputSize();
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_TOTAL_INPUT_BYTES,
|
||||
compaction->CalculateTotalInputSize());
|
||||
ThreadStatus::COMPACTION_TOTAL_INPUT_BYTES, total_input_bytes);
|
||||
|
||||
IOSTATS_RESET(bytes_written);
|
||||
IOSTATS_RESET(bytes_read);
|
||||
@@ -242,6 +234,16 @@ void CompactionJob::ReportStartedCompaction(Compaction* compaction) {
|
||||
|
||||
job_stats_->is_manual_compaction = compaction->is_manual_compaction();
|
||||
job_stats_->is_full_compaction = compaction->is_full_compaction();
|
||||
// populate compaction stats num_input_files and total_num_of_bytes
|
||||
size_t num_input_files = 0;
|
||||
for (int input_level = 0;
|
||||
input_level < static_cast<int>(compaction->num_input_levels());
|
||||
++input_level) {
|
||||
const LevelFilesBrief* flevel = compaction->input_levels(input_level);
|
||||
num_input_files += flevel->num_files;
|
||||
}
|
||||
job_stats_->CompactionJobStats::num_input_files = num_input_files;
|
||||
job_stats_->total_input_bytes = total_input_bytes;
|
||||
}
|
||||
|
||||
void CompactionJob::Prepare(
|
||||
@@ -837,8 +839,7 @@ Status CompactionJob::Run() {
|
||||
}
|
||||
|
||||
ReleaseSubcompactionResources();
|
||||
TEST_SYNC_POINT("CompactionJob::ReleaseSubcompactionResources:0");
|
||||
TEST_SYNC_POINT("CompactionJob::ReleaseSubcompactionResources:1");
|
||||
TEST_SYNC_POINT("CompactionJob::ReleaseSubcompactionResources");
|
||||
|
||||
for (const auto& state : compact_->sub_compact_states) {
|
||||
for (const auto& output : state.GetOutputs()) {
|
||||
@@ -878,7 +879,14 @@ Status CompactionJob::Run() {
|
||||
UpdateCompactionJobOutputStats(internal_stats_);
|
||||
|
||||
// Verify number of output records
|
||||
if (status.ok() && db_options_.compaction_verify_record_count) {
|
||||
// Only verify on table with format collects table properties
|
||||
const auto& mutable_cf_options = compact_->compaction->mutable_cf_options();
|
||||
if (status.ok() &&
|
||||
(mutable_cf_options.table_factory->IsInstanceOf(
|
||||
TableFactory::kBlockBasedTableName()) ||
|
||||
mutable_cf_options.table_factory->IsInstanceOf(
|
||||
TableFactory::kPlainTableName())) &&
|
||||
db_options_.compaction_verify_record_count) {
|
||||
uint64_t total_output_num = 0;
|
||||
for (const auto& state : compact_->sub_compact_states) {
|
||||
for (const auto& output : state.GetOutputs()) {
|
||||
@@ -1176,7 +1184,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
// creation across both CompactionJob and CompactionServiceCompactionJob
|
||||
sub_compact->AssignRangeDelAggregator(
|
||||
std::make_unique<CompactionRangeDelAggregator>(
|
||||
&cfd->internal_comparator(), existing_snapshots_,
|
||||
&cfd->internal_comparator(), job_context_->snapshot_seqs,
|
||||
&full_history_ts_low_, &trim_ts_));
|
||||
|
||||
// TODO: since we already use C++17, should use
|
||||
@@ -1317,8 +1325,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
env_, cfd->user_comparator(), cfd->ioptions().merge_operator.get(),
|
||||
compaction_filter, db_options_.info_log.get(),
|
||||
false /* internal key corruption is expected */,
|
||||
existing_snapshots_.empty() ? 0 : existing_snapshots_.back(),
|
||||
snapshot_checker_, compact_->compaction->level(), db_options_.stats);
|
||||
job_context_->GetLatestSnapshotSequence(), job_context_->snapshot_checker,
|
||||
compact_->compaction->level(), db_options_.stats);
|
||||
|
||||
const auto& mutable_cf_options =
|
||||
sub_compact->compaction->mutable_cf_options();
|
||||
@@ -1354,9 +1362,10 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
|
||||
auto c_iter = std::make_unique<CompactionIterator>(
|
||||
input, cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
&existing_snapshots_, earliest_snapshot_,
|
||||
earliest_write_conflict_snapshot_, job_snapshot_seq, snapshot_checker_,
|
||||
env_, ShouldReportDetailedTime(env_, stats_),
|
||||
&(job_context_->snapshot_seqs), earliest_snapshot_,
|
||||
job_context_->earliest_write_conflict_snapshot, job_snapshot_seq,
|
||||
job_context_->snapshot_checker, env_,
|
||||
ShouldReportDetailedTime(env_, stats_),
|
||||
/*expect_valid_internal_key=*/true, sub_compact->RangeDelAgg(),
|
||||
blob_file_builder.get(), db_options_.allow_data_in_errors,
|
||||
db_options_.enforce_single_del_contracts, manual_compaction_canceled_,
|
||||
@@ -1645,10 +1654,6 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
Status s = input_status;
|
||||
|
||||
// Add range tombstones
|
||||
auto earliest_snapshot = kMaxSequenceNumber;
|
||||
if (existing_snapshots_.size() > 0) {
|
||||
earliest_snapshot = existing_snapshots_[0];
|
||||
}
|
||||
if (s.ok()) {
|
||||
// Inclusive lower bound, exclusive upper bound
|
||||
std::pair<SequenceNumber, SequenceNumber> keep_seqno_range{
|
||||
@@ -1674,7 +1679,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
s = outputs.AddRangeDels(*sub_compact->RangeDelAgg(), comp_start_user_key,
|
||||
comp_end_user_key, range_del_out_stats,
|
||||
bottommost_level_, cfd->internal_comparator(),
|
||||
earliest_snapshot, keep_seqno_range,
|
||||
earliest_snapshot_, keep_seqno_range,
|
||||
next_table_min_key, full_history_ts_low_);
|
||||
}
|
||||
RecordDroppedKeys(range_del_out_stats, &sub_compact->compaction_job_stats);
|
||||
@@ -2293,8 +2298,8 @@ void CompactionJob::LogCompaction() {
|
||||
cfd->GetName().c_str(), scratch);
|
||||
// build event logger report
|
||||
auto stream = event_logger_->Log();
|
||||
stream << "job" << job_id_ << "event" << "compaction_started"
|
||||
<< "compaction_reason"
|
||||
stream << "job" << job_id_ << "event" << "compaction_started" << "cf_name"
|
||||
<< cfd->GetName() << "compaction_reason"
|
||||
<< GetCompactionReasonString(compaction->compaction_reason());
|
||||
for (size_t i = 0; i < compaction->num_input_levels(); ++i) {
|
||||
stream << ("files_L" + std::to_string(compaction->level(i)));
|
||||
@@ -2306,9 +2311,10 @@ void CompactionJob::LogCompaction() {
|
||||
}
|
||||
stream << "score" << compaction->score() << "input_data_size"
|
||||
<< compaction->CalculateTotalInputSize() << "oldest_snapshot_seqno"
|
||||
<< (existing_snapshots_.empty()
|
||||
<< (job_context_->snapshot_seqs.empty()
|
||||
? int64_t{-1} // Use -1 for "none"
|
||||
: static_cast<int64_t>(existing_snapshots_[0]));
|
||||
: static_cast<int64_t>(
|
||||
job_context_->GetEarliestSnapshotSequence()));
|
||||
if (compaction->SupportsPerKeyPlacement()) {
|
||||
stream << "proximal_after_seqno" << proximal_after_seqno_;
|
||||
stream << "preserve_seqno_after" << preserve_seqno_after_;
|
||||
|
||||
@@ -142,27 +142,27 @@ class SubcompactionState;
|
||||
|
||||
class CompactionJob {
|
||||
public:
|
||||
CompactionJob(
|
||||
int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const FileOptions& file_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
FSDirectory* db_directory, FSDirectory* output_directory,
|
||||
FSDirectory* blob_output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
|
||||
bool paranoid_file_checks, bool measure_io_stats,
|
||||
const std::string& dbname, CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<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 = nullptr,
|
||||
int* bg_compaction_scheduled = nullptr,
|
||||
int* bg_bottom_compaction_scheduled = nullptr);
|
||||
CompactionJob(int job_id, Compaction* compaction,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const FileOptions& file_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
FSDirectory* db_directory, FSDirectory* output_directory,
|
||||
FSDirectory* blob_output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
|
||||
JobContext* job_context, std::shared_ptr<Cache> table_cache,
|
||||
EventLogger* event_logger, bool paranoid_file_checks,
|
||||
bool measure_io_stats, const std::string& dbname,
|
||||
CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<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 = nullptr,
|
||||
int* bg_compaction_scheduled = nullptr,
|
||||
int* bg_bottom_compaction_scheduled = nullptr);
|
||||
|
||||
virtual ~CompactionJob();
|
||||
|
||||
@@ -321,21 +321,8 @@ class CompactionJob {
|
||||
FSDirectory* blob_output_directory_;
|
||||
InstrumentedMutex* db_mutex_;
|
||||
ErrorHandler* db_error_handler_;
|
||||
// If there were two snapshots with seq numbers s1 and
|
||||
// s2 and s1 < s2, and if we find two instances of a key k1 then lies
|
||||
// entirely within s1 and s2, then the earlier version of k1 can be safely
|
||||
// deleted because that version is not visible in any snapshot.
|
||||
std::vector<SequenceNumber> existing_snapshots_;
|
||||
|
||||
SequenceNumber earliest_snapshot_;
|
||||
|
||||
// This is the earliest snapshot that could be used for write-conflict
|
||||
// checking by a transaction. For any user-key newer than this snapshot, we
|
||||
// should make sure not to remove evidence that a write occurred.
|
||||
SequenceNumber earliest_write_conflict_snapshot_;
|
||||
|
||||
const SnapshotChecker* const snapshot_checker_;
|
||||
|
||||
JobContext* job_context_;
|
||||
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
@@ -427,8 +414,9 @@ struct CompactionServiceInput {
|
||||
// CompactionServiceOutputFile is the metadata for the output SST file
|
||||
struct CompactionServiceOutputFile {
|
||||
std::string file_name;
|
||||
SequenceNumber smallest_seqno;
|
||||
SequenceNumber largest_seqno;
|
||||
uint64_t file_size{};
|
||||
SequenceNumber smallest_seqno{};
|
||||
SequenceNumber largest_seqno{};
|
||||
std::string smallest_internal_key;
|
||||
std::string largest_internal_key;
|
||||
uint64_t oldest_ancester_time = kUnknownOldestAncesterTime;
|
||||
@@ -436,24 +424,26 @@ struct CompactionServiceOutputFile {
|
||||
uint64_t epoch_number = kUnknownEpochNumber;
|
||||
std::string file_checksum = kUnknownFileChecksum;
|
||||
std::string file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
uint64_t paranoid_hash;
|
||||
uint64_t paranoid_hash{};
|
||||
bool marked_for_compaction;
|
||||
UniqueId64x2 unique_id{};
|
||||
TableProperties table_properties;
|
||||
bool is_proximal_level_output;
|
||||
Temperature file_temperature;
|
||||
Temperature file_temperature = Temperature::kUnknown;
|
||||
|
||||
CompactionServiceOutputFile() = default;
|
||||
CompactionServiceOutputFile(
|
||||
const std::string& name, SequenceNumber smallest, SequenceNumber largest,
|
||||
std::string _smallest_internal_key, std::string _largest_internal_key,
|
||||
uint64_t _oldest_ancester_time, uint64_t _file_creation_time,
|
||||
uint64_t _epoch_number, const std::string& _file_checksum,
|
||||
const std::string& name, uint64_t size, SequenceNumber smallest,
|
||||
SequenceNumber largest, std::string _smallest_internal_key,
|
||||
std::string _largest_internal_key, uint64_t _oldest_ancester_time,
|
||||
uint64_t _file_creation_time, uint64_t _epoch_number,
|
||||
const std::string& _file_checksum,
|
||||
const std::string& _file_checksum_func_name, uint64_t _paranoid_hash,
|
||||
bool _marked_for_compaction, UniqueId64x2 _unique_id,
|
||||
const TableProperties& _table_properties, bool _is_proximal_level_output,
|
||||
Temperature _file_temperature)
|
||||
: file_name(name),
|
||||
file_size(size),
|
||||
smallest_seqno(smallest),
|
||||
largest_seqno(largest),
|
||||
smallest_internal_key(std::move(_smallest_internal_key)),
|
||||
@@ -521,9 +511,9 @@ class CompactionServiceCompactionJob : private CompactionJob {
|
||||
const std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
FSDirectory* output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
|
||||
const std::string& dbname, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
JobContext* job_context, std::shared_ptr<Cache> table_cache,
|
||||
EventLogger* event_logger, const std::string& dbname,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<bool>& manual_compaction_canceled,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
std::string output_path,
|
||||
|
||||
@@ -43,7 +43,6 @@ void VerifyInitializationOfCompactionJobStats(
|
||||
ASSERT_EQ(compaction_job_stats.elapsed_micros, 0U);
|
||||
|
||||
ASSERT_EQ(compaction_job_stats.num_input_records, 0U);
|
||||
ASSERT_EQ(compaction_job_stats.num_input_files, 0U);
|
||||
ASSERT_EQ(compaction_job_stats.num_input_files_at_output_level, 0U);
|
||||
|
||||
ASSERT_EQ(compaction_job_stats.num_output_records, 0U);
|
||||
@@ -52,7 +51,6 @@ void VerifyInitializationOfCompactionJobStats(
|
||||
ASSERT_TRUE(compaction_job_stats.is_manual_compaction);
|
||||
ASSERT_FALSE(compaction_job_stats.is_remote_compaction);
|
||||
|
||||
ASSERT_EQ(compaction_job_stats.total_input_bytes, 0U);
|
||||
ASSERT_EQ(compaction_job_stats.total_output_bytes, 0U);
|
||||
|
||||
ASSERT_EQ(compaction_job_stats.total_input_raw_key_bytes, 0U);
|
||||
@@ -232,11 +230,6 @@ class CompactionJobTestBase : public testing::Test {
|
||||
// set default for the tests
|
||||
mutable_cf_options_.target_file_size_base = 1024 * 1024;
|
||||
mutable_cf_options_.max_compaction_bytes = 10 * 1024 * 1024;
|
||||
|
||||
// Turn off compaction_verify_record_count MockTables
|
||||
if (table_type == TableTypeForTest::kMockTable) {
|
||||
db_options_.compaction_verify_record_count = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
@@ -465,9 +458,10 @@ class CompactionJobTestBase : public testing::Test {
|
||||
ReadOptions read_opts;
|
||||
Status s = cf_options_.table_factory->NewTableReader(
|
||||
read_opts,
|
||||
TableReaderOptions(cfd->ioptions(), nullptr, FileOptions(),
|
||||
TableReaderOptions(cfd->ioptions(), /*prefix_extractor=*/nullptr,
|
||||
/*compression_manager=*/nullptr, FileOptions(),
|
||||
cfd_->internal_comparator(),
|
||||
0 /* block_protection_bytes_per_key */),
|
||||
/*block_protection_bytes_per_key=*/0),
|
||||
std::move(freader), file_size, &table_reader, false);
|
||||
ASSERT_OK(s);
|
||||
assert(table_reader);
|
||||
@@ -600,11 +594,11 @@ class CompactionJobTestBase : public testing::Test {
|
||||
const std::vector<std::vector<FileMetaData*>>& input_files,
|
||||
const std::vector<int> input_levels,
|
||||
std::function<void(Compaction& comp)>&& verify_func,
|
||||
const std::vector<SequenceNumber>& snapshots = {}) {
|
||||
std::vector<SequenceNumber>&& snapshots = {}) {
|
||||
const int kLastLevel = cf_options_.num_levels - 1;
|
||||
verify_per_key_placement_ = std::move(verify_func);
|
||||
mock::KVVector empty_map;
|
||||
RunCompaction(input_files, input_levels, {empty_map}, snapshots,
|
||||
RunCompaction(input_files, input_levels, {empty_map}, std::move(snapshots),
|
||||
kMaxSequenceNumber, kLastLevel, false);
|
||||
}
|
||||
|
||||
@@ -613,7 +607,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
const std::vector<std::vector<FileMetaData*>>& input_files,
|
||||
const std::vector<int>& input_levels,
|
||||
const std::vector<mock::KVVector>& expected_results,
|
||||
const std::vector<SequenceNumber>& snapshots = {},
|
||||
std::vector<SequenceNumber>&& snapshots = {},
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber,
|
||||
int output_level = 1, bool verify = true,
|
||||
std::vector<uint64_t> expected_oldest_blob_file_numbers = {},
|
||||
@@ -657,7 +651,8 @@ class CompactionJobTestBase : public testing::Test {
|
||||
mutable_cf_options_.max_compaction_bytes, 0, kNoCompression,
|
||||
cfd->GetLatestMutableCFOptions().compression_opts,
|
||||
Temperature::kUnknown, max_subcompactions, grandparents,
|
||||
/*earliest_snapshot*/ std::nullopt, /*snapshot_checker*/ nullptr, true);
|
||||
/*earliest_snapshot*/ std::nullopt, /*snapshot_checker*/ nullptr,
|
||||
CompactionReason::kManualCompaction);
|
||||
compaction.FinalizeInputInfo(cfd->current());
|
||||
|
||||
assert(db_options_.info_log);
|
||||
@@ -670,13 +665,15 @@ class CompactionJobTestBase : public testing::Test {
|
||||
ucmp_->timestamp_size() == full_history_ts_low_.size());
|
||||
const std::atomic<bool> kManualCompactionCanceledFalse{false};
|
||||
JobContext job_context(1, false /* create_superversion */);
|
||||
job_context.InitSnapshotContext(snapshot_checker, nullptr,
|
||||
earliest_write_conflict_snapshot,
|
||||
std::move(snapshots));
|
||||
CompactionJob compaction_job(
|
||||
0, &compaction, db_options_, mutable_db_options_, env_options_,
|
||||
versions_.get(), &shutting_down_, &log_buffer, nullptr, nullptr,
|
||||
nullptr, nullptr, &mutex_, &error_handler_, snapshots,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, &job_context,
|
||||
table_cache_, &event_logger, false, false, dbname_,
|
||||
&compaction_job_stats_, Env::Priority::USER, nullptr /* IOTracer */,
|
||||
nullptr, nullptr, &mutex_, &error_handler_, &job_context, table_cache_,
|
||||
&event_logger, false, false, dbname_, &compaction_job_stats_,
|
||||
Env::Priority::USER, nullptr /* IOTracer */,
|
||||
/*manual_compaction_canceled=*/kManualCompactionCanceledFalse,
|
||||
env_->GenerateUniqueId(), DBImpl::GenerateDbSessionId(nullptr),
|
||||
full_history_ts_low_);
|
||||
@@ -1674,6 +1671,7 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
UniqueId64x2 id{rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX)};
|
||||
result.output_files.emplace_back(
|
||||
rnd.RandomString(rnd.Uniform(kStrMaxLen)) /* file_name */,
|
||||
rnd64.Uniform(UINT64_MAX) /* file_size */,
|
||||
rnd64.Uniform(UINT64_MAX) /* smallest_seqno */,
|
||||
rnd64.Uniform(UINT64_MAX) /* largest_seqno */,
|
||||
rnd.RandomBinaryString(
|
||||
@@ -2040,7 +2038,7 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
snapshots.emplace_back(i);
|
||||
}
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2}, snapshots);
|
||||
{expected_file1, expected_file2}, std::move(snapshots));
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutForMaxCompactionBytesSameKey) {
|
||||
@@ -2099,7 +2097,8 @@ TEST_F(CompactionJobTest, CutForMaxCompactionBytesSameKey) {
|
||||
snapshots.emplace_back(i);
|
||||
}
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2, expected_file3}, snapshots);
|
||||
{expected_file1, expected_file2, expected_file3},
|
||||
std::move(snapshots));
|
||||
}
|
||||
|
||||
class CompactionJobTimestampTest : public CompactionJobTestBase {
|
||||
|
||||
@@ -54,6 +54,7 @@ Status CompactionOutputs::Finish(
|
||||
}
|
||||
current_output().finished = true;
|
||||
stats_.bytes_written += current_bytes;
|
||||
stats_.bytes_written_pre_comp += builder_->PreCompressionSize();
|
||||
stats_.num_output_files = static_cast<int>(outputs_.size());
|
||||
|
||||
return s;
|
||||
|
||||
@@ -381,7 +381,7 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_options.max_subcompactions,
|
||||
/* grandparents */ {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr, true);
|
||||
/* snapshot_checker */ nullptr, CompactionReason::kManualCompaction);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
@@ -680,10 +680,9 @@ Compaction* CompactionPicker::CompactRange(
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_range_options.max_subcompactions,
|
||||
/* grandparents */ {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ true, trim_ts, /* score */ -1,
|
||||
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kUnknown,
|
||||
/* snapshot_checker */ nullptr, CompactionReason::kManualCompaction,
|
||||
trim_ts, /* score */ -1,
|
||||
/* l0_files_might_overlap */ true,
|
||||
compact_range_options.blob_garbage_collection_policy,
|
||||
compact_range_options.blob_garbage_collection_age_cutoff);
|
||||
|
||||
@@ -873,9 +872,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_range_options.max_subcompactions, std::move(grandparents),
|
||||
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
|
||||
/* is manual */ true, trim_ts, /* score */ -1,
|
||||
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kUnknown,
|
||||
CompactionReason::kManualCompaction, trim_ts, /* score */ -1,
|
||||
/* l0_files_might_overlap */ true,
|
||||
compact_range_options.blob_garbage_collection_policy,
|
||||
compact_range_options.blob_garbage_collection_age_cutoff);
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class CompactionPicker {
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) = 0;
|
||||
LogBuffer* log_buffer, bool require_max_output_level) = 0;
|
||||
|
||||
// The returned Compaction might not include the whole requested range.
|
||||
// In that case, compaction_end will be set to the next key that needs
|
||||
@@ -272,7 +272,8 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
const MutableDBOptions& /*mutable_db_options*/,
|
||||
const std::vector<SequenceNumber>& /*existing_snapshots*/,
|
||||
const SnapshotChecker* /*snapshot_checker*/,
|
||||
VersionStorageInfo* /*vstorage*/, LogBuffer* /* log_buffer */) override {
|
||||
VersionStorageInfo* /*vstorage*/, LogBuffer* /* log_buffer */,
|
||||
bool /*require_max_output_level*/ = false) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,11 +127,9 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false,
|
||||
/* snapshot_checker */ nullptr, CompactionReason::kFIFOTtl,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kFIFOTtl);
|
||||
/* l0_files_might_overlap */ true);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -200,11 +198,10 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
mutable_cf_options.default_write_temperature,
|
||||
0 /* max_subcompactions */, {},
|
||||
/* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr, /* is manual */ false,
|
||||
/* snapshot_checker */ nullptr,
|
||||
CompactionReason::kFIFOReduceNumFiles,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
/* l0_files_might_overlap */ true,
|
||||
CompactionReason::kFIFOReduceNumFiles);
|
||||
/* l0_files_might_overlap */ true);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
@@ -297,11 +294,9 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false,
|
||||
/* snapshot_checker */ nullptr, CompactionReason::kFIFOMaxSize,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true,
|
||||
/* l0_files_might_overlap */ true, CompactionReason::kFIFOMaxSize);
|
||||
/* l0_files_might_overlap */ true);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -416,10 +411,9 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
|
||||
mutable_cf_options.compression, mutable_cf_options.compression_opts,
|
||||
compaction_target_temp,
|
||||
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false, /* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kChangeTemperature);
|
||||
/* snapshot_checker */ nullptr, CompactionReason::kChangeTemperature,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* l0_files_might_overlap */ true);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -428,7 +422,7 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& /* existing_snapshots */,
|
||||
const SnapshotChecker* /* snapshot_checker */, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) {
|
||||
LogBuffer* log_buffer, bool /* require_max_output_level*/) {
|
||||
Compaction* c = nullptr;
|
||||
if (mutable_cf_options.ttl > 0) {
|
||||
c = PickTTLCompaction(cf_name, mutable_cf_options, mutable_db_options,
|
||||
|
||||
@@ -23,7 +23,8 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& /* existing_snapshots */,
|
||||
const SnapshotChecker* /* snapshot_checker */,
|
||||
VersionStorageInfo* version, LogBuffer* log_buffer) override;
|
||||
VersionStorageInfo* version, LogBuffer* log_buffer,
|
||||
bool /* require_max_output_level*/ = false) override;
|
||||
|
||||
Compaction* CompactRange(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
|
||||
@@ -145,7 +145,6 @@ class LevelCompactionBuilder {
|
||||
int parent_index_ = -1;
|
||||
int base_index_ = -1;
|
||||
double start_level_score_ = 0;
|
||||
bool is_manual_ = false;
|
||||
bool is_l0_trivial_move_ = false;
|
||||
CompactionInputFiles start_level_inputs_;
|
||||
std::vector<CompactionInputFiles> compaction_inputs_;
|
||||
@@ -561,9 +560,8 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, std::move(grandparents_),
|
||||
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
|
||||
is_manual_,
|
||||
/* trim_ts */ "", start_level_score_, false /* deletion_compaction */,
|
||||
l0_files_might_overlap, compaction_reason_);
|
||||
compaction_reason_,
|
||||
/* trim_ts */ "", start_level_score_, l0_files_might_overlap);
|
||||
|
||||
// If it's level 0 compaction, make sure we don't execute any other level 0
|
||||
// compactions in parallel
|
||||
@@ -978,7 +976,7 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& /*existing_snapshots */,
|
||||
const SnapshotChecker* /*snapshot_checker*/, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) {
|
||||
LogBuffer* log_buffer, bool /* require_max_output_level*/) {
|
||||
LevelCompactionBuilder builder(cf_name, vstorage, this, log_buffer,
|
||||
mutable_cf_options, ioptions_,
|
||||
mutable_db_options);
|
||||
|
||||
@@ -25,7 +25,8 @@ class LevelCompactionPicker : public CompactionPicker {
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& /* existing_snapshots */,
|
||||
const SnapshotChecker* /* snapshot_checker */,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) override;
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer,
|
||||
bool /*require_max_output_level*/ = false) override;
|
||||
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
};
|
||||
|
||||
@@ -1134,10 +1134,15 @@ TEST_F(CompactionPickerTest, FIFOToCold1) {
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.file_temperature_age_thresholds = {
|
||||
{Temperature::kCold, kColdThreshold}};
|
||||
fifo_options_.allow_trivial_copy_when_change_temperature = true;
|
||||
fifo_options_.trivial_copy_buffer_size = 16 * 1024 * 1024;
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
auto copiedIOptions = ioptions_;
|
||||
copiedIOptions.compaction_style = kCompactionStyleFIFO;
|
||||
FIFOCompactionPicker fifo_compaction_picker(copiedIOptions, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
@@ -1186,7 +1191,10 @@ TEST_F(CompactionPickerTest, FIFOToColdMaxCompactionSize) {
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 9;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
auto copiedIOptions = ioptions_;
|
||||
copiedIOptions.compaction_style = kCompactionStyleFIFO;
|
||||
FIFOCompactionPicker fifo_compaction_picker(copiedIOptions, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
@@ -1253,7 +1261,10 @@ TEST_F(CompactionPickerTest, FIFOToColdWithExistingCold) {
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
auto copiedIOptions = ioptions_;
|
||||
copiedIOptions.compaction_style = kCompactionStyleFIFO;
|
||||
FIFOCompactionPicker fifo_compaction_picker(copiedIOptions, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
@@ -1318,7 +1329,10 @@ TEST_F(CompactionPickerTest, FIFOToColdWithHotBetweenCold) {
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
auto copiedIOptions = ioptions_;
|
||||
copiedIOptions.compaction_style = kCompactionStyleFIFO;
|
||||
FIFOCompactionPicker fifo_compaction_picker(copiedIOptions, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
@@ -1385,7 +1399,10 @@ TEST_F(CompactionPickerTest, FIFOToHotAndWarm) {
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
auto copiedIOptions = ioptions_;
|
||||
copiedIOptions.compaction_style = kCompactionStyleFIFO;
|
||||
FIFOCompactionPicker fifo_compaction_picker(copiedIOptions, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
|
||||
@@ -38,7 +38,8 @@ class UniversalCompactionBuilder {
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
|
||||
UniversalCompactionPicker* picker, LogBuffer* log_buffer)
|
||||
UniversalCompactionPicker* picker, LogBuffer* log_buffer,
|
||||
bool require_max_output_level)
|
||||
: ioptions_(ioptions),
|
||||
icmp_(icmp),
|
||||
cf_name_(cf_name),
|
||||
@@ -46,7 +47,8 @@ class UniversalCompactionBuilder {
|
||||
mutable_db_options_(mutable_db_options),
|
||||
vstorage_(vstorage),
|
||||
picker_(picker),
|
||||
log_buffer_(log_buffer) {
|
||||
log_buffer_(log_buffer),
|
||||
require_max_output_level_(require_max_output_level) {
|
||||
assert(icmp_);
|
||||
const auto* ucmp = icmp_->user_comparator();
|
||||
assert(ucmp);
|
||||
@@ -102,6 +104,174 @@ class UniversalCompactionBuilder {
|
||||
bool level_has_marked_standalone_rangedel;
|
||||
};
|
||||
|
||||
unsigned int GetMaxNumFilesToCompactBasedOnMaxReadAmp(
|
||||
const int file_num_compaction_trigger, const unsigned int ratio,
|
||||
int* num_sr_not_compacted_output, int* max_num_runs_output) const {
|
||||
assert(num_sr_not_compacted_output);
|
||||
assert(max_num_runs_output);
|
||||
int max_num_runs =
|
||||
mutable_cf_options_.compaction_options_universal.max_read_amp;
|
||||
if (max_num_runs < 0) {
|
||||
// any value < -1 is not valid
|
||||
assert(max_num_runs == -1);
|
||||
// By default, fall back to `level0_file_num_compaction_trigger`
|
||||
max_num_runs = file_num_compaction_trigger;
|
||||
} else if (max_num_runs == 0) {
|
||||
if (mutable_cf_options_.compaction_options_universal.stop_style ==
|
||||
kCompactionStopStyleTotalSize) {
|
||||
// 0 means auto-tuning by RocksDB. We estimate max num run based on
|
||||
// max_run_size, size_ratio and write buffer size:
|
||||
// Assume the size of the lowest level size is equal to
|
||||
// write_buffer_size. Each subsequent level is the max size without
|
||||
// triggering size_ratio compaction. `max_num_runs` is the minimum
|
||||
// number of levels required such that the target size of the
|
||||
// largest level is at least `max_run_size_`.
|
||||
max_num_runs = 1;
|
||||
double cur_level_max_size =
|
||||
static_cast<double>(mutable_cf_options_.write_buffer_size);
|
||||
double total_run_size = 0;
|
||||
while (cur_level_max_size < static_cast<double>(max_run_size_)) {
|
||||
// This loop should not take too many iterations since
|
||||
// cur_level_max_size at least doubles each iteration.
|
||||
total_run_size += cur_level_max_size;
|
||||
cur_level_max_size = (100.0 + ratio) / 100.0 * total_run_size;
|
||||
++max_num_runs;
|
||||
}
|
||||
} else {
|
||||
// TODO: implement the auto-tune logic for this stop style
|
||||
max_num_runs = file_num_compaction_trigger;
|
||||
}
|
||||
} else {
|
||||
// max_num_runs > 0, it's the limit on the number of sorted run
|
||||
}
|
||||
|
||||
// Get the total number of sorted runs that are not being compacted
|
||||
int num_sr_not_compacted = 0;
|
||||
for (size_t i = 0; i < sorted_runs_.size(); i++) {
|
||||
if (sorted_runs_[i].being_compacted == false &&
|
||||
!sorted_runs_[i].level_has_marked_standalone_rangedel) {
|
||||
num_sr_not_compacted++;
|
||||
}
|
||||
}
|
||||
|
||||
*num_sr_not_compacted_output = num_sr_not_compacted;
|
||||
*max_num_runs_output = max_num_runs;
|
||||
|
||||
if (num_sr_not_compacted > max_num_runs) {
|
||||
return num_sr_not_compacted - max_num_runs + 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Compaction* MaybePickPeriodicCompaction(Compaction* const prev_picked_c) {
|
||||
if (prev_picked_c != nullptr ||
|
||||
vstorage_->FilesMarkedForPeriodicCompaction().empty()) {
|
||||
return prev_picked_c;
|
||||
}
|
||||
// Always need to do a full compaction for periodic compaction.
|
||||
Compaction* c = PickPeriodicCompaction();
|
||||
TEST_SYNC_POINT_CALLBACK("PostPickPeriodicCompaction", c);
|
||||
if (c != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: picked for periodic compaction\n",
|
||||
cf_name_.c_str());
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* MaybePickSizeAmpCompaction(Compaction* const prev_picked_c,
|
||||
int file_num_compaction_trigger) {
|
||||
if (prev_picked_c != nullptr ||
|
||||
sorted_runs_.size() <
|
||||
static_cast<size_t>(file_num_compaction_trigger)) {
|
||||
return prev_picked_c;
|
||||
}
|
||||
Compaction* c = PickCompactionToReduceSizeAmp();
|
||||
if (c != nullptr) {
|
||||
TEST_SYNC_POINT("PickCompactionToReduceSizeAmpReturnNonnullptr");
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: picked for size amp compaction \n",
|
||||
cf_name_.c_str());
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* MaybePickCompactionToReduceSortedRunsBasedFileRatio(
|
||||
Compaction* const prev_picked_c, int file_num_compaction_trigger,
|
||||
unsigned int ratio) {
|
||||
if (prev_picked_c != nullptr ||
|
||||
sorted_runs_.size() <
|
||||
static_cast<size_t>(file_num_compaction_trigger)) {
|
||||
return prev_picked_c;
|
||||
}
|
||||
Compaction* c = PickCompactionToReduceSortedRuns(ratio, UINT_MAX);
|
||||
if (c != nullptr) {
|
||||
TEST_SYNC_POINT("PickCompactionToReduceSortedRunsReturnNonnullptr");
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: picked for size ratio compaction to "
|
||||
"reduce sorted run\n",
|
||||
cf_name_.c_str());
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* MaybePickCompactionToReduceSortedRuns(
|
||||
Compaction* const prev_picked_c, int file_num_compaction_trigger,
|
||||
unsigned int ratio) {
|
||||
if (prev_picked_c != nullptr ||
|
||||
sorted_runs_.size() <
|
||||
static_cast<size_t>(file_num_compaction_trigger)) {
|
||||
return prev_picked_c;
|
||||
}
|
||||
|
||||
int num_sr_not_compacted = 0;
|
||||
int max_num_runs = 0;
|
||||
const unsigned int max_num_files_to_compact =
|
||||
GetMaxNumFilesToCompactBasedOnMaxReadAmp(file_num_compaction_trigger,
|
||||
ratio, &num_sr_not_compacted,
|
||||
&max_num_runs);
|
||||
if (max_num_files_to_compact == 0) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: skipping compaction to reduce sorted run, num "
|
||||
"sorted runs not "
|
||||
"being compacted -- %u, max num runs allowed -- %d, max_run_size "
|
||||
"-- %" PRIu64 "\n",
|
||||
cf_name_.c_str(), num_sr_not_compacted, max_num_runs, max_run_size_);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Compaction* c =
|
||||
PickCompactionToReduceSortedRuns(UINT_MAX, max_num_files_to_compact);
|
||||
if (c != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: picked for sorted run num compaction "
|
||||
"to reduce sorted run, to "
|
||||
"compact file num -- %u, max num runs allowed"
|
||||
"-- %d, max_run_size -- %" PRIu64 "\n",
|
||||
cf_name_.c_str(), max_num_files_to_compact, max_num_runs,
|
||||
max_run_size_);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* MaybePickDeleteTriggeredCompaction(
|
||||
Compaction* const prev_picked_c) {
|
||||
if (prev_picked_c != nullptr) {
|
||||
return prev_picked_c;
|
||||
}
|
||||
Compaction* c = PickDeleteTriggeredCompaction();
|
||||
if (c != nullptr) {
|
||||
TEST_SYNC_POINT("PickDeleteTriggeredCompactionReturnNonnullptr");
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: picked for delete triggered compaction\n",
|
||||
cf_name_.c_str());
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
// Pick Universal compaction to limit read amplification
|
||||
Compaction* PickCompactionToReduceSortedRuns(
|
||||
unsigned int ratio, unsigned int max_number_of_files_to_compact);
|
||||
@@ -249,6 +419,13 @@ class UniversalCompactionBuilder {
|
||||
return num_l0_to_exclude;
|
||||
}
|
||||
|
||||
bool MeetsOutputLevelRequirements(int output_level) const {
|
||||
return !require_max_output_level_ ||
|
||||
Compaction::OutputToNonZeroMaxOutputLevel(
|
||||
output_level,
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind));
|
||||
}
|
||||
|
||||
const ImmutableOptions& ioptions_;
|
||||
const InternalKeyComparator* icmp_;
|
||||
double score_;
|
||||
@@ -260,6 +437,7 @@ class UniversalCompactionBuilder {
|
||||
VersionStorageInfo* vstorage_;
|
||||
UniversalCompactionPicker* picker_;
|
||||
LogBuffer* log_buffer_;
|
||||
bool require_max_output_level_;
|
||||
// Optional earliest snapshot at time of compaction picking. This is only
|
||||
// provided if the column family doesn't enable user-defined timestamps.
|
||||
// And this information is only passed to `Compaction` picked by deletion
|
||||
@@ -420,10 +598,11 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) {
|
||||
LogBuffer* log_buffer, bool require_max_output_level) {
|
||||
UniversalCompactionBuilder builder(
|
||||
ioptions_, icmp_, cf_name, mutable_cf_options, mutable_db_options,
|
||||
existing_snapshots, snapshot_checker, vstorage, this, log_buffer);
|
||||
existing_snapshots, snapshot_checker, vstorage, this, log_buffer,
|
||||
require_max_output_level);
|
||||
return builder.PickCompaction();
|
||||
}
|
||||
|
||||
@@ -554,13 +733,21 @@ bool UniversalCompactionBuilder::ShouldSkipMarkedFile(
|
||||
Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
const int kLevel0 = 0;
|
||||
score_ = vstorage_->CompactionScore(kLevel0);
|
||||
int max_output_level =
|
||||
const int max_output_level =
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
const int file_num_compaction_trigger =
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger;
|
||||
const unsigned int ratio =
|
||||
mutable_cf_options_.compaction_options_universal.size_ratio;
|
||||
|
||||
if (max_output_level == 0 &&
|
||||
!MeetsOutputLevelRequirements(0 /* output_level */)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
max_run_size_ = 0;
|
||||
sorted_runs_ =
|
||||
CalculateSortedRuns(*vstorage_, max_output_level, &max_run_size_);
|
||||
int file_num_compaction_trigger =
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger;
|
||||
|
||||
if (sorted_runs_.size() == 0 ||
|
||||
(vstorage_->FilesMarkedForPeriodicCompaction().empty() &&
|
||||
@@ -572,6 +759,7 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
ROCKS_LOG_BUFFER_MAX_SZ(
|
||||
log_buffer_, 3072,
|
||||
@@ -579,119 +767,14 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
cf_name_.c_str(), sorted_runs_.size(), vstorage_->LevelSummary(&tmp));
|
||||
|
||||
Compaction* c = nullptr;
|
||||
// Periodic compaction has higher priority than other type of compaction
|
||||
// because it's a hard requirement.
|
||||
if (!vstorage_->FilesMarkedForPeriodicCompaction().empty()) {
|
||||
// Always need to do a full compaction for periodic compaction.
|
||||
c = PickPeriodicCompaction();
|
||||
TEST_SYNC_POINT_CALLBACK("PostPickPeriodicCompaction", c);
|
||||
}
|
||||
|
||||
if (c == nullptr &&
|
||||
sorted_runs_.size() >= static_cast<size_t>(file_num_compaction_trigger)) {
|
||||
// Check for size amplification.
|
||||
if ((c = PickCompactionToReduceSizeAmp()) != nullptr) {
|
||||
TEST_SYNC_POINT("PickCompactionToReduceSizeAmpReturnNonnullptr");
|
||||
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Universal: compacting for size amp\n",
|
||||
cf_name_.c_str());
|
||||
} else {
|
||||
// Size amplification is within limits. Try reducing read
|
||||
// amplification while maintaining file size ratios.
|
||||
unsigned int ratio =
|
||||
mutable_cf_options_.compaction_options_universal.size_ratio;
|
||||
|
||||
if ((c = PickCompactionToReduceSortedRuns(ratio, UINT_MAX)) != nullptr) {
|
||||
TEST_SYNC_POINT("PickCompactionToReduceSortedRunsReturnNonnullptr");
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: compacting for size ratio\n",
|
||||
cf_name_.c_str());
|
||||
} else {
|
||||
// Size amplification and file size ratios are within configured limits.
|
||||
// If max read amplification exceeds configured limits, then force
|
||||
// compaction to reduce the number sorted runs without looking at file
|
||||
// size ratios.
|
||||
|
||||
// This is guaranteed by NeedsCompaction()
|
||||
assert(sorted_runs_.size() >=
|
||||
static_cast<size_t>(file_num_compaction_trigger));
|
||||
int max_num_runs =
|
||||
mutable_cf_options_.compaction_options_universal.max_read_amp;
|
||||
if (max_num_runs < 0) {
|
||||
// any value < -1 is not valid
|
||||
assert(max_num_runs == -1);
|
||||
// By default, fall back to `level0_file_num_compaction_trigger`
|
||||
max_num_runs = file_num_compaction_trigger;
|
||||
} else if (max_num_runs == 0) {
|
||||
if (mutable_cf_options_.compaction_options_universal.stop_style ==
|
||||
kCompactionStopStyleTotalSize) {
|
||||
// 0 means auto-tuning by RocksDB. We estimate max num run based on
|
||||
// max_run_size, size_ratio and write buffer size:
|
||||
// Assume the size of the lowest level size is equal to
|
||||
// write_buffer_size. Each subsequent level is the max size without
|
||||
// triggering size_ratio compaction. `max_num_runs` is the minimum
|
||||
// number of levels required such that the target size of the
|
||||
// largest level is at least `max_run_size_`.
|
||||
max_num_runs = 1;
|
||||
double cur_level_max_size =
|
||||
static_cast<double>(mutable_cf_options_.write_buffer_size);
|
||||
double total_run_size = 0;
|
||||
while (cur_level_max_size < static_cast<double>(max_run_size_)) {
|
||||
// This loop should not take too many iterations since
|
||||
// cur_level_max_size at least doubles each iteration.
|
||||
total_run_size += cur_level_max_size;
|
||||
cur_level_max_size = (100.0 + ratio) / 100.0 * total_run_size;
|
||||
++max_num_runs;
|
||||
}
|
||||
} else {
|
||||
// TODO: implement the auto-tune logic for this stop style
|
||||
max_num_runs = file_num_compaction_trigger;
|
||||
}
|
||||
} else {
|
||||
// max_num_runs > 0, it's the limit on the number of sorted run
|
||||
}
|
||||
// Get the total number of sorted runs that are not being compacted
|
||||
int num_sr_not_compacted = 0;
|
||||
for (size_t i = 0; i < sorted_runs_.size(); i++) {
|
||||
if (sorted_runs_[i].being_compacted == false &&
|
||||
!sorted_runs_[i].level_has_marked_standalone_rangedel) {
|
||||
num_sr_not_compacted++;
|
||||
}
|
||||
}
|
||||
|
||||
// The number of sorted runs that are not being compacted is greater
|
||||
// than the maximum allowed number of sorted runs
|
||||
if (num_sr_not_compacted > max_num_runs) {
|
||||
unsigned int num_files = num_sr_not_compacted - max_num_runs + 1;
|
||||
if ((c = PickCompactionToReduceSortedRuns(UINT_MAX, num_files)) !=
|
||||
nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: compacting for file num, to "
|
||||
"compact file num -- %u, max num runs allowed"
|
||||
"-- %d, max_run_size -- %" PRIu64 "\n",
|
||||
cf_name_.c_str(), num_files, max_num_runs,
|
||||
max_run_size_);
|
||||
}
|
||||
} else {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: skipping compaction for file num, num runs not "
|
||||
"being compacted -- %u, max num runs allowed -- %d, max_run_size "
|
||||
"-- %" PRIu64 "\n",
|
||||
cf_name_.c_str(), num_sr_not_compacted, max_num_runs,
|
||||
max_run_size_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c == nullptr) {
|
||||
if ((c = PickDeleteTriggeredCompaction()) != nullptr) {
|
||||
TEST_SYNC_POINT("PickDeleteTriggeredCompactionReturnNonnullptr");
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: delete triggered compaction\n",
|
||||
cf_name_.c_str());
|
||||
}
|
||||
}
|
||||
c = MaybePickPeriodicCompaction(c);
|
||||
c = MaybePickSizeAmpCompaction(c, file_num_compaction_trigger);
|
||||
c = MaybePickCompactionToReduceSortedRunsBasedFileRatio(
|
||||
c, file_num_compaction_trigger, ratio);
|
||||
c = MaybePickCompactionToReduceSortedRuns(c, file_num_compaction_trigger,
|
||||
ratio);
|
||||
c = MaybePickDeleteTriggeredCompaction(c);
|
||||
|
||||
if (c == nullptr) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
@@ -700,6 +783,7 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
}
|
||||
assert(c->output_level() <=
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind));
|
||||
assert(MeetsOutputLevelRequirements(c->output_level()));
|
||||
|
||||
if (mutable_cf_options_.compaction_options_universal.allow_trivial_move ==
|
||||
true &&
|
||||
@@ -825,14 +909,16 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
if (sr->being_compacted) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: %s"
|
||||
"[%d] being compacted, skipping",
|
||||
"[%d] being compacted, skipping for compaction to "
|
||||
"reduce sorted runs",
|
||||
cf_name_.c_str(), file_num_buf, loop);
|
||||
} else if (sr->level_has_marked_standalone_rangedel) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: %s"
|
||||
"[%d] has standalone range tombstone files marked for "
|
||||
"compaction, skipping",
|
||||
cf_name_.c_str(), file_num_buf, loop);
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: %s"
|
||||
"[%d] has standalone range tombstone files marked for "
|
||||
"compaction, skipping for compaction to reduce sorted runs",
|
||||
cf_name_.c_str(), file_num_buf, loop);
|
||||
}
|
||||
|
||||
sr = nullptr;
|
||||
@@ -845,7 +931,8 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
sr->Dump(file_num_buf, sizeof(file_num_buf), true);
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: Possible candidate %s[%d].",
|
||||
"[%s] Universal: Possible candidate for compaction to "
|
||||
"reduce sorted runs %s[%d].",
|
||||
cf_name_.c_str(), file_num_buf, loop);
|
||||
}
|
||||
|
||||
@@ -947,6 +1034,10 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
output_level = sorted_runs_[first_index_after].level - 1;
|
||||
}
|
||||
|
||||
if (!MeetsOutputLevelRequirements(output_level)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> inputs(max_output_level + 1);
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
inputs[i].level = start_level + static_cast<int>(i);
|
||||
@@ -1006,10 +1097,9 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, grandparents,
|
||||
/* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false, /* trim_ts */ "", score_,
|
||||
false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true, compaction_reason);
|
||||
/* snapshot_checker */ nullptr, compaction_reason,
|
||||
/* trim_ts */ "", score_,
|
||||
/* l0_files_might_overlap */ true);
|
||||
}
|
||||
|
||||
// Look at overall size amplification. If size amplification
|
||||
@@ -1039,18 +1129,19 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
sr->Dump(file_num_buf, sizeof(file_num_buf), true);
|
||||
if (sr->being_compacted) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: stopping at sorted run undergoing compaction: "
|
||||
"%s[%" ROCKSDB_PRIszt "]",
|
||||
cf_name_.c_str(), file_num_buf, start_index - 1);
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: stopping for size amp compaction at "
|
||||
"sorted run undergoing compaction: "
|
||||
"%s[%" ROCKSDB_PRIszt "]",
|
||||
cf_name_.c_str(), file_num_buf, start_index - 1);
|
||||
} else if (sr->level_has_marked_standalone_rangedel) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: stopping at sorted run that has standalone range "
|
||||
"tombstone files marked for compaction: "
|
||||
"%s[%" ROCKSDB_PRIszt "]",
|
||||
cf_name_.c_str(), file_num_buf, start_index - 1);
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: stopping for size amp compaction at "
|
||||
"sorted run that has "
|
||||
"standalone range "
|
||||
"tombstone files marked for compaction: "
|
||||
"%s[%" ROCKSDB_PRIszt "]",
|
||||
cf_name_.c_str(), file_num_buf, start_index - 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1066,11 +1157,12 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
{
|
||||
const size_t num_l0_to_exclude = MightExcludeNewL0sToReduceWriteStop(
|
||||
num_l0_files, end_index, start_index, candidate_size);
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: Excluding %" ROCKSDB_PRIszt
|
||||
" latest L0 files to reduce potential write stop "
|
||||
"triggered by `level0_stop_writes_trigger`",
|
||||
cf_name_.c_str(), num_l0_to_exclude);
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: Excluding for size amp compaction %" ROCKSDB_PRIszt
|
||||
" latest L0 files to reduce potential write stop "
|
||||
"triggered by `level0_stop_writes_trigger`",
|
||||
cf_name_.c_str(), num_l0_to_exclude);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1088,18 +1180,18 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
|
||||
// size amplification = percentage of additional size
|
||||
if (candidate_size * 100 < ratio * base_sr_size) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: size amp not needed. newer-files-total-size %" PRIu64
|
||||
" earliest-file-size %" PRIu64,
|
||||
cf_name_.c_str(), candidate_size, base_sr_size);
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: size amp compction not needed. "
|
||||
"newer-files-total-size %" PRIu64
|
||||
" earliest-file-size %" PRIu64,
|
||||
cf_name_.c_str(), candidate_size, base_sr_size);
|
||||
return nullptr;
|
||||
} else {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: size amp needed. newer-files-total-size %" PRIu64
|
||||
" earliest-file-size %" PRIu64,
|
||||
cf_name_.c_str(), candidate_size, base_sr_size);
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: size amp compaction needed. "
|
||||
"newer-files-total-size %" PRIu64
|
||||
" earliest-file-size %" PRIu64,
|
||||
cf_name_.c_str(), candidate_size, base_sr_size);
|
||||
}
|
||||
// Since incremental compaction can't include more than second last
|
||||
// level, it can introduce penalty, compared to full compaction. We
|
||||
@@ -1354,10 +1446,9 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
|
||||
/* max_subcompactions */ 0, /* grandparents */ {},
|
||||
/* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true,
|
||||
CompactionReason::kUniversalSizeAmplification);
|
||||
CompactionReason::kUniversalSizeAmplification,
|
||||
/* trim_ts */ "", score_,
|
||||
/* l0_files_might_overlap */ true);
|
||||
}
|
||||
|
||||
// Pick files marked for compaction. Typically, files are marked by
|
||||
@@ -1450,6 +1541,10 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
}
|
||||
assert(output_level <= max_output_level);
|
||||
|
||||
if (!MeetsOutputLevelRequirements(output_level)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (output_level != 0) {
|
||||
if (start_level == 0) {
|
||||
if (!picker_->GetOverlappingL0Files(vstorage_, &start_level_inputs,
|
||||
@@ -1503,11 +1598,9 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, grandparents, earliest_snapshot_,
|
||||
snapshot_checker_,
|
||||
/* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
snapshot_checker_, CompactionReason::kFilesMarkedForCompaction,
|
||||
/* trim_ts */ "", score_,
|
||||
/* l0_files_might_overlap */ true);
|
||||
}
|
||||
|
||||
Compaction* UniversalCompactionBuilder::PickCompactionToOldest(
|
||||
@@ -1574,6 +1667,10 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
|
||||
output_level = sorted_runs_[end_index + 1].level - 1;
|
||||
}
|
||||
|
||||
if (!MeetsOutputLevelRequirements(output_level)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// intra L0 compactions outputs could have overlap
|
||||
if (output_level != 0 && picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
@@ -1599,10 +1696,9 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {},
|
||||
/* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true, compaction_reason);
|
||||
/* snapshot_checker */ nullptr, compaction_reason,
|
||||
/* trim_ts */ "", score_,
|
||||
/* l0_files_might_overlap */ true);
|
||||
}
|
||||
|
||||
Compaction* UniversalCompactionBuilder::PickPeriodicCompaction() {
|
||||
|
||||
@@ -18,12 +18,15 @@ class UniversalCompactionPicker : public CompactionPicker {
|
||||
UniversalCompactionPicker(const ImmutableOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
|
||||
// If `require_max_output_level` is true, only pick compaction
|
||||
// with max output level or return nullptr if no such compaction exists.
|
||||
Compaction* PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
LogBuffer* log_buffer, bool require_max_output_level = false) override;
|
||||
int MaxOutputLevel() const override { return NumberLevels() - 1; }
|
||||
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
|
||||
@@ -41,7 +41,7 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
}
|
||||
|
||||
compaction_input.cf_name = compaction->column_family_data()->GetName();
|
||||
compaction_input.snapshots = existing_snapshots_;
|
||||
compaction_input.snapshots = job_context_->snapshot_seqs;
|
||||
compaction_input.has_begin = sub_compact->start.has_value();
|
||||
compaction_input.begin =
|
||||
compaction_input.has_begin ? sub_compact->start->ToString() : "";
|
||||
@@ -74,7 +74,9 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
compaction_input.output_level, input_files_oss.str().c_str());
|
||||
CompactionServiceJobInfo info(
|
||||
dbname_, db_id_, db_session_id_, GetCompactionId(sub_compact),
|
||||
dbname_, db_id_, db_session_id_,
|
||||
compaction->column_family_data()->GetID(),
|
||||
compaction->column_family_data()->GetName(), GetCompactionId(sub_compact),
|
||||
thread_pri_, compaction->compaction_reason(),
|
||||
compaction->is_full_compaction(), compaction->is_manual_compaction(),
|
||||
compaction->bottommost_level(), compaction->start_level(),
|
||||
@@ -113,7 +115,7 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
}
|
||||
|
||||
std::string debug_str_before_wait =
|
||||
compaction->input_version()->DebugString();
|
||||
compaction->input_version()->DebugString(/*hex=*/true);
|
||||
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Waiting for remote compaction...",
|
||||
@@ -124,13 +126,14 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
&compaction_result_binary);
|
||||
|
||||
if (compaction_status != CompactionServiceJobStatus::kSuccess) {
|
||||
ROCKS_LOG_ERROR(db_options_.info_log,
|
||||
"[%s] [JOB %d] Wait() status is not kSuccess. "
|
||||
"\nDebugString Before Wait():\n%s"
|
||||
"\nDebugString After Wait():\n%s",
|
||||
compaction->column_family_data()->GetName().c_str(),
|
||||
job_id_, debug_str_before_wait.c_str(),
|
||||
compaction->input_version()->DebugString().c_str());
|
||||
ROCKS_LOG_ERROR(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Wait() status is not kSuccess. "
|
||||
"\nDebugString Before Wait():\n%s"
|
||||
"\nDebugString After Wait():\n%s",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
debug_str_before_wait.c_str(),
|
||||
compaction->input_version()->DebugString(/*hex=*/true).c_str());
|
||||
}
|
||||
|
||||
if (compaction_status == CompactionServiceJobStatus::kUseLocal) {
|
||||
@@ -218,18 +221,24 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
}
|
||||
|
||||
FileMetaData meta;
|
||||
uint64_t file_size;
|
||||
// FIXME: file_size should be part of CompactionServiceOutputFile so that
|
||||
// we don't get DB corruption if the full file size has not been propagated
|
||||
// back to the caller through the file system (which could have metadata
|
||||
// lag or caching bugs).
|
||||
s = fs_->GetFileSize(tgt_file, IOOptions(), &file_size, nullptr);
|
||||
uint64_t file_size = file.file_size;
|
||||
|
||||
// TODO - Clean this up in the next release.
|
||||
// For backward compatibility - in case the remote worker does not populate
|
||||
// the file_size yet. If missing, continue to populate this from the file
|
||||
// system.
|
||||
if (file_size == 0) {
|
||||
s = fs_->GetFileSize(tgt_file, IOOptions(), &file_size, nullptr);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
sub_compact->status = s;
|
||||
db_options_.compaction_service->OnInstallation(
|
||||
response.scheduled_job_id, CompactionServiceJobStatus::kFailure);
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
assert(file_size > 0);
|
||||
|
||||
meta.fd = FileDescriptor(file_num, compaction->output_path_id(), file_size,
|
||||
file.smallest_seqno, file.largest_seqno);
|
||||
meta.smallest.DecodeFrom(file.smallest_internal_key);
|
||||
@@ -295,9 +304,9 @@ CompactionServiceCompactionJob::CompactionServiceCompactionJob(
|
||||
VersionSet* versions, const std::atomic<bool>* shutting_down,
|
||||
LogBuffer* log_buffer, FSDirectory* output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
|
||||
const std::string& dbname, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
JobContext* job_context, std::shared_ptr<Cache> table_cache,
|
||||
EventLogger* event_logger, const std::string& dbname,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<bool>& manual_compaction_canceled,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
std::string output_path,
|
||||
@@ -306,9 +315,8 @@ CompactionServiceCompactionJob::CompactionServiceCompactionJob(
|
||||
: CompactionJob(job_id, compaction, db_options, mutable_db_options,
|
||||
file_options, versions, shutting_down, log_buffer, nullptr,
|
||||
output_directory, nullptr, stats, db_mutex,
|
||||
db_error_handler, std::move(existing_snapshots),
|
||||
kMaxSequenceNumber, nullptr, nullptr,
|
||||
std::move(table_cache), event_logger,
|
||||
db_error_handler, job_context, std::move(table_cache),
|
||||
event_logger,
|
||||
compaction->mutable_cf_options().paranoid_file_checks,
|
||||
compaction->mutable_cf_options().report_bg_io_stats, dbname,
|
||||
&(compaction_service_result->stats), Env::Priority::USER,
|
||||
@@ -418,14 +426,14 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
for (const auto& output_file : sub_compact->GetOutputs()) {
|
||||
auto& meta = output_file.meta;
|
||||
compaction_result_->output_files.emplace_back(
|
||||
MakeTableFileName(meta.fd.GetNumber()), meta.fd.smallest_seqno,
|
||||
meta.fd.largest_seqno, meta.smallest.Encode().ToString(),
|
||||
meta.largest.Encode().ToString(), meta.oldest_ancester_time,
|
||||
meta.file_creation_time, meta.epoch_number, meta.file_checksum,
|
||||
meta.file_checksum_func_name, output_file.validator.GetHash(),
|
||||
meta.marked_for_compaction, meta.unique_id,
|
||||
*output_file.table_properties, output_file.is_proximal_level,
|
||||
meta.temperature);
|
||||
MakeTableFileName(meta.fd.GetNumber()), meta.fd.GetFileSize(),
|
||||
meta.fd.smallest_seqno, meta.fd.largest_seqno,
|
||||
meta.smallest.Encode().ToString(), meta.largest.Encode().ToString(),
|
||||
meta.oldest_ancester_time, meta.file_creation_time, meta.epoch_number,
|
||||
meta.file_checksum, meta.file_checksum_func_name,
|
||||
output_file.validator.GetHash(), meta.marked_for_compaction,
|
||||
meta.unique_id, *output_file.table_properties,
|
||||
output_file.is_proximal_level, meta.temperature);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,6 +533,10 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
{offsetof(struct CompactionServiceOutputFile, file_name),
|
||||
OptionType::kEncodedString, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"file_size",
|
||||
{offsetof(struct CompactionServiceOutputFile, file_size),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"smallest_seqno",
|
||||
{offsetof(struct CompactionServiceOutputFile, smallest_seqno),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/utilities/options_util.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
#include "utilities/merge_operators/string_append/stringappend.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -21,10 +22,10 @@ class MyTestCompactionService : public CompactionService {
|
||||
: db_path_(std::move(db_path)),
|
||||
options_(options),
|
||||
statistics_(statistics),
|
||||
start_info_("na", "na", "na", 0, Env::TOTAL, CompactionReason::kUnknown,
|
||||
false, false, false, -1, -1),
|
||||
wait_info_("na", "na", "na", 0, Env::TOTAL, CompactionReason::kUnknown,
|
||||
false, false, false, -1, -1),
|
||||
start_info_("na", "na", "na", 0, "na", 0, Env::TOTAL,
|
||||
CompactionReason::kUnknown, false, false, false, -1, -1),
|
||||
wait_info_("na", "na", "na", 0, "na", 0, Env::TOTAL,
|
||||
CompactionReason::kUnknown, false, false, false, -1, -1),
|
||||
listeners_(listeners),
|
||||
table_properties_collector_factories_(
|
||||
std::move(table_properties_collector_factories)) {}
|
||||
@@ -434,6 +435,30 @@ TEST_F(CompactionServiceTest, ManualCompaction) {
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
|
||||
auto info = my_cs->GetCompactionInfoForStart();
|
||||
ASSERT_EQ(0, info.cf_id);
|
||||
ASSERT_EQ(kDefaultColumnFamilyName, info.cf_name);
|
||||
|
||||
info = my_cs->GetCompactionInfoForWait();
|
||||
ASSERT_EQ(0, info.cf_id);
|
||||
ASSERT_EQ(kDefaultColumnFamilyName, info.cf_name);
|
||||
|
||||
// Test non-default CF
|
||||
ASSERT_OK(
|
||||
db_->CompactRange(CompactRangeOptions(), handles_[1], nullptr, nullptr));
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
|
||||
info = my_cs->GetCompactionInfoForStart();
|
||||
ASSERT_EQ(handles_[1]->GetID(), info.cf_id);
|
||||
ASSERT_EQ(handles_[1]->GetName(), info.cf_name);
|
||||
|
||||
info = my_cs->GetCompactionInfoForWait();
|
||||
ASSERT_EQ(handles_[1]->GetID(), info.cf_id);
|
||||
ASSERT_EQ(handles_[1]->GetName(), info.cf_name);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CompactionOutputFileIOError) {
|
||||
@@ -1198,6 +1223,32 @@ TEST_F(CompactionServiceTest, CompactionFilter) {
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), 1);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, MergeOperator) {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator.reset(new StringAppendOperator(','));
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
for (int i = 0; i < 200; i++) {
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), Key(i),
|
||||
"merge_op_append_" + std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
// verify result
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i) + ",merge_op_append_" +
|
||||
std::to_string(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i) + ",merge_op_append_" +
|
||||
std::to_string(i));
|
||||
}
|
||||
}
|
||||
auto my_cs = GetCompactionService();
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), 1);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, Snapshot) {
|
||||
Options options = CurrentOptions();
|
||||
ReopenWithCompactionService(&options);
|
||||
@@ -1371,6 +1422,7 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
ASSERT_EQ(true, info.bottommost_level);
|
||||
ASSERT_EQ(1, info.base_input_level);
|
||||
ASSERT_EQ(2, info.output_level);
|
||||
ASSERT_EQ(kDefaultColumnFamilyName, info.cf_name);
|
||||
|
||||
// Test priority BOTTOM
|
||||
env_->SetBackgroundThreads(1, Env::BOTTOM);
|
||||
|
||||
@@ -106,7 +106,11 @@ class SubcompactionState {
|
||||
subcompaction_job_info.subcompaction_job_id = static_cast<int>(sub_job_id);
|
||||
subcompaction_job_info.base_input_level = c->start_level();
|
||||
subcompaction_job_info.output_level = c->output_level();
|
||||
subcompaction_job_info.compaction_reason = c->compaction_reason();
|
||||
subcompaction_job_info.compression = c->output_compression();
|
||||
subcompaction_job_info.stats = compaction_job_stats;
|
||||
subcompaction_job_info.blob_compression_type =
|
||||
c->mutable_cf_options().blob_compression_type;
|
||||
}
|
||||
|
||||
SubcompactionState() = delete;
|
||||
|
||||
@@ -225,6 +225,8 @@ TEST_F(TieredCompactionTest, SequenceBasedTieredStorageUniversal) {
|
||||
flush_stats.micros = 1;
|
||||
flush_stats.bytes_written = bytes_per_file;
|
||||
flush_stats.num_output_files = 1;
|
||||
flush_stats.num_input_records = kNumKeys;
|
||||
flush_stats.num_output_records = kNumKeys;
|
||||
expect_stats[0].Add(flush_stats);
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -1080,6 +1082,8 @@ TEST_F(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
flush_stats.micros = 1;
|
||||
flush_stats.bytes_written = bytes_per_file;
|
||||
flush_stats.num_output_files = 1;
|
||||
flush_stats.num_input_records = kNumKeys;
|
||||
flush_stats.num_output_records = kNumKeys;
|
||||
expect_stats[0].Add(flush_stats);
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
+4
-3
@@ -93,9 +93,10 @@ Status VerifySstFileChecksumInternal(const Options& options,
|
||||
nullptr /* file_read_hist */, ioptions.rate_limiter.get()));
|
||||
const bool kImmortal = true;
|
||||
auto reader_options = TableReaderOptions(
|
||||
ioptions, options.prefix_extractor, env_options, internal_comparator,
|
||||
options.block_protection_bytes_per_key, false /* skip_filters */,
|
||||
!kImmortal, false /* force_direct_prefetch */, -1 /* level */);
|
||||
ioptions, options.prefix_extractor, options.compression_manager.get(),
|
||||
env_options, internal_comparator, options.block_protection_bytes_per_key,
|
||||
false /* skip_filters */, !kImmortal, false /* force_direct_prefetch */,
|
||||
-1 /* level */);
|
||||
reader_options.largest_seqno = largest_seqno;
|
||||
s = options.table_factory->NewTableReader(
|
||||
read_options, reader_options, std::move(file_reader), file_size,
|
||||
|
||||
+74
-6
@@ -556,6 +556,74 @@ TEST_F(CorruptionTest, TableFileFooterNotMagic) {
|
||||
ASSERT_TRUE(s.ToString().find(".sst") != std::string::npos);
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, DBOpenWithWrongFileSize) {
|
||||
// Validate that when paranoid flag is true, DB::Open() fails if one of the
|
||||
// file corrupted. Validate that when paranoid flag is false, DB::Open()
|
||||
// succeed if one of the file corrupted, and the healthy file is readable.
|
||||
CloseDb();
|
||||
|
||||
const std::string test_cf_name = "test_cf";
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs;
|
||||
cf_descs.emplace_back(kDefaultColumnFamilyName, ColumnFamilyOptions());
|
||||
cf_descs.emplace_back(test_cf_name, ColumnFamilyOptions());
|
||||
|
||||
{
|
||||
options_.create_missing_column_families = true;
|
||||
std::vector<ColumnFamilyHandle*> cfhs;
|
||||
ASSERT_OK(DB::Open(options_, dbname_, cf_descs, &cfhs, &db_));
|
||||
assert(db_ != nullptr); // suppress false clang-analyze report
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), cfhs[0], "k", "v"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), cfhs[1], "k1", "v1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), cfhs[0], "k2", "v2"));
|
||||
for (auto* cfh : cfhs) {
|
||||
delete cfh;
|
||||
}
|
||||
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
|
||||
ASSERT_OK(dbi->TEST_FlushMemTable());
|
||||
|
||||
// ********************************************
|
||||
// Corrupt the file by making the file bigger
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
std::string filename = dbname_ + metadata[0].name;
|
||||
const auto& fs = options_.env->GetFileSystem();
|
||||
{
|
||||
std::unique_ptr<FSWritableFile> f;
|
||||
ASSERT_OK(fs->ReopenWritableFile(filename, FileOptions(), &f, nullptr));
|
||||
ASSERT_OK(f->Append("blahblah", IOOptions(), nullptr));
|
||||
ASSERT_OK(f->Close(IOOptions(), nullptr));
|
||||
}
|
||||
CloseDb();
|
||||
}
|
||||
|
||||
// DB failed to open due to one of the file is corrupted, as paranoid flag is
|
||||
// true
|
||||
options_.paranoid_checks = true;
|
||||
std::vector<ColumnFamilyHandle*> cfhs;
|
||||
auto s = DB::Open(options_, dbname_, cf_descs, &cfhs, &db_);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(s.ToString().find("file size mismatch") != std::string::npos);
|
||||
|
||||
// DB opened successfully, as paranoid flag is false, validate the one that is
|
||||
// healthy is still accessible
|
||||
options_.paranoid_checks = false;
|
||||
ASSERT_OK(DB::Open(options_, dbname_, cf_descs, &cfhs, &db_));
|
||||
assert(db_ != nullptr); // suppress false clang-analyze report
|
||||
|
||||
std::string v;
|
||||
ASSERT_OK(db_->Get(ReadOptions(), cfhs[1], "k1", &v));
|
||||
ASSERT_EQ(v, "v1");
|
||||
|
||||
// Validate the default column family is corrupted
|
||||
Check(0, 0);
|
||||
s = db_->Get(ReadOptions(), cfhs[0], "k1", &v);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
|
||||
delete cfhs[1];
|
||||
delete cfhs[0];
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, TableFileWrongSize) {
|
||||
Build(100);
|
||||
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
|
||||
@@ -579,13 +647,16 @@ TEST_F(CorruptionTest, TableFileWrongSize) {
|
||||
// DB actually accepts this without paranoid checks, relying on size
|
||||
// recorded in manifest to locate the SST footer.
|
||||
options_.paranoid_checks = false;
|
||||
options_.skip_checking_sst_file_sizes_on_db_open = false;
|
||||
Reopen();
|
||||
Check(100, 100);
|
||||
// As footer could not be extraced, file is completely unreadable
|
||||
Check(0, 0);
|
||||
std::string v;
|
||||
auto s = db_->Get(ReadOptions(), "k1", &v);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
|
||||
// But reports the issue with paranoid checks
|
||||
options_.paranoid_checks = true;
|
||||
Status s = TryReopen();
|
||||
s = TryReopen();
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(s.ToString().find("file size mismatch") != std::string::npos);
|
||||
|
||||
@@ -851,9 +922,6 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
// Skip verifying record count against TableProperties for
|
||||
// MockTables
|
||||
options.compaction_verify_record_count = false;
|
||||
Status s;
|
||||
for (const auto& mode : corruption_modes) {
|
||||
delete db_;
|
||||
|
||||
@@ -5086,6 +5086,7 @@ TEST_F(DBBasicTest, DisallowMemtableWrite) {
|
||||
options_allow.create_if_missing = true;
|
||||
Options options_disallow = options_allow;
|
||||
options_disallow.disallow_memtable_writes = true;
|
||||
options_disallow.paranoid_memory_checks = true;
|
||||
|
||||
DestroyAndReopen(options_allow);
|
||||
// CFs allowing and disallowing memtable write
|
||||
@@ -5125,6 +5126,11 @@ TEST_F(DBBasicTest, DisallowMemtableWrite) {
|
||||
EXPECT_EQ(Get(2, "b2"), "2");
|
||||
EXPECT_EQ(Get(3, "b3"), "NOT_FOUND");
|
||||
|
||||
std::unique_ptr<Iterator> iter(
|
||||
dbfull()->NewIterator(ReadOptions(), handles_[3]));
|
||||
iter->Seek("a3");
|
||||
ASSERT_OK(iter->status());
|
||||
iter.reset();
|
||||
// When the DB is re-opened with WAL entries for a CF that is newly setting
|
||||
// disallow_memtable_writes, we detect that and fail the open gracefully.
|
||||
ASSERT_EQ(TryReopenWithColumnFamilies(
|
||||
|
||||
+72
-57
@@ -506,6 +506,8 @@ TEST_P(DBBlockCacheTest1, WarmCacheWithBlocksDuringFlush) {
|
||||
table_options.prepopulate_block_cache =
|
||||
BlockBasedTableOptions::PrepopulateBlockCache::kFlushOnly;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
// Include a compression dictionary block
|
||||
options.compression_opts.max_dict_bytes = 123;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::string value(kValueSize, 'a');
|
||||
@@ -537,6 +539,9 @@ TEST_P(DBBlockCacheTest1, WarmCacheWithBlocksDuringFlush) {
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_FILTER_HIT));
|
||||
}
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_FILTER_MISS));
|
||||
|
||||
// Including compression dict
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_MISS));
|
||||
}
|
||||
|
||||
// Verify compaction not counted
|
||||
@@ -824,68 +829,78 @@ TEST_F(DBBlockCacheTest, CacheCompressionDict) {
|
||||
const int kNumEntriesPerFile = 128;
|
||||
const int kNumBytesPerEntry = 1024;
|
||||
|
||||
// Try all the available libraries that support dictionary compression
|
||||
std::vector<CompressionType> compression_types;
|
||||
if (Zlib_Supported()) {
|
||||
compression_types.push_back(kZlibCompression);
|
||||
}
|
||||
if (LZ4_Supported()) {
|
||||
compression_types.push_back(kLZ4Compression);
|
||||
compression_types.push_back(kLZ4HCCompression);
|
||||
}
|
||||
if (ZSTD_Supported()) {
|
||||
compression_types.push_back(kZSTD);
|
||||
}
|
||||
std::vector<CompressionType> dict_compressions =
|
||||
GetSupportedDictCompressions();
|
||||
Random rnd(301);
|
||||
for (auto compression_type : compression_types) {
|
||||
Options options = CurrentOptions();
|
||||
options.bottommost_compression = compression_type;
|
||||
options.bottommost_compression_opts.max_dict_bytes = 4096;
|
||||
options.bottommost_compression_opts.enabled = true;
|
||||
options.create_if_missing = true;
|
||||
options.num_levels = 2;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.target_file_size_base = kNumEntriesPerFile * kNumBytesPerEntry;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache.reset(new MockCache());
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
// Format version before and after compression handling changes
|
||||
for (int format_version : {6, 7}) {
|
||||
// Test all supported compression types because (at least historically)
|
||||
// dictionary compression could be enabled and a dictionary block saved
|
||||
// but ignored by some compression types. Ensure we at least don't crash
|
||||
// or return corruption for those.
|
||||
for (auto compression_type : GetSupportedCompressions()) {
|
||||
// Extra handling checks only for types actually supporting dictionary
|
||||
// compression.
|
||||
bool dict_supported =
|
||||
std::count(dict_compressions.begin(), dict_compressions.end(),
|
||||
compression_type) > 0;
|
||||
|
||||
RecordCacheCountersForCompressionDict(options);
|
||||
Options options = CurrentOptions();
|
||||
options.bottommost_compression = compression_type;
|
||||
options.bottommost_compression_opts.max_dict_bytes = 4096;
|
||||
options.bottommost_compression_opts.enabled = true;
|
||||
options.create_if_missing = true;
|
||||
options.num_levels = 2;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.target_file_size_base = kNumEntriesPerFile * kNumBytesPerEntry;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache.reset(new MockCache());
|
||||
table_options.format_version = format_version;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
ASSERT_EQ(i, NumTableFilesAtLevel(0, 0));
|
||||
for (int j = 0; j < kNumEntriesPerFile; ++j) {
|
||||
std::string value = rnd.RandomString(kNumBytesPerEntry);
|
||||
ASSERT_OK(Put(Key(j * kNumFiles + i), value.c_str()));
|
||||
RecordCacheCountersForCompressionDict(options);
|
||||
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
ASSERT_EQ(i, NumTableFilesAtLevel(0, 0));
|
||||
for (int j = 0; j < kNumEntriesPerFile; ++j) {
|
||||
std::string value = rnd.RandomString(kNumBytesPerEntry);
|
||||
ASSERT_OK(Put(Key(j * kNumFiles + i), value.c_str()));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(kNumFiles, NumTableFilesAtLevel(1));
|
||||
|
||||
if (dict_supported) {
|
||||
// Compression dictionary blocks are preloaded.
|
||||
CheckCacheCountersForCompressionDict(
|
||||
options, kNumFiles /* expected_compression_dict_misses */,
|
||||
0 /* expected_compression_dict_hits */,
|
||||
kNumFiles /* expected_compression_dict_inserts */);
|
||||
}
|
||||
|
||||
// Seek to a key in a file. It should cause the SST's dictionary
|
||||
// meta-block to be read.
|
||||
RecordCacheCounters(options);
|
||||
RecordCacheCountersForCompressionDict(options);
|
||||
ReadOptions read_options;
|
||||
ASSERT_NE("NOT_FOUND", Get(Key(kNumFiles * kNumEntriesPerFile - 1)));
|
||||
|
||||
if (dict_supported) {
|
||||
// Two block hits: index and dictionary since they are prefetched
|
||||
// One block missed/added: data block
|
||||
CheckCacheCounters(options, 1 /* expected_misses */,
|
||||
2 /* expected_hits */, 1 /* expected_inserts */,
|
||||
0 /* expected_failures */);
|
||||
CheckCacheCountersForCompressionDict(
|
||||
options, 0 /* expected_compression_dict_misses */,
|
||||
1 /* expected_compression_dict_hits */,
|
||||
0 /* expected_compression_dict_inserts */);
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(kNumFiles, NumTableFilesAtLevel(1));
|
||||
|
||||
// Compression dictionary blocks are preloaded.
|
||||
CheckCacheCountersForCompressionDict(
|
||||
options, kNumFiles /* expected_compression_dict_misses */,
|
||||
0 /* expected_compression_dict_hits */,
|
||||
kNumFiles /* expected_compression_dict_inserts */);
|
||||
|
||||
// Seek to a key in a file. It should cause the SST's dictionary meta-block
|
||||
// to be read.
|
||||
RecordCacheCounters(options);
|
||||
RecordCacheCountersForCompressionDict(options);
|
||||
ReadOptions read_options;
|
||||
ASSERT_NE("NOT_FOUND", Get(Key(kNumFiles * kNumEntriesPerFile - 1)));
|
||||
// Two block hits: index and dictionary since they are prefetched
|
||||
// One block missed/added: data block
|
||||
CheckCacheCounters(options, 1 /* expected_misses */, 2 /* expected_hits */,
|
||||
1 /* expected_inserts */, 0 /* expected_failures */);
|
||||
CheckCacheCountersForCompressionDict(
|
||||
options, 0 /* expected_compression_dict_misses */,
|
||||
1 /* expected_compression_dict_hits */,
|
||||
0 /* expected_compression_dict_inserts */);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+448
-239
@@ -455,6 +455,72 @@ TEST_P(DBCompactionTestWithParam, CompactionDeletionTrigger) {
|
||||
}
|
||||
}
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
TEST_F(DBCompactionTest, UniversalReduceFileLockingRepickNothing) {
|
||||
const int kFileNumCompactionTrigger = 3;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_options_universal.reduce_file_locking = true;
|
||||
// Set `max_background_jobs` to be 3 to allow low and bottom priority thread
|
||||
// to run compaction together
|
||||
options.max_background_jobs = 3;
|
||||
Env::Default()->SetBackgroundThreads(1, Env::Priority::BOTTOM);
|
||||
options.num_levels = 3;
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.level0_file_num_compaction_trigger = kFileNumCompactionTrigger;
|
||||
options.compaction_options_universal.max_size_amplification_percent = 1;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Need to get a token to enable compaction parallelism up to
|
||||
// `max_background_compactions` jobs.
|
||||
auto pressure_token =
|
||||
dbfull()->TEST_write_controler().GetCompactionPressureToken();
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{// Wait for the full (bottom-priority) compaction to be pre-picked as an
|
||||
// intent (that is allowing files to be picked by other compactions and
|
||||
// will pick later when the bottom-priority thread is available to
|
||||
// execute the compaction) before triggering the low-priority compaction.
|
||||
{"DBImpl::BackgroundCompaction:ForwardToBottomPriPool",
|
||||
"LowPriCompaction"},
|
||||
// Wait for low-priority compaction to start before
|
||||
// repicking for the full compaction intent (bottom-priority), enabling
|
||||
// them to run in parallel.
|
||||
{"DBImpl::BackgroundCompaction:NonTrivial",
|
||||
"DBImpl::BGWorkBottomCompaction"}});
|
||||
|
||||
bool bottom_pri_compaction_attempt_repick = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction():AfterPickCompactionBottomPri",
|
||||
[&](void* arg) {
|
||||
bottom_pri_compaction_attempt_repick = true;
|
||||
Compaction* c = static_cast<Compaction*>(arg);
|
||||
// Verify the intended full compaction for bottom priority thread does
|
||||
// not get to run (i.e, output to bottommost level) since when it
|
||||
// repicks its files, some of the the intended input files are already
|
||||
// compacted by the low priority thread
|
||||
assert(c == nullptr);
|
||||
});
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
for (int i = 0; i < kFileNumCompactionTrigger; ++i) {
|
||||
if (i == 0) {
|
||||
ASSERT_OK(Put("file_locked_for_bottom_pri_compaction", "value"));
|
||||
} else {
|
||||
ASSERT_OK(
|
||||
Put("file_not_locked_for_bottom_pri_compaction" + std::to_string(i),
|
||||
"value"));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("LowPriCompaction");
|
||||
ASSERT_OK(Put("a_new_file_to_pick_for_low_pri_compaction", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_TRUE(bottom_pri_compaction_attempt_repick);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, SkipStatsUpdateTest) {
|
||||
// This test verify UpdateAccumulatedStats is not on
|
||||
@@ -2824,46 +2890,99 @@ TEST_F(DBCompactionTest, L0_CompactionBug_Issue44_b) {
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ManualAutoRace) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BGWorkCompaction", "DBCompactionTest::ManualAutoRace:1"},
|
||||
{"DBImpl::RunManualCompaction:WaitScheduled",
|
||||
"BackgroundCallCompaction:0"}});
|
||||
const int kNumL0FilesTrigger = 4;
|
||||
// Verify that the auto compaction is retried after the conflicting exclusive
|
||||
// manual compaction finishes for:
|
||||
// 1. Non-bottom-priority compactions (tested with level compaction)
|
||||
// 2. Bottom-priority compactions (tested with universal compaction)
|
||||
for (auto compaction_style :
|
||||
{kCompactionStyleLevel, kCompactionStyleUniversal}) {
|
||||
Env::Default()->SetBackgroundThreads(
|
||||
compaction_style == kCompactionStyleUniversal ? 2 : 0,
|
||||
Env::Priority::BOTTOM);
|
||||
for (auto universal_reduce_file_locking : {false, true}) {
|
||||
if (compaction_style != kCompactionStyleUniversal &&
|
||||
universal_reduce_file_locking) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 3;
|
||||
options.level0_file_num_compaction_trigger = kNumL0FilesTrigger;
|
||||
options.compaction_style = compaction_style;
|
||||
options.compaction_options_universal.reduce_file_locking =
|
||||
universal_reduce_file_locking;
|
||||
|
||||
ASSERT_OK(Put(1, "foo", ""));
|
||||
ASSERT_OK(Put(1, "bar", ""));
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Put(1, "foo", ""));
|
||||
ASSERT_OK(Put(1, "bar", ""));
|
||||
// Generate four files in CF 0, which should trigger an auto compaction
|
||||
ASSERT_OK(Put("foo", ""));
|
||||
ASSERT_OK(Put("bar", ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo", ""));
|
||||
ASSERT_OK(Put("bar", ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo", ""));
|
||||
ASSERT_OK(Put("bar", ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo", ""));
|
||||
ASSERT_OK(Put("bar", ""));
|
||||
ASSERT_OK(Flush());
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"exclusive_manual_compaction_cf"}, options);
|
||||
|
||||
// The auto compaction is scheduled but waited until here
|
||||
TEST_SYNC_POINT("DBCompactionTest::ManualAutoRace:1");
|
||||
// The auto compaction will wait until the manual compaction is registerd
|
||||
// before processing so that it will be cancelled.
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = true;
|
||||
ASSERT_OK(dbfull()->CompactRange(cro, handles_[1], nullptr, nullptr));
|
||||
ASSERT_EQ("0,1", FilesPerLevel(1));
|
||||
// Set up sync points to ensure that the auto compaction
|
||||
// encounters a conflict from exclusive manual compaction before the auto
|
||||
// compaction gets to pick files, This will trigger a retry later.
|
||||
//
|
||||
// Specifically, the sync points are set up as following:
|
||||
// 1. Wait until background low-pri scheduled (not picking files yet) or
|
||||
// bottom-pri scheduled (not repicking files yet) for
|
||||
// `universal_reduce_file_locking = true` before triggering
|
||||
// CompactRange()
|
||||
//
|
||||
// 2. Wait until the triggered CompactRange()
|
||||
// registers its compaction and creates conflict before the auto
|
||||
// compaction picks or repicks files for the background compaction.
|
||||
if (compaction_style == kCompactionStyleLevel ||
|
||||
!universal_reduce_file_locking) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BGWorkCompaction", "DBCompactionTest::ManualAutoRace:1"},
|
||||
{"DBImpl::RunManualCompaction:WaitScheduled",
|
||||
"BackgroundCallCompaction:0"}});
|
||||
} else {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction:ForwardToBottomPriPool",
|
||||
"DBCompactionTest::ManualAutoRace:1"},
|
||||
{"DBImpl::RunManualCompaction:WaitScheduled",
|
||||
"BackgroundCallCompaction:0:BottomPri"}});
|
||||
}
|
||||
|
||||
// Eventually the cancelled compaction will be rescheduled and executed.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
bool encounter_conflict = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction()::Conflict",
|
||||
[&](void* /*arg*/) { encounter_conflict = true; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Generate files in CF 1 for exclusive CompactRange()
|
||||
ASSERT_OK(Put(1, "foo", ""));
|
||||
ASSERT_OK(Put(1, "bar", ""));
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Put(1, "foo", ""));
|
||||
ASSERT_OK(Put(1, "bar", ""));
|
||||
// Generate files in CF0 to trigger full compaction
|
||||
for (int i = 0; i < kNumL0FilesTrigger; ++i) {
|
||||
ASSERT_OK(Put("foo", ""));
|
||||
ASSERT_OK(Put("bar", ""));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBCompactionTest::ManualAutoRace:1");
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = true;
|
||||
ASSERT_OK(dbfull()->CompactRange(cro, handles_[1], nullptr, nullptr));
|
||||
ASSERT_EQ(compaction_style == kCompactionStyleLevel ? "0,1" : "0,0,1",
|
||||
FilesPerLevel(1));
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_TRUE(encounter_conflict);
|
||||
|
||||
// Verify that the auto compaction is eventually executed after the
|
||||
// exclusive CompactRange() finishes.
|
||||
ASSERT_EQ(compaction_style == kCompactionStyleLevel ? "0,1" : "0,0,1",
|
||||
FilesPerLevel(0));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
Env::Default()->SetBackgroundThreads(0, Env::Priority::BOTTOM);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, ManualCompaction) {
|
||||
@@ -3986,41 +4105,51 @@ TEST_P(DBCompactionTestWithParam, IntraL0CompactionDoesNotObsoleteDeletions) {
|
||||
TEST_P(DBCompactionTestWithParam, FullCompactionInBottomPriThreadPool) {
|
||||
const int kNumFilesTrigger = 3;
|
||||
Env::Default()->SetBackgroundThreads(1, Env::Priority::BOTTOM);
|
||||
for (bool use_universal_compaction : {false, true}) {
|
||||
Options options = CurrentOptions();
|
||||
if (use_universal_compaction) {
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
} else {
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
for (auto compaction_style :
|
||||
{kCompactionStyleLevel, kCompactionStyleUniversal}) {
|
||||
for (auto universal_reduce_file_locking : {false, true}) {
|
||||
if (compaction_style != kCompactionStyleUniversal &&
|
||||
universal_reduce_file_locking) {
|
||||
continue;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = compaction_style;
|
||||
if (compaction_style == kCompactionStyleLevel) {
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
} else {
|
||||
options.compaction_options_universal.reduce_file_locking =
|
||||
universal_reduce_file_locking;
|
||||
// Trigger compaction if size amplification exceeds 110%
|
||||
options.compaction_options_universal.max_size_amplification_percent =
|
||||
110;
|
||||
}
|
||||
options.num_levels = 4;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
options.level0_file_num_compaction_trigger = kNumFilesTrigger;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int num_bottom_pri_compactions = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BGWorkBottomCompaction",
|
||||
[&](void* /*arg*/) { ++num_bottom_pri_compactions; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int num = 0; num < kNumFilesTrigger; num++) {
|
||||
ASSERT_EQ(NumSortedRuns(), num);
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_EQ(1, num_bottom_pri_compactions);
|
||||
|
||||
// Verify that size amplification did occur
|
||||
ASSERT_EQ(NumSortedRuns(), 1);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
options.num_levels = 4;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
options.level0_file_num_compaction_trigger = kNumFilesTrigger;
|
||||
// Trigger compaction if size amplification exceeds 110%
|
||||
options.compaction_options_universal.max_size_amplification_percent = 110;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int num_bottom_pri_compactions = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BGWorkBottomCompaction",
|
||||
[&](void* /*arg*/) { ++num_bottom_pri_compactions; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int num = 0; num < kNumFilesTrigger; num++) {
|
||||
ASSERT_EQ(NumSortedRuns(), num);
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_EQ(1, num_bottom_pri_compactions);
|
||||
|
||||
// Verify that size amplification did occur
|
||||
ASSERT_EQ(NumSortedRuns(), 1);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
Env::Default()->SetBackgroundThreads(0, Env::Priority::BOTTOM);
|
||||
}
|
||||
@@ -6634,7 +6763,7 @@ TEST_P(RoundRobinSubcompactionsAgainstResources, SubcompactionsUsingResources) {
|
||||
// compaction is enough to make post-compaction L1 size less than
|
||||
// the maximum size (this test assumes only one round-robin compaction
|
||||
// is triggered by kLevelMaxLevelSize)
|
||||
options.max_compaction_bytes = 100000000;
|
||||
options.max_compaction_bytes = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
env_->SetBackgroundThreads(total_low_pri_threads_, Env::LOW);
|
||||
@@ -6667,41 +6796,33 @@ TEST_P(RoundRobinSubcompactionsAgainstResources, SubcompactionsUsingResources) {
|
||||
// More than 10 files are selected for round-robin under auto
|
||||
// compaction. The number of planned subcompaction is restricted by
|
||||
// the minimum number between available threads and compaction limits
|
||||
ASSERT_EQ(num_planned_subcompactions - options.max_subcompactions,
|
||||
std::min(total_low_pri_threads_, max_compaction_limits_) - 1);
|
||||
auto actual_reserved_threads =
|
||||
num_planned_subcompactions - options.max_subcompactions;
|
||||
auto expected_reserved_threads =
|
||||
std::min(total_low_pri_threads_, max_compaction_limits_) - 1;
|
||||
ASSERT_EQ(actual_reserved_threads, expected_reserved_threads);
|
||||
num_planned_subcompactions_verified = true;
|
||||
});
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"RoundRobinSubcompactionsAgainstResources:0",
|
||||
"BackgroundCallCompaction:0"},
|
||||
{"CompactionJob::AcquireSubcompactionResources:0",
|
||||
"RoundRobinSubcompactionsAgainstResources:1"},
|
||||
{"RoundRobinSubcompactionsAgainstResources:2",
|
||||
"CompactionJob::AcquireSubcompactionResources:1"},
|
||||
{"CompactionJob::ReleaseSubcompactionResources:0",
|
||||
"RoundRobinSubcompactionsAgainstResources:3"},
|
||||
{"RoundRobinSubcompactionsAgainstResources:4",
|
||||
"CompactionJob::ReleaseSubcompactionResources:1"}});
|
||||
|
||||
int acquire_count = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::AcquireSubcompactionResources:0",
|
||||
[&](void* /*arg*/) { acquire_count++; });
|
||||
int release_count = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::ReleaseSubcompactionResources",
|
||||
[&](void* /*arg*/) { release_count++; });
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_OK(dbfull()->EnableAutoCompaction({dbfull()->DefaultColumnFamily()}));
|
||||
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstResources:0");
|
||||
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstResources:1");
|
||||
auto pressure_token =
|
||||
dbfull()->TEST_write_controler().GetCompactionPressureToken();
|
||||
|
||||
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstResources:2");
|
||||
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstResources:3");
|
||||
// We can reserve more threads now except one is being used
|
||||
ASSERT_EQ(total_low_pri_threads_ - 1,
|
||||
env_->ReserveThreads(total_low_pri_threads_, Env::Priority::LOW));
|
||||
ASSERT_EQ(
|
||||
total_low_pri_threads_ - 1,
|
||||
env_->ReleaseThreads(total_low_pri_threads_ - 1, Env::Priority::LOW));
|
||||
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstResources:4");
|
||||
ASSERT_OK(dbfull()->EnableAutoCompaction({dbfull()->DefaultColumnFamily()}));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_TRUE(num_planned_subcompactions_verified);
|
||||
ASSERT_EQ(acquire_count, release_count);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
@@ -9434,104 +9555,134 @@ TEST_F(DBCompactionTest, CompactionWithChecksumHandoffManifest2) {
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, FIFOChangeTemperature) {
|
||||
for (bool write_time_default : {false, true}) {
|
||||
SCOPED_TRACE("write time default? " + std::to_string(write_time_default));
|
||||
for (bool should_allow_trivial_copy : {false, true}) {
|
||||
for (bool write_time_default : {false, true}) {
|
||||
int32_t before_compaction_calls = 0;
|
||||
int32_t after_compaction_calls = 0;
|
||||
if (should_allow_trivial_copy) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TriviaCopyBeforeCompaction",
|
||||
[&](void*) { ++before_compaction_calls; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TriviaCopyAfterCompaction",
|
||||
[&](void*) { ++after_compaction_calls; });
|
||||
} else {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:BeforeCompaction",
|
||||
[&](void*) { ++before_compaction_calls; });
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.num_levels = 1;
|
||||
options.max_open_files = -1;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
CompactionOptionsFIFO fifo_options;
|
||||
fifo_options.file_temperature_age_thresholds = {{Temperature::kCold, 1000}};
|
||||
fifo_options.max_table_files_size = 100000000;
|
||||
options.compaction_options_fifo = fifo_options;
|
||||
env_->SetMockSleep();
|
||||
if (write_time_default) {
|
||||
options.default_write_temperature = Temperature::kWarm;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
[&](void*) { ++after_compaction_calls; });
|
||||
}
|
||||
|
||||
SCOPED_TRACE("write time default? " + std::to_string(write_time_default));
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.num_levels = 1;
|
||||
options.max_open_files = -1;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
CompactionOptionsFIFO fifo_options;
|
||||
fifo_options.file_temperature_age_thresholds = {
|
||||
{Temperature::kCold, 1000}};
|
||||
fifo_options.max_table_files_size = 100000000;
|
||||
fifo_options.allow_trivial_copy_when_change_temperature =
|
||||
should_allow_trivial_copy;
|
||||
fifo_options.trivial_copy_buffer_size = 4096;
|
||||
options.compaction_options_fifo = fifo_options;
|
||||
env_->SetMockSleep();
|
||||
if (write_time_default) {
|
||||
options.default_write_temperature = Temperature::kWarm;
|
||||
}
|
||||
// Should be ignored (TODO: fail?)
|
||||
options.last_level_temperature = Temperature::kHot;
|
||||
Reopen(options);
|
||||
|
||||
int total_cold = 0;
|
||||
int total_warm = 0;
|
||||
int total_hot = 0;
|
||||
int total_unknown = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile::FileOptions.temperature", [&](void* arg) {
|
||||
Temperature temperature = *(static_cast<Temperature*>(arg));
|
||||
if (temperature == Temperature::kCold) {
|
||||
total_cold++;
|
||||
} else if (temperature == Temperature::kWarm) {
|
||||
total_warm++;
|
||||
} else if (temperature == Temperature::kHot) {
|
||||
total_hot++;
|
||||
} else {
|
||||
assert(temperature == Temperature::kUnknown);
|
||||
total_unknown++;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// The file system does not support checksum handoff. The check
|
||||
// will be ignored.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// First two L0 files both become eligible for temperature change
|
||||
// compaction They should be compacted one-by-one.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(1200);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
if (write_time_default) {
|
||||
// Also test dynamic option change
|
||||
ASSERT_OK(db_->SetOptions({{"default_write_temperature", "kHot"}}));
|
||||
}
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ColumnFamilyMetaData metadata;
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(4, metadata.file_count);
|
||||
if (write_time_default) {
|
||||
ASSERT_EQ(Temperature::kHot, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kWarm, metadata.levels[0].files[1].temperature);
|
||||
// Includes obsolete/deleted files moved to cold
|
||||
ASSERT_EQ(total_warm, 3);
|
||||
ASSERT_EQ(total_hot, 1);
|
||||
// Includes non-SST DB files
|
||||
ASSERT_GT(total_unknown, 0);
|
||||
} else {
|
||||
ASSERT_EQ(Temperature::kUnknown,
|
||||
metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kUnknown,
|
||||
metadata.levels[0].files[1].temperature);
|
||||
ASSERT_EQ(total_warm, 0);
|
||||
ASSERT_EQ(total_hot, 0);
|
||||
// Includes non-SST DB files
|
||||
ASSERT_GT(total_unknown, 4);
|
||||
}
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[2].temperature);
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[3].temperature);
|
||||
ASSERT_EQ(2, total_cold);
|
||||
|
||||
ASSERT_EQ(2, before_compaction_calls);
|
||||
ASSERT_EQ(2, after_compaction_calls);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
// Should be ignored (TODO: fail?)
|
||||
options.last_level_temperature = Temperature::kHot;
|
||||
Reopen(options);
|
||||
|
||||
int total_cold = 0;
|
||||
int total_warm = 0;
|
||||
int total_hot = 0;
|
||||
int total_unknown = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile::FileOptions.temperature", [&](void* arg) {
|
||||
Temperature temperature = *(static_cast<Temperature*>(arg));
|
||||
if (temperature == Temperature::kCold) {
|
||||
total_cold++;
|
||||
} else if (temperature == Temperature::kWarm) {
|
||||
total_warm++;
|
||||
} else if (temperature == Temperature::kHot) {
|
||||
total_hot++;
|
||||
} else {
|
||||
assert(temperature == Temperature::kUnknown);
|
||||
total_unknown++;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// The file system does not support checksum handoff. The check
|
||||
// will be ignored.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// First two L0 files both become eligible for temperature change compaction
|
||||
// They should be compacted one-by-one.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(1200);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
if (write_time_default) {
|
||||
// Also test dynamic option change
|
||||
ASSERT_OK(db_->SetOptions({{"default_write_temperature", "kHot"}}));
|
||||
}
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ColumnFamilyMetaData metadata;
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(4, metadata.file_count);
|
||||
if (write_time_default) {
|
||||
ASSERT_EQ(Temperature::kHot, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kWarm, metadata.levels[0].files[1].temperature);
|
||||
// Includes obsolete/deleted files moved to cold
|
||||
ASSERT_EQ(total_warm, 3);
|
||||
ASSERT_EQ(total_hot, 1);
|
||||
// Includes non-SST DB files
|
||||
ASSERT_GT(total_unknown, 0);
|
||||
} else {
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[1].temperature);
|
||||
ASSERT_EQ(total_warm, 0);
|
||||
ASSERT_EQ(total_hot, 0);
|
||||
// Includes non-SST DB files
|
||||
ASSERT_GT(total_unknown, 4);
|
||||
}
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[2].temperature);
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[3].temperature);
|
||||
ASSERT_EQ(2, total_cold);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9976,55 +10127,60 @@ TEST_F(DBCompactionTest, BottomPriCompactionCountsTowardConcurrencyLimit) {
|
||||
|
||||
env_->SetBackgroundThreads(1, Env::Priority::BOTTOM);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
options.num_levels = kNumLevels;
|
||||
DestroyAndReopen(options);
|
||||
for (bool universal_reduce_file_locking : {false, true}) {
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
options.num_levels = kNumLevels;
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.compaction_options_universal.reduce_file_locking =
|
||||
universal_reduce_file_locking;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Setup last level to be non-empty since it's a bit unclear whether
|
||||
// compaction to an empty level would be considered "bottommost".
|
||||
ASSERT_OK(Put(Key(0), "val"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(kNumLevels - 1);
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BGWorkBottomCompaction",
|
||||
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
"PreTriggerCompaction"},
|
||||
{"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
"PostTriggerCompaction",
|
||||
"BackgroundCallCompaction:0"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
port::Thread compact_range_thread([&] {
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
cro.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
|
||||
});
|
||||
|
||||
// Sleep in the low-pri thread so any newly scheduled compaction will be
|
||||
// queued. Otherwise it might finish before we check its existence.
|
||||
test::SleepingBackgroundTask sleeping_task_low;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||
Env::Priority::LOW);
|
||||
sleeping_task_low.WaitUntilSleeping();
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
"PreTriggerCompaction");
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
// Setup last level to be non-empty since it's a bit unclear whether
|
||||
// compaction to an empty level would be considered "bottommost".
|
||||
ASSERT_OK(Put(Key(0), "val"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ(0u, env_->GetThreadPoolQueueLen(Env::Priority::LOW));
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
"PostTriggerCompaction");
|
||||
MoveFilesToLevel(kNumLevels - 1);
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
compact_range_thread.join();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BGWorkBottomCompaction",
|
||||
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
"PreTriggerCompaction"},
|
||||
{"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
"PostTriggerCompaction",
|
||||
"BackgroundCallCompaction:0"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
port::Thread compact_range_thread([&] {
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
cro.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
|
||||
});
|
||||
|
||||
// Sleep in the low-pri thread so any newly scheduled compaction will be
|
||||
// queued. Otherwise it might finish before we check its existence.
|
||||
test::SleepingBackgroundTask sleeping_task_low;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
&sleeping_task_low, Env::Priority::LOW);
|
||||
sleeping_task_low.WaitUntilSleeping();
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
"PreTriggerCompaction");
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
ASSERT_OK(Put(Key(0), "val"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ(0u, env_->GetThreadPoolQueueLen(Env::Priority::LOW));
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
"PostTriggerCompaction");
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
compact_range_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, BottommostFileCompactionAllowIngestBehind) {
|
||||
@@ -10937,6 +11093,59 @@ TEST_F(DBCompactionTest, RecordNewestKeyTimeForTtlCompaction) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
||||
}
|
||||
|
||||
class PeriodicCompactionListener : public EventListener {
|
||||
public:
|
||||
explicit PeriodicCompactionListener() {}
|
||||
void OnCompactionBegin(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
if (ci.compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
++num_periodic_compactions;
|
||||
}
|
||||
}
|
||||
|
||||
std::atomic<int> num_periodic_compactions = 0;
|
||||
};
|
||||
|
||||
TEST_F(DBCompactionTest, PeriodicTask) {
|
||||
// Tests that when no trigger event is fired (flush/compaction/setoptions),
|
||||
// periodic compaction is still triggered by a scheduled periodic function.
|
||||
auto mock_clock = std::make_shared<MockSystemClock>(env_->GetSystemClock());
|
||||
mock_clock->SetCurrentTime(100);
|
||||
mock_clock->InstallTimedWaitFixCallback();
|
||||
auto mock_env = std::make_unique<CompositeEnvWrapper>(env_, mock_clock);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::StartPeriodicTaskScheduler:Init", [&](void* arg) {
|
||||
auto periodic_task_scheduler_ptr =
|
||||
static_cast<PeriodicTaskScheduler*>(arg);
|
||||
periodic_task_scheduler_ptr->TEST_OverrideTimer(mock_clock.get());
|
||||
});
|
||||
|
||||
Options options;
|
||||
options.env = mock_env.get();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.statistics = CreateDBStatistics();
|
||||
int kPeriodicCompactionSeconds = 7 * 24 * 60 * 60; // 1 week
|
||||
options.periodic_compaction_seconds = kPeriodicCompactionSeconds;
|
||||
options.num_levels = 50;
|
||||
auto listener = std::make_shared<PeriodicCompactionListener>();
|
||||
options.listeners.push_back(listener);
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
Random* rnd = Random::GetTLSInstance();
|
||||
for (int k = 0; k < 10; ++k) {
|
||||
ASSERT_OK(Put(Key(k), rnd->RandomString(100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange({}, nullptr, nullptr));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(49));
|
||||
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock->MockSleepForSeconds(kPeriodicCompactionSeconds + 1); });
|
||||
ASSERT_OK(db_->WaitForCompact({}));
|
||||
|
||||
ASSERT_EQ(listener->num_periodic_compactions, 1);
|
||||
Close();
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -17,9 +17,10 @@ class DBEncryptionTest : public DBTestBase {
|
||||
public:
|
||||
DBEncryptionTest()
|
||||
: DBTestBase("db_encryption_test", /*env_do_fsync=*/true) {}
|
||||
Env* GetTargetEnv() {
|
||||
Env* GetNonEncryptedEnv() {
|
||||
if (encrypted_env_ != nullptr) {
|
||||
return (static_cast<EnvWrapper*>(encrypted_env_))->target();
|
||||
return (static_cast_with_check<CompositeEnvWrapper>(encrypted_env_))
|
||||
->env_target();
|
||||
} else {
|
||||
return env_;
|
||||
}
|
||||
@@ -38,7 +39,7 @@ TEST_F(DBEncryptionTest, CheckEncrypted) {
|
||||
auto status = env_->GetChildren(dbname_, &fileNames);
|
||||
ASSERT_OK(status);
|
||||
|
||||
Env* target = GetTargetEnv();
|
||||
Env* target = GetNonEncryptedEnv();
|
||||
int hits = 0;
|
||||
for (auto it = fileNames.begin(); it != fileNames.end(); ++it) {
|
||||
if (*it == "LOCK") {
|
||||
@@ -89,7 +90,7 @@ TEST_F(DBEncryptionTest, CheckEncrypted) {
|
||||
}
|
||||
|
||||
TEST_F(DBEncryptionTest, ReadEmptyFile) {
|
||||
auto defaultEnv = GetTargetEnv();
|
||||
auto defaultEnv = GetNonEncryptedEnv();
|
||||
|
||||
// create empty file for reading it back in later
|
||||
auto envOptions = EnvOptions(CurrentOptions());
|
||||
@@ -116,6 +117,40 @@ TEST_F(DBEncryptionTest, ReadEmptyFile) {
|
||||
ASSERT_TRUE(data.empty());
|
||||
}
|
||||
|
||||
TEST_F(DBEncryptionTest, NotSupportedGetFileSize) {
|
||||
// Validate envrypted env does not support GetFileSize.
|
||||
// The goal of the test is to validate the encrypted env/fs does not support
|
||||
// GetFileSize API on FSRandomAccessFile interface.
|
||||
// This test combined with the rest of the integration tests validate that
|
||||
// the new API GetFileSize on FSRandomAccessFile interface is not required to
|
||||
// be supported for database to work properly.
|
||||
// The GetFileSize API is used in ReadFooterFromFile() API to get the file
|
||||
// size. When GetFileSize API is not supported, the ReadFooterFromFile() API
|
||||
// will use FileSystem GetFileSize API as fallback. Refer to the
|
||||
// EncryptedRandomAccessFile class definition for more details.
|
||||
if (!encrypted_env_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto fs = encrypted_env_->GetFileSystem();
|
||||
|
||||
// create empty file for reading it back in later
|
||||
auto filePath = dbname_ + "/empty.empty";
|
||||
|
||||
// Create empty file
|
||||
CreateFile(fs.get(), filePath, "", false);
|
||||
|
||||
// Open it for reading footer
|
||||
std::unique_ptr<FSRandomAccessFile> randomAccessFile;
|
||||
auto status = fs->NewRandomAccessFile(filePath, FileOptions(),
|
||||
&randomAccessFile, nullptr);
|
||||
ASSERT_OK(status);
|
||||
|
||||
uint64_t fileSize;
|
||||
status = randomAccessFile->GetFileSize(&fileSize);
|
||||
ASSERT_TRUE(status.IsNotSupported());
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -3504,6 +3504,63 @@ TEST_F(DBFlushTest, DBStuckAfterAtomicFlushError) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, VerifyOutputRecordCount) {
|
||||
for (bool use_plain_table : {false, true}) {
|
||||
Options options = CurrentOptions();
|
||||
options.flush_verify_memtable_count = true;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
DestroyAndReopen(options);
|
||||
// Verify flush output record count verification in different table
|
||||
// formats
|
||||
if (use_plain_table) {
|
||||
options.table_factory.reset(NewPlainTableFactory());
|
||||
}
|
||||
|
||||
// Verify that flush output record count verification does not produce false
|
||||
// positives.
|
||||
ASSERT_OK(Merge("k0", "v1"));
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Put("k2", "v1"));
|
||||
ASSERT_OK(SingleDelete("k2"));
|
||||
ASSERT_OK(Delete("k2"));
|
||||
ASSERT_OK(Delete("k3"));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), "k1", "k3"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Verify that flush output record count verification catch corruption
|
||||
DestroyAndReopen(options);
|
||||
if (use_plain_table) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"PlainTableBuilder::Add::skip",
|
||||
[&](void* skip) { *(bool*)skip = true; });
|
||||
|
||||
} else {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::Add::skip",
|
||||
[&](void* skip) { *(bool*)skip = true; });
|
||||
}
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
const char* expect =
|
||||
"Number of keys in flush output SST files does not match";
|
||||
|
||||
// 1. During DB open flush
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Put("k2", "v1"));
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), expect));
|
||||
|
||||
// 2. During regular flush
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Put("k2", "v1"));
|
||||
s = Flush();
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), expect));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+62
-101
@@ -253,6 +253,9 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
periodic_task_functions_.emplace(
|
||||
PeriodicTaskType::kRecordSeqnoTime,
|
||||
[this]() { this->RecordSeqnoToTimeMapping(); });
|
||||
periodic_task_functions_.emplace(
|
||||
PeriodicTaskType::kTriggerCompaction,
|
||||
[this]() { this->TriggerPeriodicCompaction(); });
|
||||
|
||||
versions_.reset(new VersionSet(
|
||||
dbname_, &immutable_db_options_, file_options_, table_cache_.get(),
|
||||
@@ -787,7 +790,8 @@ Status DBImpl::StartPeriodicTaskScheduler() {
|
||||
Status s = periodic_task_scheduler_.Register(
|
||||
PeriodicTaskType::kDumpStats,
|
||||
periodic_task_functions_.at(PeriodicTaskType::kDumpStats),
|
||||
mutable_db_options_.stats_dump_period_sec);
|
||||
mutable_db_options_.stats_dump_period_sec,
|
||||
/*run_immediately=*/true);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -796,7 +800,8 @@ Status DBImpl::StartPeriodicTaskScheduler() {
|
||||
Status s = periodic_task_scheduler_.Register(
|
||||
PeriodicTaskType::kPersistStats,
|
||||
periodic_task_functions_.at(PeriodicTaskType::kPersistStats),
|
||||
mutable_db_options_.stats_persist_period_sec);
|
||||
mutable_db_options_.stats_persist_period_sec,
|
||||
/*run_immediately=*/true);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -804,7 +809,15 @@ Status DBImpl::StartPeriodicTaskScheduler() {
|
||||
|
||||
Status s = periodic_task_scheduler_.Register(
|
||||
PeriodicTaskType::kFlushInfoLog,
|
||||
periodic_task_functions_.at(PeriodicTaskType::kFlushInfoLog));
|
||||
periodic_task_functions_.at(PeriodicTaskType::kFlushInfoLog),
|
||||
/*run_immediately=*/true);
|
||||
|
||||
if (s.ok()) {
|
||||
s = periodic_task_scheduler_.Register(
|
||||
PeriodicTaskType::kTriggerCompaction,
|
||||
periodic_task_functions_.at(PeriodicTaskType::kTriggerCompaction),
|
||||
/*run_immediately=*/false);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -855,7 +868,7 @@ Status DBImpl::RegisterRecordSeqnoTimeWorker() {
|
||||
s = periodic_task_scheduler_.Register(
|
||||
PeriodicTaskType::kRecordSeqnoTime,
|
||||
periodic_task_functions_.at(PeriodicTaskType::kRecordSeqnoTime),
|
||||
seqno_time_cadence);
|
||||
seqno_time_cadence, /*run_immediately=*/true);
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -1365,7 +1378,7 @@ Status DBImpl::SetDBOptions(
|
||||
s = periodic_task_scheduler_.Register(
|
||||
PeriodicTaskType::kDumpStats,
|
||||
periodic_task_functions_.at(PeriodicTaskType::kDumpStats),
|
||||
new_options.stats_dump_period_sec);
|
||||
new_options.stats_dump_period_sec, /*run_immediately=*/true);
|
||||
}
|
||||
if (new_options.max_total_wal_size !=
|
||||
mutable_db_options_.max_total_wal_size) {
|
||||
@@ -1380,7 +1393,7 @@ Status DBImpl::SetDBOptions(
|
||||
s = periodic_task_scheduler_.Register(
|
||||
PeriodicTaskType::kPersistStats,
|
||||
periodic_task_functions_.at(PeriodicTaskType::kPersistStats),
|
||||
new_options.stats_persist_period_sec);
|
||||
new_options.stats_persist_period_sec, /*run_immediately=*/true);
|
||||
}
|
||||
}
|
||||
mutex_.Lock();
|
||||
@@ -3823,10 +3836,8 @@ bool DBImpl::KeyMayExist(const ReadOptions& read_options,
|
||||
std::unique_ptr<MultiScan> DBImpl::NewMultiScan(
|
||||
const ReadOptions& _read_options, ColumnFamilyHandle* column_family,
|
||||
const std::vector<ScanOptions>& scan_opts) {
|
||||
std::unique_ptr<Iterator> iter(NewIterator(_read_options, column_family));
|
||||
iter->Prepare(scan_opts);
|
||||
std::unique_ptr<MultiScan> ms_iter =
|
||||
std::make_unique<MultiScan>(scan_opts, std::move(iter));
|
||||
std::unique_ptr<MultiScan> ms_iter = std::make_unique<MultiScan>(
|
||||
_read_options, scan_opts, this, column_family);
|
||||
return ms_iter;
|
||||
}
|
||||
|
||||
@@ -3886,12 +3897,14 @@ Iterator* DBImpl::NewIterator(const ReadOptions& _read_options,
|
||||
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv,
|
||||
/* allow_unprepared_value */ true);
|
||||
// TODO(cbi): Add support for `memtable_op_scan_flush_trigger` for tailing
|
||||
// iterator. This requires refreshing DBIter's pointer to active_mem when
|
||||
// tailing iterator refreshes to new memtable internally.
|
||||
result = DBIter::NewIter(env_, read_options, cfd->ioptions(),
|
||||
sv->mutable_cf_options, cfd->user_comparator(),
|
||||
iter, sv->current, kMaxSequenceNumber,
|
||||
/*read_callback=*/nullptr, cfh,
|
||||
/*expose_blob_index=*/false,
|
||||
/*active_mem=*/sv->mem);
|
||||
/*read_callback=*/nullptr, /*active_mem=*/nullptr,
|
||||
cfh, /*expose_blob_index=*/false);
|
||||
} else {
|
||||
// Note: no need to consider the special case of
|
||||
// last_seq_same_as_publish_seq_==false since NewIterator is overridden in
|
||||
@@ -4095,12 +4108,12 @@ Status DBImpl::NewIterators(
|
||||
auto iter = new ForwardIterator(this, read_options, cf_sv_pair.cfd,
|
||||
cf_sv_pair.super_version,
|
||||
/* allow_unprepared_value */ true);
|
||||
iterators->push_back(
|
||||
DBIter::NewIter(env_, read_options, cf_sv_pair.cfd->ioptions(),
|
||||
cf_sv_pair.super_version->mutable_cf_options,
|
||||
cf_sv_pair.cfd->user_comparator(), iter,
|
||||
cf_sv_pair.super_version->current, kMaxSequenceNumber,
|
||||
nullptr /*read_callback*/, cf_sv_pair.cfh));
|
||||
iterators->push_back(DBIter::NewIter(
|
||||
env_, read_options, cf_sv_pair.cfd->ioptions(),
|
||||
cf_sv_pair.super_version->mutable_cf_options,
|
||||
cf_sv_pair.cfd->user_comparator(), iter,
|
||||
cf_sv_pair.super_version->current, kMaxSequenceNumber,
|
||||
nullptr /*read_callback*/, /*active_mem=*/nullptr, cf_sv_pair.cfh));
|
||||
}
|
||||
} else {
|
||||
for (const auto& cf_sv_pair : cf_sv_pairs) {
|
||||
@@ -5016,7 +5029,6 @@ void DBImpl::GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
|
||||
assert(column_family);
|
||||
auto* cfd =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(column_family)->cfd();
|
||||
auto* sv = GetAndRefSuperVersion(cfd);
|
||||
{
|
||||
// Without mutex, Version::GetColumnFamilyMetaData will have data race
|
||||
// with Compaction::MarkFilesBeingCompacted. One solution is to use mutex,
|
||||
@@ -5028,9 +5040,8 @@ void DBImpl::GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
|
||||
// DB::GetColumnFamilyMetaData is not called frequently, the regression
|
||||
// should not be big. We still need to keep an eye on it.
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
sv->current->GetColumnFamilyMetaData(cf_meta);
|
||||
cfd->current()->GetColumnFamilyMetaData(cf_meta);
|
||||
}
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
}
|
||||
|
||||
void DBImpl::GetAllColumnFamilyMetaData(
|
||||
@@ -5044,85 +5055,6 @@ void DBImpl::GetAllColumnFamilyMetaData(
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::CheckConsistency() {
|
||||
mutex_.AssertHeld();
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
versions_->GetLiveFilesMetaData(&metadata);
|
||||
TEST_SYNC_POINT("DBImpl::CheckConsistency:AfterGetLiveFilesMetaData");
|
||||
|
||||
std::string corruption_messages;
|
||||
|
||||
if (immutable_db_options_.skip_checking_sst_file_sizes_on_db_open) {
|
||||
// Instead of calling GetFileSize() for each expected file, call
|
||||
// GetChildren() for the DB directory and check that all expected files
|
||||
// are listed, without checking their sizes.
|
||||
// Since sst files might be in different directories, do it for each
|
||||
// directory separately.
|
||||
std::map<std::string, std::vector<std::string>> files_by_directory;
|
||||
for (const auto& md : metadata) {
|
||||
// md.name has a leading "/". Remove it.
|
||||
std::string fname = md.name;
|
||||
if (!fname.empty() && fname[0] == '/') {
|
||||
fname = fname.substr(1);
|
||||
}
|
||||
files_by_directory[md.db_path].push_back(fname);
|
||||
}
|
||||
|
||||
IOOptions io_opts;
|
||||
io_opts.do_not_recurse = true;
|
||||
for (const auto& dir_files : files_by_directory) {
|
||||
std::string directory = dir_files.first;
|
||||
std::vector<std::string> existing_files;
|
||||
Status s = fs_->GetChildren(directory, io_opts, &existing_files,
|
||||
/*IODebugContext*=*/nullptr);
|
||||
if (!s.ok()) {
|
||||
corruption_messages +=
|
||||
"Can't list files in " + directory + ": " + s.ToString() + "\n";
|
||||
continue;
|
||||
}
|
||||
std::sort(existing_files.begin(), existing_files.end());
|
||||
|
||||
for (const std::string& fname : dir_files.second) {
|
||||
if (!std::binary_search(existing_files.begin(), existing_files.end(),
|
||||
fname) &&
|
||||
!std::binary_search(existing_files.begin(), existing_files.end(),
|
||||
Rocks2LevelTableFileName(fname))) {
|
||||
corruption_messages +=
|
||||
"Missing sst file " + fname + " in " + directory + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto& md : metadata) {
|
||||
// md.name has a leading "/".
|
||||
std::string file_path = md.db_path + md.name;
|
||||
|
||||
uint64_t fsize = 0;
|
||||
TEST_SYNC_POINT("DBImpl::CheckConsistency:BeforeGetFileSize");
|
||||
Status s = env_->GetFileSize(file_path, &fsize);
|
||||
if (!s.ok() &&
|
||||
env_->GetFileSize(Rocks2LevelTableFileName(file_path), &fsize).ok()) {
|
||||
s = Status::OK();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
corruption_messages +=
|
||||
"Can't access " + md.name + ": " + s.ToString() + "\n";
|
||||
} else if (fsize != md.size) {
|
||||
corruption_messages += "Sst file size mismatch: " + file_path +
|
||||
". Size recorded in manifest " +
|
||||
std::to_string(md.size) + ", actual size " +
|
||||
std::to_string(fsize) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (corruption_messages.size() == 0) {
|
||||
return Status::OK();
|
||||
} else {
|
||||
return Status::Corruption(corruption_messages);
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::GetDbIdentity(std::string& identity) const {
|
||||
identity.assign(db_id_);
|
||||
return Status::OK();
|
||||
@@ -6884,6 +6816,35 @@ void DBImpl::RecordSeqnoToTimeMapping() {
|
||||
sv_context.Clean();
|
||||
}
|
||||
|
||||
void DBImpl::TriggerPeriodicCompaction() {
|
||||
TEST_SYNC_POINT("DBImpl::TriggerPeriodicCompaction:StartRunning");
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Running the periodic task to trigger compactions.");
|
||||
|
||||
for (ColumnFamilyData* cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
if (cfd->GetLatestCFOptions().periodic_compaction_seconds &&
|
||||
!cfd->queued_for_compaction()) {
|
||||
cfd->current()->storage_info()->ComputeCompactionScore(
|
||||
cfd->ioptions(), cfd->GetLatestMutableCFOptions());
|
||||
EnqueuePendingCompaction(cfd);
|
||||
if (cfd->queued_for_compaction()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Periodic task to trigger compaction queued Column "
|
||||
"family [%s] for compaction.",
|
||||
cfd->GetName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::TrackOrUntrackFiles(
|
||||
const std::vector<std::string>& existing_data_files, bool track) {
|
||||
auto sfm = static_cast_with_check<SstFileManagerImpl>(
|
||||
|
||||
+29
-17
@@ -804,10 +804,6 @@ class DBImpl : public DB {
|
||||
// being detected.
|
||||
const Snapshot* GetSnapshotForWriteConflictBoundary();
|
||||
|
||||
// checks if all live files exist on file system and that their file sizes
|
||||
// match to our in-memory records
|
||||
virtual Status CheckConsistency();
|
||||
|
||||
// max_file_num_to_ignore allows bottom level compaction to filter out newly
|
||||
// compacted SST files. Setting max_file_num_to_ignore to kMaxUint64 will
|
||||
// disable the filtering
|
||||
@@ -1090,10 +1086,7 @@ class DBImpl : public DB {
|
||||
void SetSnapshotChecker(SnapshotChecker* snapshot_checker);
|
||||
|
||||
// Fill JobContext with snapshot information needed by flush and compaction.
|
||||
void GetSnapshotContext(JobContext* job_context,
|
||||
std::vector<SequenceNumber>* snapshot_seqs,
|
||||
SequenceNumber* earliest_write_conflict_snapshot,
|
||||
SnapshotChecker** snapshot_checker);
|
||||
void InitSnapshotContext(JobContext* job_context);
|
||||
|
||||
// Not thread-safe.
|
||||
void SetRecoverableStatePreReleaseCallback(PreReleaseCallback* callback);
|
||||
@@ -1289,6 +1282,12 @@ class DBImpl : public DB {
|
||||
// For the background timer job
|
||||
void RecordSeqnoToTimeMapping();
|
||||
|
||||
// Compactions rely on an event triggers like flush/compaction/SetOptions.
|
||||
// We need to trigger periodic compactions even when there is no such trigger.
|
||||
// This function checks and schedules available compactions and will run
|
||||
// periodically.
|
||||
void TriggerPeriodicCompaction();
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
std::pair<SequenceNumber, uint64_t> GetSeqnoToTimeSample() const;
|
||||
|
||||
@@ -1955,12 +1954,19 @@ class DBImpl : public DB {
|
||||
};
|
||||
struct PrepickedCompaction {
|
||||
// background compaction takes ownership of `compaction`.
|
||||
// TODO(hx235): consider using std::shared_ptr for easier ownership
|
||||
// management
|
||||
Compaction* compaction;
|
||||
// caller retains ownership of `manual_compaction_state` as it is reused
|
||||
// across background compactions.
|
||||
ManualCompactionState* manual_compaction_state; // nullptr if non-manual
|
||||
// task limiter token is requested during compaction picking.
|
||||
std::unique_ptr<TaskLimiterToken> task_token;
|
||||
// If true, `compaction` is picked temporarily to express compaction intent
|
||||
// and will be released before re-picking a real compaction based on the
|
||||
// updated LSM shape when thread associated with `compaction` is ready to
|
||||
// run
|
||||
bool need_repick;
|
||||
};
|
||||
|
||||
struct CompactionArg {
|
||||
@@ -2051,14 +2057,13 @@ class DBImpl : public DB {
|
||||
// Flush the in-memory write buffer to storage. Switches to a new
|
||||
// log-file/memtable and writes a new descriptor iff successful. Then
|
||||
// installs a new super version for the column family.
|
||||
Status FlushMemTableToOutputFile(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
bool* madeProgress, JobContext* job_context, FlushReason flush_reason,
|
||||
SuperVersionContext* superversion_context,
|
||||
std::vector<SequenceNumber>& snapshot_seqs,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, LogBuffer* log_buffer,
|
||||
Env::Priority thread_pri);
|
||||
Status FlushMemTableToOutputFile(ColumnFamilyData* cfd,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
bool* madeProgress, JobContext* job_context,
|
||||
FlushReason flush_reason,
|
||||
SuperVersionContext* superversion_context,
|
||||
LogBuffer* log_buffer,
|
||||
Env::Priority thread_pri);
|
||||
|
||||
// Flush the memtables of (multiple) column families to multiple files on
|
||||
// persistent storage.
|
||||
@@ -2460,6 +2465,8 @@ class DBImpl : public DB {
|
||||
bool* flush_rescheduled_to_retain_udt,
|
||||
Env::Priority thread_pri);
|
||||
|
||||
Compaction* CreateIntendedCompactionForwardedToBottomPriorityPool(
|
||||
Compaction* c);
|
||||
bool EnoughRoomForCompaction(ColumnFamilyData* cfd,
|
||||
const std::vector<CompactionInputFiles>& inputs,
|
||||
bool* sfm_bookkeeping, LogBuffer* log_buffer);
|
||||
@@ -2577,7 +2584,7 @@ class DBImpl : public DB {
|
||||
bool ShouldntRunManualCompaction(ManualCompactionState* m);
|
||||
bool HaveManualCompaction(ColumnFamilyData* cfd);
|
||||
bool MCOverlap(ManualCompactionState* m, ManualCompactionState* m1);
|
||||
void UpdateDeletionCompactionStats(const std::unique_ptr<Compaction>& c);
|
||||
void UpdateFIFOCompactionStatus(const std::unique_ptr<Compaction>& c);
|
||||
|
||||
// May open and read table files for table property.
|
||||
// Should not be called while holding mutex_.
|
||||
@@ -2727,6 +2734,11 @@ class DBImpl : public DB {
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
ErrorIteratorFuncType error_iterator_func);
|
||||
|
||||
bool ShouldPickCompaction(bool is_prepicked,
|
||||
const PrepickedCompaction* prepicked_compaction);
|
||||
|
||||
void ResetBottomPriCompactionIntent(ColumnFamilyData* cfd,
|
||||
std::unique_ptr<Compaction>& c);
|
||||
// Lock over the persistent DB state. Non-nullptr iff successfully acquired.
|
||||
FileLock* db_lock_ = nullptr;
|
||||
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "file/file_util.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "logging/logging.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_updater.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/io_status.h"
|
||||
#include "rocksdb/options.h"
|
||||
@@ -143,10 +145,7 @@ IOStatus DBImpl::SyncClosedWals(const WriteOptions& write_options,
|
||||
Status DBImpl::FlushMemTableToOutputFile(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
bool* made_progress, JobContext* job_context, FlushReason flush_reason,
|
||||
SuperVersionContext* superversion_context,
|
||||
std::vector<SequenceNumber>& snapshot_seqs,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, LogBuffer* log_buffer,
|
||||
SuperVersionContext* superversion_context, LogBuffer* log_buffer,
|
||||
Env::Priority thread_pri) {
|
||||
mutex_.AssertHeld();
|
||||
assert(cfd);
|
||||
@@ -210,7 +209,6 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
FlushJob flush_job(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options, max_memtable_id,
|
||||
file_options_for_compaction_, versions_.get(), &mutex_, &shutting_down_,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
job_context, flush_reason, log_buffer, directories_.GetDbDir(),
|
||||
GetDataDir(cfd, 0U),
|
||||
GetCompressionFlush(cfd->ioptions(), mutable_cf_options), stats_,
|
||||
@@ -395,11 +393,8 @@ Status DBImpl::FlushMemTablesToOutputFiles(
|
||||
bg_flush_args, made_progress, job_context, log_buffer, thread_pri);
|
||||
}
|
||||
assert(bg_flush_args.size() == 1);
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
SnapshotChecker* snapshot_checker;
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
InitSnapshotContext(job_context);
|
||||
|
||||
const auto& bg_flush_arg = bg_flush_args[0];
|
||||
ColumnFamilyData* cfd = bg_flush_arg.cfd_;
|
||||
// intentional infrequent copy for each flush
|
||||
@@ -410,8 +405,7 @@ Status DBImpl::FlushMemTablesToOutputFiles(
|
||||
FlushReason flush_reason = bg_flush_arg.flush_reason_;
|
||||
Status s = FlushMemTableToOutputFile(
|
||||
cfd, mutable_cf_options_copy, made_progress, job_context, flush_reason,
|
||||
superversion_context, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, log_buffer, thread_pri);
|
||||
superversion_context, log_buffer, thread_pri);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -446,12 +440,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
}
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
SnapshotChecker* snapshot_checker;
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
|
||||
InitSnapshotContext(job_context);
|
||||
autovector<FSDirectory*> distinct_output_dirs;
|
||||
autovector<std::string> distinct_output_dir_paths;
|
||||
std::vector<std::unique_ptr<FlushJob>> jobs;
|
||||
@@ -485,8 +474,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
jobs.emplace_back(new FlushJob(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||
max_memtable_id, file_options_for_compaction_, versions_.get(), &mutex_,
|
||||
&shutting_down_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, job_context, flush_reason, log_buffer,
|
||||
&shutting_down_, job_context, flush_reason, log_buffer,
|
||||
directories_.GetDbDir(), data_dir,
|
||||
GetCompressionFlush(cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
@@ -1393,6 +1381,9 @@ Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
|
||||
TEST_SYNC_POINT_CALLBACK("TestCompactFiles:PausingManualCompaction:3",
|
||||
static_cast<void*>(const_cast<std::atomic<int>*>(
|
||||
&manual_compaction_paused_)));
|
||||
TEST_SYNC_POINT_CALLBACK("TestCancelCompactFiles:SuccessfulCompaction",
|
||||
static_cast<void*>(const_cast<std::atomic<int>*>(
|
||||
&manual_compaction_paused_)));
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
auto* current = cfd->current();
|
||||
@@ -1445,7 +1436,12 @@ Status DBImpl::CompactFilesImpl(
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
}
|
||||
if (manual_compaction_paused_.load(std::memory_order_acquire) > 0) {
|
||||
|
||||
// triggered by DisableManualCompactions or by user-set canceled flag in
|
||||
// CompactionOptions
|
||||
if (manual_compaction_paused_.load(std::memory_order_acquire) > 0 ||
|
||||
(compact_options.canceled &&
|
||||
compact_options.canceled->load(std::memory_order_acquire))) {
|
||||
return Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
|
||||
@@ -1516,11 +1512,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
// deletion compaction currently not allowed in CompactFiles.
|
||||
assert(!c->deletion_compaction());
|
||||
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
SnapshotChecker* snapshot_checker;
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
InitSnapshotContext(job_context);
|
||||
|
||||
std::unique_ptr<std::list<uint64_t>::iterator> pending_outputs_inserted_elem(
|
||||
new std::list<uint64_t>::iterator(
|
||||
@@ -1534,7 +1526,6 @@ Status DBImpl::CompactFilesImpl(
|
||||
log_buffer, directories_.GetDbDir(),
|
||||
GetDataDir(c->column_family_data(), c->output_path_id()),
|
||||
GetDataDir(c->column_family_data(), 0), stats_, &mutex_, &error_handler_,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
job_context, table_cache_, &event_logger_,
|
||||
c->mutable_cf_options().paranoid_file_checks,
|
||||
c->mutable_cf_options().report_bg_io_stats, dbname_,
|
||||
@@ -1858,10 +1849,9 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
0 /* max_subcompactions, not applicable */,
|
||||
{} /* grandparents, not applicable */,
|
||||
std::nullopt /* earliest_snapshot */, nullptr /* snapshot_checker */,
|
||||
false /* is manual */, "" /* trim_ts */, -1 /* score, not applicable */,
|
||||
false /* is deletion compaction, not applicable */,
|
||||
false /* l0_files_might_overlap, not applicable */,
|
||||
CompactionReason::kRefitLevel));
|
||||
CompactionReason::kRefitLevel, "" /* trim_ts */,
|
||||
-1 /* score, not applicable */,
|
||||
false /* l0_files_might_overlap, not applicable */));
|
||||
cfd->compaction_picker()->RegisterCompaction(c.get());
|
||||
TEST_SYNC_POINT("DBImpl::ReFitLevel:PostRegisterCompaction");
|
||||
VersionEdit edit;
|
||||
@@ -2187,6 +2177,7 @@ Status DBImpl::RunManualCompaction(
|
||||
// Don't throttle manual compaction, only count outstanding tasks.
|
||||
assert(false);
|
||||
}
|
||||
ca->prepicked_compaction->need_repick = false;
|
||||
manual.incomplete = false;
|
||||
if (compaction->bottommost_level() &&
|
||||
env_->GetBackgroundThreads(Env::Priority::BOTTOM) > 0) {
|
||||
@@ -2716,6 +2707,10 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
// Finish waiting when ALL column families finish flushing memtables.
|
||||
// resuming_from_bg_err indicates whether the caller is trying to resume from
|
||||
// background error or in normal processing.
|
||||
// Note that the wait finishes when the flush result is installed to column
|
||||
// families' Versions and persisted in MANIFEST. It doesn't wait until
|
||||
// SuperVersion to reflect the flush result, except for the case when
|
||||
// flush_reason is `kExternalFileIngestion`.
|
||||
Status DBImpl::WaitForFlushMemTables(
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const uint64_t*>& flush_memtable_ids,
|
||||
@@ -3409,6 +3404,10 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
bool made_progress = false;
|
||||
JobContext job_context(next_job_id_.fetch_add(1), true);
|
||||
TEST_SYNC_POINT("BackgroundCallCompaction:0");
|
||||
if (bg_thread_pri == Env::Priority::BOTTOM) {
|
||||
TEST_SYNC_POINT("BackgroundCallCompaction:0:BottomPri");
|
||||
}
|
||||
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL,
|
||||
immutable_db_options_.info_log.get());
|
||||
{
|
||||
@@ -3644,34 +3643,54 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
: m->manual_end->DebugString(true).c_str()));
|
||||
}
|
||||
}
|
||||
} else if (!is_prepicked && !compaction_queue_.empty()) {
|
||||
} else if (ShouldPickCompaction(is_prepicked, prepicked_compaction)) {
|
||||
bool need_repick = is_prepicked && prepicked_compaction->need_repick;
|
||||
if (HasExclusiveManualCompaction()) {
|
||||
// Can't compact right now, but try again later
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction()::Conflict");
|
||||
|
||||
// Stay in the compaction queue.
|
||||
unscheduled_compactions_++;
|
||||
// TODO(hx235): Resolve conflict between intended
|
||||
// bottom-priority compaction (requiring repick, i.e., need_repick = true)
|
||||
// and exclusive manual compaction by releasing the intended
|
||||
// bottom-priority compaction.
|
||||
if (!need_repick) {
|
||||
// Can't compact right now, but try again later
|
||||
//
|
||||
// Increase `unscheduled_compactions_` directly so we
|
||||
// don't need to
|
||||
// dequeue and enqueue the CFD again in the compaction queue and thus
|
||||
// keep the CFD's position in the queue
|
||||
unscheduled_compactions_++;
|
||||
|
||||
return Status::OK();
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
auto cfd = PickCompactionFromQueue(&task_token, log_buffer);
|
||||
if (cfd == nullptr) {
|
||||
// Can't find any executable task from the compaction queue.
|
||||
// All tasks have been throttled by compaction thread limiter.
|
||||
++unscheduled_compactions_;
|
||||
return Status::Busy();
|
||||
}
|
||||
ColumnFamilyData* cfd = nullptr;
|
||||
|
||||
// We unreference here because the following code will take a Ref() on
|
||||
// this cfd if it is going to use it (Compaction class holds a
|
||||
// reference).
|
||||
// This will all happen under a mutex so we don't have to be afraid of
|
||||
// somebody else deleting it.
|
||||
if (cfd->UnrefAndTryDelete()) {
|
||||
// This was the last reference of the column family, so no need to
|
||||
// compact.
|
||||
return Status::OK();
|
||||
if (!need_repick) {
|
||||
cfd = PickCompactionFromQueue(&task_token, log_buffer);
|
||||
if (cfd == nullptr) {
|
||||
// Can't find any executable task from the compaction queue.
|
||||
// All tasks have been throttled by compaction thread limiter.
|
||||
++unscheduled_compactions_;
|
||||
return Status::Busy();
|
||||
}
|
||||
|
||||
// We unreference here because the following code will take a Ref() on
|
||||
// this cfd if it is going to use it (Compaction class holds a
|
||||
// reference).
|
||||
// This will all happen under a mutex so we don't have to be afraid of
|
||||
// somebody else deleting it.
|
||||
if (cfd->UnrefAndTryDelete()) {
|
||||
// This was the last reference of the column family, so no need to
|
||||
// compact.
|
||||
return Status::OK();
|
||||
}
|
||||
} else {
|
||||
cfd = c->column_family_data();
|
||||
assert(cfd);
|
||||
ResetBottomPriCompactionIntent(cfd, c);
|
||||
assert(c == nullptr);
|
||||
}
|
||||
|
||||
// Pick up latest mutable CF Options and use it throughout the
|
||||
@@ -3685,21 +3704,24 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
// compaction is not necessary. Need to make sure mutex is held
|
||||
// until we make a copy in the following code
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction():BeforePickCompaction");
|
||||
SnapshotChecker* snapshot_checker = nullptr;
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
// This info is not useful for other scenarios, so save querying existing
|
||||
// snapshots for those cases.
|
||||
if (cfd->ioptions().compaction_style == kCompactionStyleUniversal &&
|
||||
cfd->user_comparator()->timestamp_size() == 0) {
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot,
|
||||
&snapshot_checker);
|
||||
InitSnapshotContext(job_context);
|
||||
assert(is_snapshot_supported_ || snapshots_.empty());
|
||||
}
|
||||
c.reset(cfd->PickCompaction(mutable_cf_options, mutable_db_options_,
|
||||
snapshot_seqs, snapshot_checker, log_buffer));
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction():AfterPickCompaction");
|
||||
c.reset(cfd->PickCompaction(
|
||||
mutable_cf_options, mutable_db_options_, job_context->snapshot_seqs,
|
||||
job_context->snapshot_checker, log_buffer,
|
||||
thread_pri == Env::Priority::BOTTOM /* require_max_output_level */));
|
||||
if (thread_pri == Env::Priority::LOW) {
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction():AfterPickCompaction");
|
||||
} else if (thread_pri == Env::Priority::BOTTOM) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::BackgroundCompaction():AfterPickCompactionBottomPri",
|
||||
c.get());
|
||||
}
|
||||
|
||||
if (c != nullptr) {
|
||||
bool enough_room = EnoughRoomForCompaction(
|
||||
@@ -3713,7 +3735,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
->storage_info()
|
||||
->ComputeCompactionScore(c->immutable_options(),
|
||||
c->mutable_cf_options());
|
||||
AddToCompactionQueue(cfd);
|
||||
EnqueuePendingCompaction(cfd);
|
||||
|
||||
c.reset();
|
||||
// Don't need to sleep here, because BackgroundCallCompaction
|
||||
@@ -3735,16 +3757,21 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
// options take effect.
|
||||
// 3) When we Pick a new compaction, we "remove" those files being
|
||||
// compacted from the calculation, which then influences compaction
|
||||
// score. Here we check if we need the new compaction even without the
|
||||
// files that are currently being compacted. If we need another
|
||||
// compaction, we might be able to execute it in parallel, so we add
|
||||
// it to the queue and schedule a new thread.
|
||||
if (cfd->NeedsCompaction()) {
|
||||
// Yes, we need more compactions!
|
||||
AddToCompactionQueue(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
// score. Inside EnqueuePendingCompaction(), we check if we need
|
||||
// the new compaction even without the files that are currently being
|
||||
// compacted. If we need another compaction, we might be able to
|
||||
// execute it in parallel, so we add it to the queue and schedule a
|
||||
// new thread.
|
||||
EnqueuePendingCompaction(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
} else if (is_prepicked) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] Pre-picked compaction repicked files for compaction as "
|
||||
"required, "
|
||||
"but upon re-evaluation, no compaction was found necessary \n",
|
||||
cfd->GetName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3786,11 +3813,251 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
c->num_input_files(0));
|
||||
if (status.ok() && io_s.ok()) {
|
||||
UpdateDeletionCompactionStats(c);
|
||||
UpdateFIFOCompactionStatus(c);
|
||||
}
|
||||
*made_progress = true;
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
c->column_family_data());
|
||||
} else if (c->is_trivial_copy_compaction()) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::BackgroundCompaction:TriviaCopyBeforeCompaction",
|
||||
c->column_family_data());
|
||||
assert(c->num_input_files(1) == 0);
|
||||
assert(c->column_family_data()->ioptions().compaction_style ==
|
||||
kCompactionStyleFIFO);
|
||||
assert(c->compaction_reason() == CompactionReason::kChangeTemperature);
|
||||
|
||||
compaction_job_stats.num_input_files = c->num_input_files(0);
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
compaction_job_stats, job_context->job_id);
|
||||
|
||||
std::vector<FileMetaData> out_files;
|
||||
for (const auto& in_file : *c->inputs(0)) {
|
||||
const uint64_t out_file_number = versions_->NewFileNumber();
|
||||
const std::string in_fname =
|
||||
TableFileName(c->immutable_options().cf_paths,
|
||||
in_file->fd.GetNumber(), in_file->fd.GetPathId());
|
||||
const std::string out_fname =
|
||||
TableFileName(c->immutable_options().cf_paths, out_file_number,
|
||||
c->output_path_id());
|
||||
|
||||
// TODO (mikechuang): Currently skip calling
|
||||
// EventHelpers::NotifyTableFileCreationStarted for the trivial copy.
|
||||
// Since it's a trivial copy we should ideally use the exact
|
||||
// TableProperties from the input file but that will break some existing
|
||||
// stress tests. For now skip the listener call for the FIFO
|
||||
// kChangeTemperature trivial copy move.
|
||||
|
||||
int64_t tmp_current_time = 0;
|
||||
auto get_time_status =
|
||||
immutable_db_options_.clock->GetCurrentTime(&tmp_current_time);
|
||||
if (!get_time_status.ok()) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] WARNING: Failed to get current time %s "
|
||||
"status=%s",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
get_time_status.ToString().c_str());
|
||||
}
|
||||
uint64_t out_file_creation_time = static_cast<uint64_t>(tmp_current_time);
|
||||
|
||||
FileOptions copied_file_options = file_options_;
|
||||
copied_file_options.temperature = c->output_temperature();
|
||||
std::unique_ptr<WritableFileWriter> dest_writer;
|
||||
{
|
||||
std::unique_ptr<FSWritableFile> dest_file;
|
||||
IOStatus writable_file_io_status =
|
||||
immutable_db_options_.fs.get()->NewWritableFile(
|
||||
out_fname, copied_file_options, &dest_file, nullptr /* dbg */);
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"NewWritableFile::FileOptions.temperature",
|
||||
const_cast<Temperature*>(&copied_file_options.temperature));
|
||||
if (!writable_file_io_status.ok()) {
|
||||
io_s = writable_file_io_status;
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] Error: Abort trivial copy compaction, failed to open "
|
||||
"NewWritableFile %s\n"
|
||||
" out_fname=%s, temperature=%s, io_status=%s",
|
||||
c->column_family_data()->GetName().c_str(), out_fname.c_str(),
|
||||
temperature_to_string[c->output_temperature()].c_str(),
|
||||
io_s.ToString().c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
FileTypeSet tmp_set = immutable_db_options_.checksum_handoff_file_types;
|
||||
dest_writer.reset(new WritableFileWriter(
|
||||
std::move(dest_file), out_fname, copied_file_options,
|
||||
immutable_db_options_.clock, io_tracer_,
|
||||
immutable_db_options_.stats, Histograms::SST_WRITE_MICROS,
|
||||
c->immutable_options().listeners,
|
||||
immutable_db_options_.file_checksum_gen_factory.get(),
|
||||
tmp_set.Contains(FileType::kTableFile), false));
|
||||
}
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] Started copying from: %s\n"
|
||||
" temperature=%s, to: %s, temperature=%s, buffer_size=%" PRIu64,
|
||||
c->column_family_data()->GetName().c_str(), in_fname.c_str(),
|
||||
temperature_to_string[in_file->temperature].c_str(),
|
||||
out_fname.c_str(),
|
||||
temperature_to_string[c->output_temperature()].c_str(),
|
||||
c->mutable_cf_options()
|
||||
.compaction_options_fifo.trivial_copy_buffer_size);
|
||||
// Add IO_LOW HINT for compaction
|
||||
IOOptions copy_files_compaction_io_options;
|
||||
copy_files_compaction_io_options.rate_limiter_priority =
|
||||
Env::IOPriority::IO_LOW;
|
||||
copy_files_compaction_io_options.type = IOType::kData;
|
||||
copy_files_compaction_io_options.io_activity =
|
||||
Env::IOActivity::kCompaction;
|
||||
|
||||
IOStatus copy_file_io_status = CopyFile(
|
||||
immutable_db_options_.fs.get() /* fileSystem */,
|
||||
in_fname /* source */, in_file->temperature /* src_temp_hint */,
|
||||
dest_writer /* dest_writer */, 0 /* size */, true /* use_fsync */,
|
||||
io_tracer_ /* io_tracer*/,
|
||||
c->mutable_cf_options()
|
||||
.compaction_options_fifo
|
||||
.trivial_copy_buffer_size /* max_read_buffer_size
|
||||
*/
|
||||
,
|
||||
copy_files_compaction_io_options /* readIOOptions */,
|
||||
copy_files_compaction_io_options /* writeIOOptions */);
|
||||
if (dest_writer) {
|
||||
IOOptions close_files_compaction_io_options;
|
||||
close_files_compaction_io_options.rate_limiter_priority =
|
||||
Env::IOPriority::IO_LOW;
|
||||
close_files_compaction_io_options.type = IOType::kData;
|
||||
close_files_compaction_io_options.io_activity =
|
||||
Env::IOActivity::kCompaction;
|
||||
// Close the dest_write
|
||||
io_s = dest_writer->Close(close_files_compaction_io_options);
|
||||
if (!io_s.ok()) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] Failed to close the writer. Failed to copy from: %s\n"
|
||||
" temperature=%s, to=%s, temperature=%s, io_status=%s",
|
||||
c->column_family_data()->GetName().c_str(), in_fname.c_str(),
|
||||
temperature_to_string[in_file->temperature].c_str(),
|
||||
out_fname.c_str(),
|
||||
temperature_to_string[c->output_temperature()].c_str(),
|
||||
io_s.ToString().c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
io_s = copy_file_io_status;
|
||||
|
||||
if (!io_s.ok()) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Failed to copy from: %s\n"
|
||||
" temperature=%s, to=%s, temperature=%s, io_status=%s",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
in_fname.c_str(),
|
||||
temperature_to_string[in_file->temperature].c_str(),
|
||||
out_fname.c_str(),
|
||||
temperature_to_string[c->output_temperature()].c_str(),
|
||||
io_s.ToString().c_str());
|
||||
break;
|
||||
}
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Successfully copying from: %s\n"
|
||||
" temperature=%s, to=%s, temperature=%s, io_status=%s",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
in_fname.c_str(),
|
||||
temperature_to_string[in_file->temperature].c_str(),
|
||||
out_fname.c_str(),
|
||||
temperature_to_string[c->output_temperature()].c_str(),
|
||||
io_s.ToString().c_str());
|
||||
|
||||
FileMetaData out_file_metadata{
|
||||
out_file_number,
|
||||
c->output_path_id(),
|
||||
in_file->fd.GetFileSize(),
|
||||
in_file->smallest,
|
||||
in_file->largest,
|
||||
in_file->fd.smallest_seqno,
|
||||
in_file->fd.largest_seqno,
|
||||
false /* marked_for_compact */,
|
||||
c->output_temperature() /* temperature */,
|
||||
in_file->oldest_blob_file_number,
|
||||
in_file->oldest_ancester_time,
|
||||
out_file_creation_time,
|
||||
c->MinInputFileEpochNumber(),
|
||||
dest_writer->GetFileChecksum(),
|
||||
dest_writer->GetFileChecksumFuncName(),
|
||||
in_file->unique_id,
|
||||
in_file->compensated_range_deletion_size,
|
||||
in_file->tail_size,
|
||||
in_file->user_defined_timestamps_persisted};
|
||||
|
||||
out_files.push_back(std::move(out_file_metadata));
|
||||
}
|
||||
|
||||
// Update version set
|
||||
if (status.ok() && io_s.ok()) {
|
||||
// NOTE: ChangeTemperature should only copy one file at one file
|
||||
// hence *c->inputs(0) == out_files.size() == 1 if copy succeeded
|
||||
assert(c->inputs(0)->size() == 1);
|
||||
assert(out_files.size() == 1);
|
||||
|
||||
auto out_file_metadata_it = out_files.begin();
|
||||
for (const auto& in_file : *c->inputs(0)) {
|
||||
if (out_file_metadata_it == out_files.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
c->edit()->DeleteFile(c->level(), in_file->fd.GetNumber());
|
||||
c->edit()->AddFile(c->level(), *out_file_metadata_it);
|
||||
++out_file_metadata_it;
|
||||
}
|
||||
|
||||
status = versions_->LogAndApply(
|
||||
c->column_family_data(), read_options, write_options, c->edit(),
|
||||
&mutex_, directories_.GetDbDir(),
|
||||
/*new_descriptor_log=*/false, /*column_family_options=*/nullptr,
|
||||
[&c, &compaction_released](const Status& s) {
|
||||
c->ReleaseCompactionFiles(s);
|
||||
compaction_released = true;
|
||||
});
|
||||
}
|
||||
|
||||
// TODO (mikechuang): Currently skip calling
|
||||
// EventHelper::LogAndNotifyTableFileCreationFinished for the trivial copy.
|
||||
// Since it's a trivial copy we should ideally use the exact TableProperties
|
||||
// from the input file but that will break some existing stress tests. For
|
||||
// now skip the listener call for the FIFO kChangeTemperature trivial copy
|
||||
// move.
|
||||
|
||||
if (io_s.ok()) {
|
||||
io_s = versions_->io_status();
|
||||
}
|
||||
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data());
|
||||
if (status.ok() && io_s.ok()) {
|
||||
UpdateFIFOCompactionStatus(c);
|
||||
} else {
|
||||
for (const auto& in_file : *c->inputs(0)) {
|
||||
const std::string in_fname =
|
||||
TableFileName(c->immutable_options().cf_paths,
|
||||
in_file->fd.GetNumber(), in_file->fd.GetPathId());
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] Failed to do trvial copy compaction: %s"
|
||||
" temperature=%s, to temperature=%s, status=%s, io_status=%s",
|
||||
c->column_family_data()->GetName().c_str(), in_fname.c_str(),
|
||||
temperature_to_string[in_file->temperature].c_str(),
|
||||
temperature_to_string[c->output_temperature()].c_str(),
|
||||
status.ToString().c_str(), io_s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
*made_progress = true;
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::BackgroundCompaction:TriviaCopyAfterCompaction",
|
||||
c->column_family_data());
|
||||
} else if (!trivial_move_disallowed && c->IsTrivialMove()) {
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:TrivialMove");
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:BeforeCompaction",
|
||||
@@ -3881,14 +4148,16 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
c->column_family_data());
|
||||
} else if (!is_prepicked && c->output_level() > 0 &&
|
||||
c->output_level() ==
|
||||
} else if (!is_prepicked &&
|
||||
Compaction::OutputToNonZeroMaxOutputLevel(
|
||||
c->output_level(),
|
||||
c->column_family_data()
|
||||
->current()
|
||||
->storage_info()
|
||||
->MaxOutputLevel(
|
||||
immutable_db_options_.allow_ingest_behind) &&
|
||||
immutable_db_options_.allow_ingest_behind)) &&
|
||||
env_->GetBackgroundThreads(Env::Priority::BOTTOM) > 0) {
|
||||
assert(thread_pri == Env::Priority::LOW);
|
||||
// Forward compactions involving last level to the bottom pool if it exists,
|
||||
// such that compactions unlikely to contribute to write stalls can be
|
||||
// delayed or deprioritized.
|
||||
@@ -3897,7 +4166,23 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
ca->db = this;
|
||||
ca->compaction_pri_ = Env::Priority::BOTTOM;
|
||||
ca->prepicked_compaction = new PrepickedCompaction;
|
||||
ca->prepicked_compaction->compaction = c.release();
|
||||
|
||||
// If `universal_reduce_file_locking` is true, we only lock a limited set of
|
||||
// input files by creating an intended compaction to forward to bottom
|
||||
// priority pool and repicking files when bottom priority thread
|
||||
// gets to execute this intended compaction
|
||||
const bool need_repick =
|
||||
c->mutable_cf_options()
|
||||
.compaction_options_universal.reduce_file_locking;
|
||||
if (need_repick) {
|
||||
ca->prepicked_compaction->compaction =
|
||||
CreateIntendedCompactionForwardedToBottomPriorityPool(c.get());
|
||||
c.reset();
|
||||
ca->prepicked_compaction->need_repick = true;
|
||||
} else {
|
||||
ca->prepicked_compaction->compaction = c.release();
|
||||
ca->prepicked_compaction->need_repick = false;
|
||||
}
|
||||
ca->prepicked_compaction->manual_compaction_state = nullptr;
|
||||
// Transfer requested token, so it doesn't need to do it again.
|
||||
ca->prepicked_compaction->task_token = std::move(task_token);
|
||||
@@ -3912,11 +4197,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
output_level = c->output_level();
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:NonTrivial",
|
||||
&output_level);
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
SnapshotChecker* snapshot_checker;
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
InitSnapshotContext(job_context);
|
||||
assert(is_snapshot_supported_ || snapshots_.empty());
|
||||
|
||||
CompactionJob compaction_job(
|
||||
@@ -3925,8 +4206,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
&shutting_down_, log_buffer, directories_.GetDbDir(),
|
||||
GetDataDir(c->column_family_data(), c->output_path_id()),
|
||||
GetDataDir(c->column_family_data(), 0), stats_, &mutex_,
|
||||
&error_handler_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, job_context, table_cache_, &event_logger_,
|
||||
&error_handler_, job_context, table_cache_, &event_logger_,
|
||||
c->mutable_cf_options().paranoid_file_checks,
|
||||
c->mutable_cf_options().report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, thread_pri, io_tracer_,
|
||||
@@ -3946,8 +4226,15 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
compaction_job_stats, job_context->job_id);
|
||||
mutex_.Unlock();
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:BeforeRun", nullptr);
|
||||
if (thread_pri == Env::Priority::LOW) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:BeforeRun", nullptr);
|
||||
} else {
|
||||
assert(thread_pri == Env::Priority::BOTTOM);
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:BeforeRunBottomPri");
|
||||
}
|
||||
|
||||
// Should handle error?
|
||||
compaction_job.Run().PermitUncheckedError();
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:NonTrivial:AfterRun");
|
||||
@@ -4041,9 +4328,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
->storage_info()
|
||||
->ComputeCompactionScore(c->immutable_options(),
|
||||
c->mutable_cf_options());
|
||||
if (!cfd->queued_for_compaction()) {
|
||||
AddToCompactionQueue(cfd);
|
||||
}
|
||||
EnqueuePendingCompaction(cfd);
|
||||
}
|
||||
}
|
||||
// this will unref its input_version and column_family_data
|
||||
@@ -4088,6 +4373,72 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
return status;
|
||||
}
|
||||
|
||||
// Create an intended compaction to forward based on the original picked
|
||||
// compaction. It serves two purposes while it is waiting
|
||||
// for a bottom-priority thread becomes available to run:
|
||||
// - Prevent the last input file (or sorted run if non-L0) from
|
||||
// being included in compaction score calculations unnecessarily since the
|
||||
// intended compaction is already scheduled to compact it
|
||||
// - Allow other input files to be picked by low-priority compactions that can
|
||||
// run right away
|
||||
//
|
||||
// Once a bottom-priority available to run this intended compaction, it will
|
||||
// repick files to consider the LSM updates that occurred during the waiting
|
||||
// period.
|
||||
Compaction* DBImpl::CreateIntendedCompactionForwardedToBottomPriorityPool(
|
||||
Compaction* c) {
|
||||
auto* cfd = c->column_family_data();
|
||||
const auto& io = c->immutable_options();
|
||||
const auto& mo = c->mutable_cf_options();
|
||||
auto* vstorage = c->input_version()->storage_info();
|
||||
|
||||
std::vector<CompactionInputFiles> inputs(1);
|
||||
|
||||
const std::vector<FileMetaData*>* max_intput_level_files = nullptr;
|
||||
int max_intput_level = 0;
|
||||
|
||||
for (size_t i = c->num_input_levels(); i >= 1; --i) {
|
||||
size_t level = i - 1;
|
||||
if (c->num_input_files(level) > 0) {
|
||||
max_intput_level = static_cast<int>(level);
|
||||
max_intput_level_files = c->inputs(level);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(max_intput_level_files);
|
||||
assert(!max_intput_level_files->empty());
|
||||
inputs[0].level = max_intput_level;
|
||||
|
||||
if (max_intput_level == 0) {
|
||||
// The last input file
|
||||
inputs[0].files.push_back(
|
||||
(*max_intput_level_files)[max_intput_level_files->size() - 1]);
|
||||
} else {
|
||||
// The last input sorted run
|
||||
for (FileMetaData* f : (*max_intput_level_files)) {
|
||||
inputs[0].files.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
c->ReleaseCompactionFiles(Status::OK());
|
||||
|
||||
Compaction* intended_compaction =
|
||||
new Compaction(vstorage, io, mo, mutable_db_options_, std::move(inputs),
|
||||
c->output_level(), c->target_output_file_size(),
|
||||
c->max_compaction_bytes(), c->output_path_id(),
|
||||
c->output_compression(), c->output_compression_opts(),
|
||||
c->output_temperature(), c->max_subcompactions(),
|
||||
c->grandparents(), std::nullopt /* earliest_snapshot */,
|
||||
nullptr /* snapshot_checker */, c->compaction_reason());
|
||||
|
||||
cfd->compaction_picker()->RegisterCompaction(intended_compaction);
|
||||
vstorage->ComputeCompactionScore(io, mo);
|
||||
intended_compaction->FinalizeInputInfo(cfd->current());
|
||||
|
||||
return intended_compaction;
|
||||
}
|
||||
|
||||
bool DBImpl::HasPendingManualCompaction() {
|
||||
return (!manual_compaction_dequeue_.empty());
|
||||
}
|
||||
@@ -4176,8 +4527,7 @@ bool DBImpl::MCOverlap(ManualCompactionState* m, ManualCompactionState* m1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DBImpl::UpdateDeletionCompactionStats(
|
||||
const std::unique_ptr<Compaction>& c) {
|
||||
void DBImpl::UpdateFIFOCompactionStatus(const std::unique_ptr<Compaction>& c) {
|
||||
if (c == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -4191,6 +4541,9 @@ void DBImpl::UpdateDeletionCompactionStats(
|
||||
case CompactionReason::kFIFOTtl:
|
||||
RecordTick(stats_, FIFO_TTL_COMPACTIONS);
|
||||
break;
|
||||
case CompactionReason::kChangeTemperature:
|
||||
RecordTick(stats_, FIFO_CHANGE_TEMPERATURE_COMPACTIONS);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
@@ -4357,31 +4710,33 @@ void DBImpl::SetSnapshotChecker(SnapshotChecker* snapshot_checker) {
|
||||
snapshot_checker_.reset(snapshot_checker);
|
||||
}
|
||||
|
||||
void DBImpl::GetSnapshotContext(
|
||||
JobContext* job_context, std::vector<SequenceNumber>* snapshot_seqs,
|
||||
SequenceNumber* earliest_write_conflict_snapshot,
|
||||
SnapshotChecker** snapshot_checker_ptr) {
|
||||
void DBImpl::InitSnapshotContext(JobContext* job_context) {
|
||||
mutex_.AssertHeld();
|
||||
assert(job_context != nullptr);
|
||||
assert(snapshot_seqs != nullptr);
|
||||
assert(earliest_write_conflict_snapshot != nullptr);
|
||||
assert(snapshot_checker_ptr != nullptr);
|
||||
|
||||
*snapshot_checker_ptr = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && *snapshot_checker_ptr == nullptr) {
|
||||
*snapshot_checker_ptr = DisableGCSnapshotChecker::Instance();
|
||||
if (job_context->snapshot_context_initialized) {
|
||||
return;
|
||||
}
|
||||
if (*snapshot_checker_ptr != nullptr) {
|
||||
SnapshotChecker* snapshot_checker = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && !snapshot_checker) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
}
|
||||
std::unique_ptr<ManagedSnapshot> managed_snapshot = nullptr;
|
||||
if (snapshot_checker) {
|
||||
// If snapshot_checker is used, that means the flush/compaction may
|
||||
// contain values not visible to snapshot taken after
|
||||
// flush/compaction job starts. Take a snapshot and it will appear
|
||||
// in snapshot_seqs and force compaction iterator to consider such
|
||||
// snapshots.
|
||||
const Snapshot* job_snapshot =
|
||||
GetSnapshotImpl(false /*write_conflict_boundary*/, false /*lock*/);
|
||||
job_context->job_snapshot.reset(new ManagedSnapshot(this, job_snapshot));
|
||||
const Snapshot* snapshot =
|
||||
GetSnapshotImpl(/*is_write_conflict_boundary=*/false, /*lock=*/false);
|
||||
managed_snapshot.reset(new ManagedSnapshot(this, snapshot));
|
||||
}
|
||||
*snapshot_seqs = snapshots_.GetAll(earliest_write_conflict_snapshot);
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber;
|
||||
std::vector<SequenceNumber> snapshot_seqs =
|
||||
snapshots_.GetAll(&earliest_write_conflict_snapshot);
|
||||
job_context->InitSnapshotContext(
|
||||
snapshot_checker, std::move(managed_snapshot),
|
||||
earliest_write_conflict_snapshot, std::move(snapshot_seqs));
|
||||
}
|
||||
|
||||
Status DBImpl::WaitForCompact(
|
||||
@@ -4440,4 +4795,18 @@ Status DBImpl::WaitForCompact(
|
||||
}
|
||||
}
|
||||
|
||||
bool DBImpl::ShouldPickCompaction(
|
||||
bool is_prepicked, const PrepickedCompaction* prepicked_compaction) {
|
||||
return (!is_prepicked && !compaction_queue_.empty()) ||
|
||||
(is_prepicked && prepicked_compaction->need_repick);
|
||||
}
|
||||
|
||||
void DBImpl::ResetBottomPriCompactionIntent(ColumnFamilyData* cfd,
|
||||
std::unique_ptr<Compaction>& c) {
|
||||
c->ReleaseCompactionFiles(Status::OK());
|
||||
cfd->current()->storage_info()->ComputeCompactionScore(
|
||||
c->immutable_options(), c->mutable_cf_options());
|
||||
c.reset();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -84,6 +84,7 @@ void DBImpl::TEST_GetFilesMetaData(
|
||||
}
|
||||
|
||||
uint64_t DBImpl::TEST_Current_Manifest_FileNo() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return versions_->manifest_file_number();
|
||||
}
|
||||
|
||||
|
||||
@@ -70,9 +70,6 @@ Status DBImplFollower::Recover(
|
||||
}
|
||||
return s;
|
||||
}
|
||||
if (immutable_db_options_.paranoid_checks && s.ok()) {
|
||||
s = CheckConsistency();
|
||||
}
|
||||
if (s.ok()) {
|
||||
default_cf_handle_ = new ColumnFamilyHandleImpl(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), this, &mutex_);
|
||||
|
||||
+48
-32
@@ -191,12 +191,6 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src,
|
||||
"wal_compression is disabled since only zstd is supported");
|
||||
}
|
||||
|
||||
if (!result.paranoid_checks) {
|
||||
result.skip_checking_sst_file_sizes_on_db_open = true;
|
||||
ROCKS_LOG_INFO(result.info_log,
|
||||
"file size check will be skipped during open.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -694,9 +688,6 @@ Status DBImpl::Recover(
|
||||
s = MaybeUpdateNextFileNumber(recovery_ctx);
|
||||
}
|
||||
|
||||
if (immutable_db_options_.paranoid_checks && s.ok()) {
|
||||
s = CheckConsistency();
|
||||
}
|
||||
if (s.ok() && !read_only) {
|
||||
// TODO: share file descriptors (FSDirectory) with SetDirectories above
|
||||
std::map<std::string, std::shared_ptr<FSDirectory>> created_dirs;
|
||||
@@ -2000,6 +1991,9 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
const bool logical_strip_timestamp =
|
||||
ts_sz > 0 && !cfd->ioptions().persist_user_defined_timestamps;
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats flush_stats(CompactionReason::kFlush,
|
||||
1 /* count */);
|
||||
{
|
||||
ScopedArenaPtr<InternalIterator> iter(
|
||||
logical_strip_timestamp
|
||||
@@ -2072,19 +2066,20 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
kMaxSequenceNumber);
|
||||
Version* version = cfd->current();
|
||||
version->Ref();
|
||||
uint64_t num_input_entries = 0;
|
||||
s = BuildTable(dbname_, versions_.get(), immutable_db_options_, tboptions,
|
||||
file_options_for_compaction_, cfd->table_cache(),
|
||||
iter.get(), std::move(range_del_iters), &meta,
|
||||
&blob_file_additions, snapshot_seqs, earliest_snapshot,
|
||||
earliest_write_conflict_snapshot, kMaxSequenceNumber,
|
||||
snapshot_checker, paranoid_file_checks,
|
||||
cfd->internal_stats(), &io_s, io_tracer_,
|
||||
BlobFileCreationReason::kRecovery,
|
||||
nullptr /* seqno_to_time_mapping */, &event_logger_,
|
||||
job_id, nullptr /* table_properties */, write_hint,
|
||||
nullptr /*full_history_ts_low*/, &blob_callback_, version,
|
||||
&num_input_entries);
|
||||
TableProperties temp_table_proerties;
|
||||
s = BuildTable(
|
||||
dbname_, versions_.get(), immutable_db_options_, tboptions,
|
||||
file_options_for_compaction_, cfd->table_cache(), iter.get(),
|
||||
std::move(range_del_iters), &meta, &blob_file_additions,
|
||||
snapshot_seqs, earliest_snapshot, earliest_write_conflict_snapshot,
|
||||
kMaxSequenceNumber, snapshot_checker, paranoid_file_checks,
|
||||
cfd->internal_stats(), &io_s, io_tracer_,
|
||||
BlobFileCreationReason::kRecovery,
|
||||
nullptr /* seqno_to_time_mapping */, &event_logger_, job_id,
|
||||
&temp_table_proerties /* table_properties */, write_hint,
|
||||
nullptr /*full_history_ts_low*/, &blob_callback_, version,
|
||||
nullptr /* memtable_payload_bytes */,
|
||||
nullptr /* memtable_garbage_bytes */, &flush_stats);
|
||||
version->Unref();
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
@@ -2100,10 +2095,31 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
}
|
||||
|
||||
uint64_t total_num_entries = mem->NumEntries();
|
||||
if (s.ok() && total_num_entries != num_input_entries) {
|
||||
if (s.ok() && total_num_entries != flush_stats.num_input_records) {
|
||||
std::string msg = "Expected " + std::to_string(total_num_entries) +
|
||||
" entries in memtable, but read " +
|
||||
std::to_string(num_input_entries);
|
||||
std::to_string(flush_stats.num_input_records);
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush during recover: %s",
|
||||
cfd->GetName().c_str(), job_id, msg.c_str());
|
||||
if (immutable_db_options_.flush_verify_memtable_count) {
|
||||
s = Status::Corruption(msg);
|
||||
}
|
||||
}
|
||||
// Only verify on table with format collects table properties
|
||||
const auto& mutable_cf_options = cfd->GetLatestMutableCFOptions();
|
||||
if (s.ok() &&
|
||||
(mutable_cf_options.table_factory->IsInstanceOf(
|
||||
TableFactory::kBlockBasedTableName()) ||
|
||||
mutable_cf_options.table_factory->IsInstanceOf(
|
||||
TableFactory::kPlainTableName())) &&
|
||||
flush_stats.num_output_records != temp_table_proerties.num_entries) {
|
||||
std::string msg =
|
||||
"Number of keys in flush output SST files does not match "
|
||||
"number of keys added to the table. Expected " +
|
||||
std::to_string(flush_stats.num_output_records) + " but there are " +
|
||||
std::to_string(temp_table_proerties.num_entries) +
|
||||
" in output SST files";
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush during recover: %s",
|
||||
cfd->GetName().c_str(), job_id, msg.c_str());
|
||||
@@ -2151,25 +2167,25 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
}
|
||||
}
|
||||
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
stats.micros = immutable_db_options_.clock->NowMicros() - start_micros;
|
||||
flush_stats.micros = immutable_db_options_.clock->NowMicros() - start_micros;
|
||||
|
||||
if (has_output) {
|
||||
stats.bytes_written = meta.fd.GetFileSize();
|
||||
stats.num_output_files = 1;
|
||||
flush_stats.bytes_written = meta.fd.GetFileSize();
|
||||
flush_stats.num_output_files = 1;
|
||||
}
|
||||
|
||||
const auto& blobs = edit->GetBlobFileAdditions();
|
||||
for (const auto& blob : blobs) {
|
||||
stats.bytes_written_blob += blob.GetTotalBlobBytes();
|
||||
flush_stats.bytes_written_blob += blob.GetTotalBlobBytes();
|
||||
}
|
||||
|
||||
stats.num_output_files_blob = static_cast<int>(blobs.size());
|
||||
flush_stats.num_output_files_blob = static_cast<int>(blobs.size());
|
||||
|
||||
cfd->internal_stats()->AddCompactionStats(level, Env::Priority::USER, stats);
|
||||
cfd->internal_stats()->AddCompactionStats(level, Env::Priority::USER,
|
||||
flush_stats);
|
||||
cfd->internal_stats()->AddCFStats(
|
||||
InternalStats::BYTES_FLUSHED,
|
||||
stats.bytes_written + stats.bytes_written_blob);
|
||||
flush_stats.bytes_written + flush_stats.bytes_written_blob);
|
||||
RecordTick(stats_, COMPACT_WRITE_BYTES, meta.fd.GetFileSize());
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -49,9 +49,6 @@ Status DBImplSecondary::Recover(
|
||||
}
|
||||
return s;
|
||||
}
|
||||
if (immutable_db_options_.paranoid_checks && s.ok()) {
|
||||
s = CheckConsistency();
|
||||
}
|
||||
// Initial max_total_in_memory_state_ before recovery logs.
|
||||
max_total_in_memory_state_ = 0;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
@@ -653,49 +650,6 @@ Status DBImplSecondary::NewIterators(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImplSecondary::CheckConsistency() {
|
||||
mutex_.AssertHeld();
|
||||
Status s = DBImpl::CheckConsistency();
|
||||
// If DBImpl::CheckConsistency() which is stricter returns success, then we
|
||||
// do not need to give a second chance.
|
||||
if (s.ok()) {
|
||||
return s;
|
||||
}
|
||||
// It's possible that DBImpl::CheckConssitency() can fail because the primary
|
||||
// may have removed certain files, causing the GetFileSize(name) call to
|
||||
// fail and returning a PathNotFound. In this case, we take a best-effort
|
||||
// approach and just proceed.
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImplSecondary::CheckConsistency:AfterFirstAttempt", &s);
|
||||
|
||||
if (immutable_db_options_.skip_checking_sst_file_sizes_on_db_open) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
versions_->GetLiveFilesMetaData(&metadata);
|
||||
|
||||
std::string corruption_messages;
|
||||
for (const auto& md : metadata) {
|
||||
// md.name has a leading "/".
|
||||
std::string file_path = md.db_path + md.name;
|
||||
|
||||
uint64_t fsize = 0;
|
||||
s = env_->GetFileSize(file_path, &fsize);
|
||||
if (!s.ok() &&
|
||||
(env_->GetFileSize(Rocks2LevelTableFileName(file_path), &fsize).ok() ||
|
||||
s.IsPathNotFound())) {
|
||||
s = Status::OK();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
corruption_messages +=
|
||||
"Can't access " + md.name + ": " + s.ToString() + "\n";
|
||||
}
|
||||
}
|
||||
return corruption_messages.empty() ? Status::OK()
|
||||
: Status::Corruption(corruption_messages);
|
||||
}
|
||||
|
||||
Status DBImplSecondary::TryCatchUpWithPrimary() {
|
||||
assert(versions_.get() != nullptr);
|
||||
Status s;
|
||||
@@ -728,13 +682,13 @@ Status DBImplSecondary::TryCatchUpWithPrimary() {
|
||||
// instance
|
||||
if (s.ok()) {
|
||||
s = FindAndRecoverLogFiles(&cfds_changed, &job_context);
|
||||
}
|
||||
if (s.IsPathNotFound()) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Secondary tries to read WAL, but WAL file(s) have already "
|
||||
"been purged by primary.");
|
||||
s = Status::OK();
|
||||
if (s.IsPathNotFound()) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Secondary tries to read WAL, but WAL file(s) have already "
|
||||
"been purged by primary.");
|
||||
s = Status::OK();
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
for (auto cfd : cfds_changed) {
|
||||
@@ -909,7 +863,7 @@ Status DBImplSecondary::CompactWithoutInstallation(
|
||||
ROCKS_LOG_ERROR(
|
||||
immutable_db_options_.info_log,
|
||||
"GetCompactionInputsFromFileNumbers() failed - %s.\n DebugString: %s",
|
||||
s.ToString().c_str(), version->DebugString().c_str());
|
||||
s.ToString().c_str(), version->DebugString(/*hex=*/true).c_str());
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -933,7 +887,10 @@ Status DBImplSecondary::CompactWithoutInstallation(
|
||||
immutable_db_options_.info_log.get());
|
||||
|
||||
const int job_id = next_job_id_.fetch_add(1);
|
||||
|
||||
JobContext job_context(0, true /*create_superversion*/);
|
||||
std::vector<SequenceNumber> snapshots = input.snapshots;
|
||||
job_context.InitSnapshotContext(nullptr, nullptr, kMaxSequenceNumber,
|
||||
std::move(snapshots));
|
||||
// use primary host's db_id for running the compaction, but db_session_id is
|
||||
// using the local one, which is to make sure the unique id is unique from
|
||||
// the remote compactors. Because the id is generated from db_id,
|
||||
@@ -944,7 +901,7 @@ Status DBImplSecondary::CompactWithoutInstallation(
|
||||
job_id, c.get(), immutable_db_options_, mutable_db_options_,
|
||||
file_options_for_compaction_, versions_.get(), &shutting_down_,
|
||||
&log_buffer, output_dir.get(), stats_, &mutex_, &error_handler_,
|
||||
input.snapshots, table_cache_, &event_logger_, dbname_, io_tracer_,
|
||||
&job_context, table_cache_, &event_logger_, dbname_, io_tracer_,
|
||||
options.canceled ? *options.canceled : kManualCompactionCanceledFalse_,
|
||||
input.db_id, db_session_id_, secondary_path_, input, result);
|
||||
|
||||
|
||||
@@ -248,12 +248,6 @@ class DBImplSecondary : public DBImpl {
|
||||
Status MaybeInitLogReader(uint64_t log_number,
|
||||
log::FragmentBufferedReader** log_reader);
|
||||
|
||||
// Check if all live files exist on file system and that their file sizes
|
||||
// matche to the in-memory records. It is possible that some live files may
|
||||
// have been deleted by the primary. In this case, CheckConsistency() does
|
||||
// not flag the missing file as inconsistency.
|
||||
Status CheckConsistency() override;
|
||||
|
||||
#ifndef NDEBUG
|
||||
Status TEST_CompactWithoutInstallation(const OpenAndCompactOptions& options,
|
||||
ColumnFamilyHandle* cfh,
|
||||
|
||||
@@ -856,8 +856,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
write_group, current_sequence, column_family_memtables_.get(),
|
||||
&flush_scheduler_, &trim_history_scheduler_,
|
||||
write_options.ignore_missing_column_families,
|
||||
0 /*recovery_log_number*/, this, parallel, seq_per_batch_,
|
||||
batch_per_txn_);
|
||||
0 /*recovery_log_number*/, this, seq_per_batch_, batch_per_txn_);
|
||||
} else {
|
||||
write_group.last_sequence = last_sequence;
|
||||
write_thread_.LaunchParallelMemTableWriters(&write_group);
|
||||
@@ -929,6 +928,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
/*min_prep_log=*/log_ref, last_sequence,
|
||||
/*memtable_updated=*/memtable_update_count > 0,
|
||||
write_options.ignore_missing_column_families);
|
||||
RecordTick(stats_, NUMBER_WBWI_INGEST);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1114,7 +1114,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
memtable_write_group, w.sequence, column_family_memtables_.get(),
|
||||
&flush_scheduler_, &trim_history_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/, this,
|
||||
false /*concurrent_memtable_writes*/, seq_per_batch_, batch_per_txn_);
|
||||
seq_per_batch_, batch_per_txn_);
|
||||
if (memtable_write_group.status
|
||||
.ok()) { // Don't publish a partial batch write
|
||||
versions_->SetLastSequence(memtable_write_group.last_sequence);
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "test_util/testutil.h"
|
||||
|
||||
+32
-22
@@ -66,11 +66,11 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
timestamp_lb_(read_options.iter_start_ts),
|
||||
timestamp_size_(timestamp_ub_ ? timestamp_ub_->size() : 0),
|
||||
active_mem_(active_mem),
|
||||
memtable_seqno_lb_((active_mem_ && !active_mem_->IsEmpty())
|
||||
? active_mem_->GetFirstSequenceNumber()
|
||||
: kMaxSequenceNumber),
|
||||
memtable_op_scan_flush_trigger_(
|
||||
mutable_cf_options.memtable_op_scan_flush_trigger),
|
||||
memtable_seqno_lb_(kMaxSequenceNumber),
|
||||
memtable_op_scan_flush_trigger_(0),
|
||||
avg_op_scan_flush_trigger_(0),
|
||||
iter_step_since_seek_(1),
|
||||
mem_hidden_op_scanned_since_seek_(0),
|
||||
direction_(kForward),
|
||||
valid_(false),
|
||||
current_entry_is_merged_(false),
|
||||
@@ -98,6 +98,25 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
// prefix_seek_opt_in_only should force total_order_seek whereever the caller
|
||||
// is duplicating the original ReadOptions
|
||||
assert(!ioptions.prefix_seek_opt_in_only || read_options.total_order_seek);
|
||||
if (active_mem_) {
|
||||
// FIXME: GetEarliestSequenceNumber() may return a seqno that is one smaller
|
||||
// than the smallest seqno in the memtable. This violates its comment and
|
||||
// entries with that seqno may not be in the active memtable. Before it's
|
||||
// fixed, we use GetFirstSequenceNumber() for more accurate result.
|
||||
memtable_seqno_lb_ = active_mem_->IsEmpty()
|
||||
? active_mem_->GetEarliestSequenceNumber()
|
||||
: active_mem_->GetFirstSequenceNumber();
|
||||
memtable_op_scan_flush_trigger_ =
|
||||
mutable_cf_options.memtable_op_scan_flush_trigger;
|
||||
if (memtable_op_scan_flush_trigger_) {
|
||||
// avg_op_scan_flush_trigger_ requires memtable_op_scan_flush_trigger_ > 0
|
||||
avg_op_scan_flush_trigger_ =
|
||||
mutable_cf_options.memtable_avg_op_scan_flush_trigger;
|
||||
}
|
||||
} else {
|
||||
// memtable_op_scan_flush_trigger_ and avg_op_scan_flush_trigger_ are
|
||||
// initialized to 0(disabled) as default.
|
||||
}
|
||||
}
|
||||
|
||||
Status DBIter::GetProperty(std::string prop_name, std::string* prop) {
|
||||
@@ -159,6 +178,7 @@ void DBIter::Next() {
|
||||
local_stats_.skip_count_ += num_internal_keys_skipped_;
|
||||
local_stats_.skip_count_--;
|
||||
num_internal_keys_skipped_ = 0;
|
||||
iter_step_since_seek_++;
|
||||
bool ok = true;
|
||||
if (direction_ == kReverse) {
|
||||
is_key_seqnum_zero_ = false;
|
||||
@@ -373,8 +393,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
// to one.
|
||||
bool reseek_done = false;
|
||||
|
||||
uint64_t mem_ops_scanned = 0;
|
||||
bool marked_for_flush = false;
|
||||
uint64_t mem_hidden_op_scanned = 0;
|
||||
do {
|
||||
// Will update is_key_seqnum_zero_ as soon as we parsed the current key
|
||||
// but we need to save the previous value to be used in the loop.
|
||||
@@ -431,12 +450,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
CompareKeyForSkip(ikey_.user_key, saved_key_.GetUserKey()) <= 0) {
|
||||
num_skipped++; // skip this entry
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
if (memtable_op_scan_flush_trigger_ && active_mem_ &&
|
||||
ikey_.sequence >= memtable_seqno_lb_ && !marked_for_flush &&
|
||||
++mem_ops_scanned >= memtable_op_scan_flush_trigger_) {
|
||||
active_mem_->MarkForFlush();
|
||||
marked_for_flush = true;
|
||||
}
|
||||
MarkMemtableForFlushForPerOpTrigger(mem_hidden_op_scanned);
|
||||
} else {
|
||||
assert(!skipping_saved_key ||
|
||||
CompareKeyForSkip(ikey_.user_key, saved_key_.GetUserKey()) > 0);
|
||||
@@ -458,12 +472,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
!iter_.iter()->IsKeyPinned() /* copy */);
|
||||
skipping_saved_key = true;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
if (memtable_op_scan_flush_trigger_ && active_mem_ &&
|
||||
ikey_.sequence >= memtable_seqno_lb_ && !marked_for_flush &&
|
||||
++mem_ops_scanned >= memtable_op_scan_flush_trigger_) {
|
||||
active_mem_->MarkForFlush();
|
||||
marked_for_flush = true;
|
||||
}
|
||||
MarkMemtableForFlushForPerOpTrigger(mem_hidden_op_scanned);
|
||||
}
|
||||
break;
|
||||
case kTypeValue:
|
||||
@@ -502,7 +511,6 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
|
||||
valid_ = true;
|
||||
return true;
|
||||
break;
|
||||
case kTypeMerge:
|
||||
if (!PrepareValueInternal()) {
|
||||
return false;
|
||||
@@ -514,7 +522,6 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
current_entry_is_merged_ = true;
|
||||
valid_ = true;
|
||||
return MergeValuesNewToOld(); // Go to a different state machine
|
||||
break;
|
||||
default:
|
||||
valid_ = false;
|
||||
status_ = Status::Corruption(
|
||||
@@ -1115,7 +1122,6 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case kTypeValue:
|
||||
case kTypeValuePreferredSeqno:
|
||||
SetValueAndColumnsFromPlain(pinned_value_);
|
||||
@@ -1588,6 +1594,7 @@ void DBIter::Seek(const Slice& target) {
|
||||
ResetBlobData();
|
||||
ResetValueAndColumns();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
MarkMemtableForFlushForAvgTrigger();
|
||||
|
||||
// Seek the inner iterator based on the target key.
|
||||
{
|
||||
@@ -1664,6 +1671,7 @@ void DBIter::SeekForPrev(const Slice& target) {
|
||||
ResetBlobData();
|
||||
ResetValueAndColumns();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
MarkMemtableForFlushForAvgTrigger();
|
||||
|
||||
// Seek the inner iterator based on the target key.
|
||||
{
|
||||
@@ -1725,6 +1733,7 @@ void DBIter::SeekToFirst() {
|
||||
ResetBlobData();
|
||||
ResetValueAndColumns();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
MarkMemtableForFlushForAvgTrigger();
|
||||
ClearSavedValue();
|
||||
is_key_seqnum_zero_ = false;
|
||||
|
||||
@@ -1788,6 +1797,7 @@ void DBIter::SeekToLast() {
|
||||
ResetBlobData();
|
||||
ResetValueAndColumns();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
MarkMemtableForFlushForAvgTrigger();
|
||||
ClearSavedValue();
|
||||
is_key_seqnum_zero_ = false;
|
||||
|
||||
|
||||
+38
-4
@@ -12,7 +12,6 @@
|
||||
#include <string>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "memory/arena.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -62,7 +61,8 @@ class DBIter final : public Iterator {
|
||||
//
|
||||
// @param active_mem Pointer to the active memtable that `internal_iter`
|
||||
// is reading from. If not null, the memtable can be marked for flush
|
||||
// according to option mutable_cf_options.memtable_op_scan_flush_trigger.
|
||||
// according to options mutable_cf_options.memtable_op_scan_flush_trigger
|
||||
// and mutable_cf_options.memtable_avg_op_scan_flush_trigger.
|
||||
// @param arena_mode If true, the DBIter will be allocated from the arena.
|
||||
static DBIter* NewIter(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
@@ -71,9 +71,9 @@ class DBIter final : public Iterator {
|
||||
InternalIterator* internal_iter,
|
||||
const Version* version, const SequenceNumber& sequence,
|
||||
ReadCallback* read_callback,
|
||||
ReadOnlyMemTable* active_mem,
|
||||
ColumnFamilyHandleImpl* cfh = nullptr,
|
||||
bool expose_blob_index = false,
|
||||
ReadOnlyMemTable* active_mem = nullptr,
|
||||
Arena* arena = nullptr) {
|
||||
void* mem = arena ? arena->AllocateAligned(sizeof(DBIter))
|
||||
: operator new(sizeof(DBIter));
|
||||
@@ -145,6 +145,7 @@ class DBIter final : public Iterator {
|
||||
void operator=(const DBIter&) = delete;
|
||||
|
||||
~DBIter() override {
|
||||
MarkMemtableForFlushForAvgTrigger();
|
||||
ThreadStatus::OperationType cur_op_type =
|
||||
ThreadStatusUtil::GetThreadOperation();
|
||||
ThreadStatusUtil::SetThreadOperation(
|
||||
@@ -417,6 +418,36 @@ class DBIter final : public Iterator {
|
||||
return true;
|
||||
}
|
||||
|
||||
void MarkMemtableForFlushForAvgTrigger() {
|
||||
if (avg_op_scan_flush_trigger_ &&
|
||||
mem_hidden_op_scanned_since_seek_ >= memtable_op_scan_flush_trigger_ &&
|
||||
mem_hidden_op_scanned_since_seek_ >=
|
||||
static_cast<uint64_t>(iter_step_since_seek_) *
|
||||
avg_op_scan_flush_trigger_) {
|
||||
assert(memtable_op_scan_flush_trigger_ > 0);
|
||||
active_mem_->MarkForFlush();
|
||||
avg_op_scan_flush_trigger_ = 0;
|
||||
memtable_op_scan_flush_trigger_ = 0;
|
||||
}
|
||||
iter_step_since_seek_ = 1;
|
||||
mem_hidden_op_scanned_since_seek_ = 0;
|
||||
}
|
||||
|
||||
void MarkMemtableForFlushForPerOpTrigger(uint64_t& mem_hidden_op_scanned) {
|
||||
if (memtable_op_scan_flush_trigger_ &&
|
||||
ikey_.sequence >= memtable_seqno_lb_) {
|
||||
if (++mem_hidden_op_scanned >= memtable_op_scan_flush_trigger_) {
|
||||
active_mem_->MarkForFlush();
|
||||
// Turn off the flush trigger checks.
|
||||
memtable_op_scan_flush_trigger_ = 0;
|
||||
avg_op_scan_flush_trigger_ = 0;
|
||||
}
|
||||
if (avg_op_scan_flush_trigger_) {
|
||||
++mem_hidden_op_scanned_since_seek_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SliceTransform* prefix_extractor_;
|
||||
Env* const env_;
|
||||
SystemClock* clock_;
|
||||
@@ -475,9 +506,12 @@ class DBIter final : public Iterator {
|
||||
const size_t timestamp_size_;
|
||||
std::string saved_timestamp_;
|
||||
std::optional<std::vector<ScanOptions>> scan_opts_;
|
||||
ReadOnlyMemTable* active_mem_;
|
||||
ReadOnlyMemTable* const active_mem_;
|
||||
SequenceNumber memtable_seqno_lb_;
|
||||
uint32_t memtable_op_scan_flush_trigger_;
|
||||
uint32_t avg_op_scan_flush_trigger_;
|
||||
uint32_t iter_step_since_seek_;
|
||||
uint32_t mem_hidden_op_scanned_since_seek_;
|
||||
Direction direction_;
|
||||
bool valid_;
|
||||
bool current_entry_is_merged_;
|
||||
|
||||
@@ -532,7 +532,7 @@ TEST_F(DBIteratorStressTest, StressTest) {
|
||||
env_, ropt, ImmutableOptions(options),
|
||||
MutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, /*version=*/nullptr, sequence,
|
||||
nullptr /*read_callback*/));
|
||||
nullptr /*read_callback*/, /*active_mem=*/nullptr));
|
||||
}
|
||||
|
||||
// Do a random operation. It's important to do it on ref_it
|
||||
|
||||
+84
-82
@@ -262,7 +262,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -296,7 +296,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -323,7 +323,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -356,7 +356,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -392,7 +392,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
@@ -423,7 +423,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 7 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_TRUE(GetPerfLevel() == kEnableCount);
|
||||
@@ -462,7 +462,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -488,7 +488,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
@@ -512,7 +512,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -548,7 +548,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 7 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_TRUE(GetPerfLevel() == kEnableCount);
|
||||
@@ -579,7 +579,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -623,7 +623,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -655,7 +655,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -686,7 +686,7 @@ TEST_F(DBIteratorTest, DBIteratorEmpty) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
ASSERT_OK(db_iter->status());
|
||||
@@ -699,7 +699,7 @@ TEST_F(DBIteratorTest, DBIteratorEmpty) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
ASSERT_OK(db_iter->status());
|
||||
@@ -723,7 +723,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkipCountSkips) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
2 /* sequence */, nullptr /* read_callback */));
|
||||
2 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -770,7 +770,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -808,7 +808,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -838,8 +838,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 202 /* sequence */,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -874,7 +873,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i /* sequence */,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
ASSERT_OK(db_iter->status());
|
||||
@@ -893,7 +892,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 200 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -931,7 +930,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -968,7 +967,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -1019,7 +1018,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1066,7 +1065,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1111,7 +1110,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1150,7 +1149,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1186,7 +1185,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1217,7 +1216,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1255,7 +1254,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1294,7 +1293,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 * i + 1 /* sequence */,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1348,7 +1347,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, MutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 * i + 1 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1391,7 +1390,7 @@ TEST_F(DBIteratorTest, DBIteratorTimedPutBasic) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
7 /* sequence */, nullptr /* read_callback */));
|
||||
7 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1441,7 +1440,7 @@ TEST_F(DBIteratorTest, DBIterator1) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
1 /* sequence */, nullptr /* read_callback */));
|
||||
1 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1470,7 +1469,7 @@ TEST_F(DBIteratorTest, DBIterator2) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
0 /* sequence */, nullptr /* read_callback */));
|
||||
0 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1495,7 +1494,7 @@ TEST_F(DBIteratorTest, DBIterator3) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
2 /* sequence */, nullptr /* read_callback */));
|
||||
2 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1520,7 +1519,7 @@ TEST_F(DBIteratorTest, DBIterator4) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
4 /* sequence */, nullptr /* read_callback */));
|
||||
4 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1554,7 +1553,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1578,7 +1577,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 1 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1602,7 +1601,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1626,7 +1625,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 3 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1650,7 +1649,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1674,7 +1673,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 5 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1698,7 +1697,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 6 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1720,7 +1719,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -1751,7 +1750,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1775,7 +1774,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 1 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1799,7 +1798,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1823,7 +1822,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 3 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
ASSERT_OK(db_iter->status());
|
||||
@@ -1843,7 +1842,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1867,7 +1866,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 5 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1891,7 +1890,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 6 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1935,7 +1934,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1971,7 +1970,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2013,7 +2012,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2055,7 +2054,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 5 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2102,7 +2101,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 6 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2150,7 +2149,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 7 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2192,7 +2191,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 9 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2240,7 +2239,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 13 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2289,7 +2288,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 14 /* sequence */,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2321,7 +2320,7 @@ TEST_F(DBIteratorTest, DBIterator8) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -2349,10 +2348,11 @@ TEST_F(DBIteratorTest, DBIterator9) {
|
||||
internal_iter->AddMerge("d", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
DBIter::NewIter(env_, ro, ImmutableOptions(options),
|
||||
MutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2419,7 +2419,7 @@ TEST_F(DBIteratorTest, DBIterator10) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->Seek("c");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2459,7 +2459,7 @@ TEST_F(DBIteratorTest, SeekToLastOccurrenceSeq0) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, 0 /* force seek */, nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -2489,7 +2489,7 @@ TEST_F(DBIteratorTest, DBIterator11) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
1 /* sequence */, nullptr /* read_callback */));
|
||||
1 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -2517,7 +2517,7 @@ TEST_F(DBIteratorTest, DBIterator12) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, 0 /* force seek */, nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -2557,7 +2557,7 @@ TEST_F(DBIteratorTest, DBIterator13) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
2 /* sequence */, nullptr /* read_callback */));
|
||||
2 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), key);
|
||||
@@ -2587,7 +2587,7 @@ TEST_F(DBIteratorTest, DBIterator14) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
4 /* sequence */, nullptr /* read_callback */));
|
||||
4 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -2627,7 +2627,8 @@ class DBIterWithMergeIterTest : public testing::Test {
|
||||
db_iter_.reset(DBIter::NewIter(
|
||||
env_, ro_, ImmutableOptions(options_), MutableCFOptions(options_),
|
||||
BytewiseComparator(), merge_iter, nullptr /* version */,
|
||||
8 /* read data earlier than seqId 8 */, nullptr /* read_callback */));
|
||||
8 /* read data earlier than seqId 8 */, nullptr /* read_callback */,
|
||||
/*active_mem=*/nullptr));
|
||||
}
|
||||
|
||||
Env* env_;
|
||||
@@ -3066,7 +3067,7 @@ TEST_F(DBIteratorTest, SeekPrefixTombstones) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
int skipped_keys = 0;
|
||||
|
||||
@@ -3099,10 +3100,11 @@ TEST_F(DBIteratorTest, SeekToFirstLowerBound) {
|
||||
Slice lower_bound(lower_bound_str);
|
||||
ro.iterate_lower_bound = &lower_bound;
|
||||
Options options;
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
DBIter::NewIter(env_, ro, ImmutableOptions(options),
|
||||
MutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
if (i == kNumKeys + 1) {
|
||||
@@ -3141,7 +3143,7 @@ TEST_F(DBIteratorTest, PrevLowerBound) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
for (int i = kNumKeys; i >= kLowerBound; --i) {
|
||||
@@ -3169,7 +3171,7 @@ TEST_F(DBIteratorTest, SeekLessLowerBound) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
auto before_lower_bound_str = std::to_string(kLowerBound - 1);
|
||||
Slice before_lower_bound(lower_bound_str);
|
||||
@@ -3194,7 +3196,7 @@ TEST_F(DBIteratorTest, ReverseToForwardWithDisappearingKeys) {
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ReadOptions(), ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekForPrev("a");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
#include "db/arena_wrapped_db_iter.h"
|
||||
#include "db/db_iter.h"
|
||||
@@ -3890,6 +3892,7 @@ TEST_P(DBIteratorTest, MemtableOpsScanFlushTriggerWithSeek) {
|
||||
db_->GetIntProperty("rocksdb.num-deletes-active-mem-table", &val));
|
||||
ASSERT_EQ(0, val);
|
||||
} else {
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
uint64_t val = 0;
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty("rocksdb.num-deletes-active-mem-table", &val));
|
||||
@@ -3984,6 +3987,344 @@ TEST_P(DBIteratorTest, MemtableOpsScanFlushTriggerWithNext) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, AverageMemtableOpsScanFlushTrigger) {
|
||||
// Tests option memtable_avg_op_scan_flush_trigger with
|
||||
// long tombstone sequences.
|
||||
Random* r = Random::GetTLSInstance();
|
||||
|
||||
const int kAvgTrigger = 10;
|
||||
const int kMaxTrigger = 500;
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_op_scan_flush_trigger = kMaxTrigger;
|
||||
options.memtable_avg_op_scan_flush_trigger = kAvgTrigger;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kNumKeys = 1000;
|
||||
// Base data that will be covered by a consecutive sequence of tombstones.
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(Put(Key(i), r->RandomString(50)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange({}, nullptr, nullptr));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(6));
|
||||
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
// We issue slightly more deletions than kAvgTrigger between visible keys
|
||||
// to ensure avg skipped entries exceed kAvgTrigger.
|
||||
if (i % (kAvgTrigger + 2) != 0) {
|
||||
ASSERT_OK(SingleDelete(Key(i)));
|
||||
}
|
||||
}
|
||||
|
||||
// Each operation, except the first Seek, is expected to see kAvgTrigger + 1
|
||||
// tombstones (from the active memtable) before it finds the next visible key.
|
||||
SetPerfLevel(PerfLevel::kEnableCount);
|
||||
get_perf_context()->Reset();
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
iter->Seek(Key(1));
|
||||
ASSERT_EQ(get_perf_context()->next_on_memtable_count, kAvgTrigger + 1);
|
||||
iter.reset();
|
||||
// Should not flush since total entries skipped is below
|
||||
// memtable_op_scan_flush_trigger
|
||||
ASSERT_OK(Put(Key(0), "dummy write"));
|
||||
ASSERT_OK(Put(Key(0), "dummy write"));
|
||||
ASSERT_OK(db_->WaitForCompact({}));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
|
||||
get_perf_context()->Reset();
|
||||
iter.reset(db_->NewIterator(ReadOptions()));
|
||||
int num_ops = 1;
|
||||
uint64_t num_skipped = 0;
|
||||
iter->Seek(Key(0));
|
||||
ASSERT_EQ(iter->key(), Key(0));
|
||||
uint64_t last_memtable_next_count =
|
||||
get_perf_context()->next_on_memtable_count;
|
||||
iter->Next();
|
||||
num_ops++;
|
||||
while (iter->Valid()) {
|
||||
ASSERT_OK(iter->status());
|
||||
uint64_t num_skipped_in_op =
|
||||
get_perf_context()->next_on_memtable_count - last_memtable_next_count;
|
||||
ASSERT_GE(num_skipped_in_op, kAvgTrigger + 1);
|
||||
last_memtable_next_count = get_perf_context()->next_on_memtable_count;
|
||||
num_skipped += num_skipped_in_op;
|
||||
iter->Next();
|
||||
num_ops++;
|
||||
}
|
||||
// During iterator destruction we mark memtable for flush
|
||||
iter.reset();
|
||||
|
||||
// avg trigger
|
||||
ASSERT_GE(num_skipped, kAvgTrigger * num_ops);
|
||||
// memtable_op_scan_flush_trigger
|
||||
ASSERT_GE(num_skipped, kMaxTrigger);
|
||||
// Average hidden entries scanned from memtable per operation is more than
|
||||
// kAvgTrigger and the total skipped is more than
|
||||
// memtable_op_scan_flush_trigger, the current memtable should be marked for
|
||||
// flush. The following two writes will trigger the flush.
|
||||
ASSERT_OK(Put(Key(0), "dummy write"));
|
||||
// Before a write, we schedule memtables for flush if requested.
|
||||
ASSERT_OK(Put(Key(0), "dummy write"));
|
||||
ASSERT_OK(db_->WaitForCompact({}));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, AverageMemtableOpsScanFlushTriggerByOverwrites) {
|
||||
// Tests option memtable_avg_op_scan_flush_trigger with overwrites to keys.
|
||||
Random* r = Random::GetTLSInstance();
|
||||
|
||||
const int kAvgTrigger = 25;
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_op_scan_flush_trigger = 250;
|
||||
options.memtable_avg_op_scan_flush_trigger = kAvgTrigger;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kNumKeys = 100;
|
||||
// Base data that will be covered by a consecutive sequence of tombstones.
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(Put(Key(i), r->RandomString(50)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange({}, nullptr, nullptr));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(6));
|
||||
|
||||
// One visible key every 10 keys.
|
||||
// Each non-visible user key has 3 non-visible entries in the active memtable.
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
if (i % 10 != 0) {
|
||||
ASSERT_OK(Put(Key(i), r->RandomString(50)));
|
||||
ASSERT_OK(Put(Key(i), r->RandomString(50)));
|
||||
ASSERT_OK(Delete(Key(i)));
|
||||
}
|
||||
}
|
||||
|
||||
SetPerfLevel(PerfLevel::kEnableCount);
|
||||
get_perf_context()->Reset();
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
iter->Seek(Key(1));
|
||||
ASSERT_GT(get_perf_context()->next_on_memtable_count, kAvgTrigger);
|
||||
// Re-seek to trigger check for flush trigger
|
||||
iter->Seek(Key(1));
|
||||
// Should not flush since total entries skipped is below
|
||||
// memtable_op_scan_flush_trigger
|
||||
ASSERT_FALSE(static_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())
|
||||
->cfd()
|
||||
->mem()
|
||||
->IsMarkedForFlush());
|
||||
ASSERT_OK(Put(Key(0), "dummy write"));
|
||||
ASSERT_OK(Put(Key(0), "dummy write"));
|
||||
ASSERT_OK(db_->WaitForCompact({}));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
get_perf_context()->Reset();
|
||||
|
||||
int num_ops = 1;
|
||||
iter->Seek(Key(1));
|
||||
while (iter->Valid()) {
|
||||
num_ops++;
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_GT(get_perf_context()->next_on_memtable_count, num_ops * kAvgTrigger);
|
||||
|
||||
// Re-seek should check conditions for marking memtable for flush
|
||||
iter->Seek(Key(80));
|
||||
|
||||
// Average hidden entries scanned from memtable per operation is 2.
|
||||
ASSERT_OK(Put(Key(0), "dummy write"));
|
||||
// Before a write, we schedule memtables for flush if requested.
|
||||
ASSERT_OK(Put(Key(0), "dummy write"));
|
||||
ASSERT_OK(db_->WaitForCompact({}));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
class DBMultiScanIteratorTest : public DBTestBase {
|
||||
public:
|
||||
DBMultiScanIteratorTest()
|
||||
: DBTestBase("db_multi_scan_iterator_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBMultiScanIteratorTest, BasicTest) {
|
||||
// Create a file
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
std::stringstream ss;
|
||||
ss << std::setw(2) << std::setfill('0') << i;
|
||||
ASSERT_OK(Put("k" + ss.str(), "val" + ss.str()));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::vector<std::string> key_ranges({"k03", "k10", "k25", "k50"});
|
||||
ReadOptions ro;
|
||||
std::vector<ScanOptions> scan_options(
|
||||
{ScanOptions(key_ranges[0], key_ranges[1]),
|
||||
ScanOptions(key_ranges[2], key_ranges[3])});
|
||||
ColumnFamilyHandle* cfh = dbfull()->DefaultColumnFamily();
|
||||
std::unique_ptr<MultiScan> iter =
|
||||
dbfull()->NewMultiScan(ro, cfh, scan_options);
|
||||
try {
|
||||
int idx = 0;
|
||||
int count = 0;
|
||||
for (auto range : *iter) {
|
||||
for (auto it : range) {
|
||||
ASSERT_GE(it.first.ToString().compare(key_ranges[idx]), 0);
|
||||
ASSERT_LT(it.first.ToString().compare(key_ranges[idx + 1]), 0);
|
||||
count++;
|
||||
}
|
||||
idx += 2;
|
||||
}
|
||||
ASSERT_EQ(count, 32);
|
||||
} catch (MultiScanException& ex) {
|
||||
// Make sure exception contains the status
|
||||
ASSERT_NOK(ex.status());
|
||||
std::cerr << "Iterator returned status " << ex.what();
|
||||
abort();
|
||||
} catch (std::logic_error& ex) {
|
||||
std::cerr << "Iterator returned logic error " << ex.what();
|
||||
abort();
|
||||
}
|
||||
iter.reset();
|
||||
|
||||
// Test the overlapping scan case
|
||||
key_ranges[1] = "k30";
|
||||
scan_options[0] = ScanOptions(key_ranges[0], key_ranges[1]);
|
||||
iter = dbfull()->NewMultiScan(ro, cfh, scan_options);
|
||||
try {
|
||||
int idx = 0;
|
||||
int count = 0;
|
||||
for (auto range : *iter) {
|
||||
for (auto it : range) {
|
||||
ASSERT_GE(it.first.ToString().compare(key_ranges[idx]), 0);
|
||||
ASSERT_LT(it.first.ToString().compare(key_ranges[idx + 1]), 0);
|
||||
count++;
|
||||
}
|
||||
idx += 2;
|
||||
}
|
||||
ASSERT_EQ(count, 52);
|
||||
} catch (MultiScanException& ex) {
|
||||
// Make sure exception contains the status
|
||||
ASSERT_NOK(ex.status());
|
||||
std::cerr << "Iterator returned status " << ex.what();
|
||||
abort();
|
||||
} catch (std::logic_error& ex) {
|
||||
std::cerr << "Iterator returned logic error " << ex.what();
|
||||
abort();
|
||||
}
|
||||
iter.reset();
|
||||
|
||||
// Test the no limit scan case
|
||||
scan_options[0] = ScanOptions(key_ranges[0]);
|
||||
scan_options[1] = ScanOptions(key_ranges[2]);
|
||||
iter = dbfull()->NewMultiScan(ro, cfh, scan_options);
|
||||
try {
|
||||
int idx = 0;
|
||||
int count = 0;
|
||||
for (auto range : *iter) {
|
||||
for (auto it : range) {
|
||||
ASSERT_GE(it.first.ToString().compare(key_ranges[idx]), 0);
|
||||
if (it.first.ToString().compare(key_ranges[idx + 1]) == 0) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
idx += 2;
|
||||
}
|
||||
ASSERT_EQ(count, 52);
|
||||
} catch (MultiScanException& ex) {
|
||||
// Make sure exception contains the status
|
||||
ASSERT_NOK(ex.status());
|
||||
std::cerr << "Iterator returned status " << ex.what();
|
||||
abort();
|
||||
} catch (std::logic_error& ex) {
|
||||
std::cerr << "Iterator returned logic error " << ex.what();
|
||||
abort();
|
||||
}
|
||||
iter.reset();
|
||||
}
|
||||
|
||||
TEST_F(DBMultiScanIteratorTest, MixedBoundsTest) {
|
||||
// Create a file
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
std::stringstream ss;
|
||||
ss << std::setw(2) << std::setfill('0') << i;
|
||||
ASSERT_OK(Put("k" + ss.str(), "val" + ss.str()));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::vector<std::string> key_ranges(
|
||||
{"k03", "k10", "k25", "k50", "k75", "k90"});
|
||||
ReadOptions ro;
|
||||
std::vector<ScanOptions> scan_options(
|
||||
{ScanOptions(key_ranges[0], key_ranges[1]), ScanOptions(key_ranges[2]),
|
||||
ScanOptions(key_ranges[4], key_ranges[5])});
|
||||
ColumnFamilyHandle* cfh = dbfull()->DefaultColumnFamily();
|
||||
std::unique_ptr<MultiScan> iter =
|
||||
dbfull()->NewMultiScan(ro, cfh, scan_options);
|
||||
try {
|
||||
int idx = 0;
|
||||
int count = 0;
|
||||
for (auto range : *iter) {
|
||||
for (auto it : range) {
|
||||
ASSERT_GE(it.first.ToString().compare(
|
||||
scan_options[idx].range.start->ToString()),
|
||||
0);
|
||||
if (scan_options[idx].range.limit) {
|
||||
ASSERT_LT(it.first.ToString().compare(
|
||||
scan_options[idx].range.limit->ToString()),
|
||||
0);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
ASSERT_EQ(count, 97);
|
||||
} catch (MultiScanException& ex) {
|
||||
// Make sure exception contains the status
|
||||
ASSERT_NOK(ex.status());
|
||||
std::cerr << "Iterator returned status " << ex.what();
|
||||
abort();
|
||||
} catch (std::logic_error& ex) {
|
||||
std::cerr << "Iterator returned logic error " << ex.what();
|
||||
abort();
|
||||
}
|
||||
iter.reset();
|
||||
|
||||
scan_options[0] = ScanOptions(key_ranges[0]);
|
||||
scan_options[1] = ScanOptions(key_ranges[2], key_ranges[3]);
|
||||
scan_options[2] = ScanOptions(key_ranges[4]);
|
||||
iter = dbfull()->NewMultiScan(ro, cfh, scan_options);
|
||||
try {
|
||||
int idx = 0;
|
||||
int count = 0;
|
||||
for (auto range : *iter) {
|
||||
for (auto it : range) {
|
||||
ASSERT_GE(it.first.ToString().compare(
|
||||
scan_options[idx].range.start->ToString()),
|
||||
0);
|
||||
if (scan_options[idx].range.limit) {
|
||||
ASSERT_LT(it.first.ToString().compare(
|
||||
scan_options[idx].range.limit->ToString()),
|
||||
0);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
ASSERT_EQ(count, 147);
|
||||
} catch (MultiScanException& ex) {
|
||||
// Make sure exception contains the status
|
||||
ASSERT_NOK(ex.status());
|
||||
std::cerr << "Iterator returned status " << ex.what();
|
||||
abort();
|
||||
} catch (std::logic_error& ex) {
|
||||
std::cerr << "Iterator returned logic error " << ex.what();
|
||||
abort();
|
||||
}
|
||||
iter.reset();
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -424,6 +424,96 @@ TEST_F(DBMemTableTest, IntegrityChecks) {
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBMemTableTest, VectorConcurrentInsert) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.create_missing_column_families = true;
|
||||
options.allow_concurrent_memtable_write = true;
|
||||
options.memtable_factory.reset(new VectorRepFactory());
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"cf1"}, options);
|
||||
|
||||
// Multi-threaded writes
|
||||
{
|
||||
WriteOptions write_options;
|
||||
std::vector<port::Thread> threads;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
threads.emplace_back([&, i]() {
|
||||
int start = i * 100;
|
||||
int end = start + 100;
|
||||
WriteBatch batch;
|
||||
for (int j = start; j < end; ++j) {
|
||||
ASSERT_OK(
|
||||
batch.Put(handles_[0], Key(j), "value" + std::to_string(j)));
|
||||
}
|
||||
ASSERT_OK(db_->Write(write_options, &batch));
|
||||
});
|
||||
}
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
std::unique_ptr<Iterator> iter(
|
||||
db_->NewIterator(ReadOptions(), handles_[0]));
|
||||
iter->SeekToFirst();
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), Key(i));
|
||||
ASSERT_EQ(iter->value().ToString(), "value" + std::to_string(i));
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// Multi-threaded writes, multi CF
|
||||
{
|
||||
WriteOptions write_options;
|
||||
std::vector<port::Thread> threads;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
threads.emplace_back([&, i]() {
|
||||
int start = i * 100;
|
||||
int end = start + 100;
|
||||
WriteBatch batch;
|
||||
for (int j = start; j < end; ++j) {
|
||||
ASSERT_OK(batch.Put(handles_[0], Key(j), "CF0" + std::to_string(j)));
|
||||
ASSERT_OK(batch.Put(handles_[1], Key(j), "CF1" + std::to_string(j)));
|
||||
}
|
||||
ASSERT_OK(db_->Write(write_options, &batch));
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
std::unique_ptr<Iterator> iter0(
|
||||
db_->NewIterator(ReadOptions(), handles_[0]));
|
||||
std::unique_ptr<Iterator> iter1(
|
||||
db_->NewIterator(ReadOptions(), handles_[1]));
|
||||
iter0->SeekToFirst();
|
||||
iter1->SeekToFirst();
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
ASSERT_TRUE(iter0->Valid());
|
||||
ASSERT_EQ(iter0->key().ToString(), Key(i));
|
||||
ASSERT_EQ(iter0->value().ToString(), "CF0" + std::to_string(i));
|
||||
iter0->Next();
|
||||
|
||||
ASSERT_TRUE(iter1->Valid());
|
||||
ASSERT_EQ(iter1->key().ToString(), Key(i));
|
||||
ASSERT_EQ(iter1->value().ToString(), "CF1" + std::to_string(i));
|
||||
iter1->Next();
|
||||
}
|
||||
ASSERT_FALSE(iter0->Valid());
|
||||
ASSERT_OK(iter0->status());
|
||||
ASSERT_FALSE(iter1->Valid());
|
||||
ASSERT_OK(iter1->status());
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush(0));
|
||||
ASSERT_OK(Flush(1));
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -377,6 +377,8 @@ TEST_F(DBPropertiesTest, AggregatedTableProperties) {
|
||||
NewBloomFilterPolicy(kBloomBitsPerKey, false));
|
||||
table_options.block_size = 1024;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
// The checks assume kTableCount number of files
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -567,7 +569,7 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
options.target_file_size_base = 8192;
|
||||
options.max_bytes_for_level_base = 10000;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
// This ensures there no compaction happening when we call GetProperty().
|
||||
// The checks assume kTableCount number of files
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
|
||||
|
||||
+3
-40
@@ -605,6 +605,9 @@ class TraceFileEnv : public EnvWrapper {
|
||||
char* scratch) const override {
|
||||
return target_->Read(offset, n, result, scratch);
|
||||
}
|
||||
Status GetFileSize(uint64_t* file_size) override {
|
||||
return target_->GetFileSize(file_size);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<RandomAccessFile> target_;
|
||||
@@ -1291,46 +1294,6 @@ TEST_F(DBSecondaryTest, CatchUpAfterFlush) {
|
||||
ASSERT_OK(iter3->status());
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, CheckConsistencyWhenOpen) {
|
||||
bool called = false;
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
Reopen(options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImplSecondary::CheckConsistency:AfterFirstAttempt", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
called = true;
|
||||
auto* s = static_cast<Status*>(arg);
|
||||
ASSERT_NOK(*s);
|
||||
});
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CheckConsistency:AfterGetLiveFilesMetaData",
|
||||
"BackgroundCallCompaction:0"},
|
||||
{"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles",
|
||||
"DBImpl::CheckConsistency:BeforeGetFileSize"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("a", "value0"));
|
||||
ASSERT_OK(Put("c", "value0"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("b", "value1"));
|
||||
ASSERT_OK(Put("d", "value1"));
|
||||
ASSERT_OK(Flush());
|
||||
port::Thread thread([this]() {
|
||||
Options opts;
|
||||
opts.env = env_;
|
||||
opts.max_open_files = -1;
|
||||
OpenSecondary(opts);
|
||||
});
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
thread.join();
|
||||
ASSERT_TRUE(called);
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, StartFromInconsistent) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -135,21 +135,6 @@ TEST_F(DBSSTTest, SSTsWithLdbSuffixHandling) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
// Check that we don't crash when opening DB with
|
||||
// DBOptions::skip_checking_sst_file_sizes_on_db_open = true.
|
||||
TEST_F(DBSSTTest, SkipCheckingSSTFileSizesOnDBOpen) {
|
||||
ASSERT_OK(Put("pika", "choo"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Just open the DB with the option set to true and check that we don't crash.
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.skip_checking_sst_file_sizes_on_db_open = true;
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_EQ("choo", Get("pika"));
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, DontDeleteMovedFile) {
|
||||
// This test triggers move compaction and verifies that the file is not
|
||||
// deleted when it's part of move compaction
|
||||
@@ -1748,45 +1733,6 @@ TEST_F(DBSSTTest, GetTotalSstFilesSize) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, OpenDBWithoutGetFileSizeInvocations) {
|
||||
Options options = CurrentOptions();
|
||||
std::unique_ptr<MockEnv> env{MockEnv::Create(Env::Default())};
|
||||
options.env = env.get();
|
||||
options.disable_auto_compactions = true;
|
||||
options.compression = kNoCompression;
|
||||
options.enable_blob_files = true;
|
||||
options.blob_file_size = 32; // create one blob per file
|
||||
options.skip_checking_sst_file_sizes_on_db_open = true;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// Generate 5 files in L0
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
std::string val = "val_file_" + std::to_string(i);
|
||||
ASSERT_OK(Put(Key(j), val));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
Close();
|
||||
|
||||
bool is_get_file_size_called = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"MockFileSystem::GetFileSize:CheckFileType", [&](void* arg) {
|
||||
std::string* filename = static_cast<std::string*>(arg);
|
||||
if (filename->find(".blob") != std::string::npos) {
|
||||
is_get_file_size_called = true;
|
||||
}
|
||||
});
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_FALSE(is_get_file_size_called);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, GetTotalSstFilesSizeVersionsFilesShared) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
+117
-295
@@ -144,6 +144,121 @@ TEST_F(DBTest, MockEnvTest) {
|
||||
delete db;
|
||||
}
|
||||
|
||||
TEST_F(DBTest, RequestIdPlumbingTest) {
|
||||
// test that request_id is passed to the filesystem, from
|
||||
// ReadOptions to IODebugContext
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
|
||||
// Create a mock environment to capture IODebugContext during reads
|
||||
IODebugContext dbgCopy;
|
||||
const std::string* captured_request_id_dbg;
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read:IODebugContext", [&](void* arg) {
|
||||
IODebugContext* dbg = static_cast<IODebugContext*>(arg);
|
||||
if (dbg == nullptr) {
|
||||
captured_request_id_dbg = nullptr;
|
||||
} else {
|
||||
captured_request_id_dbg = dbg->request_id;
|
||||
// Test IODebugContext assignment operator
|
||||
dbgCopy = *dbg;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// test request_id plumbing during a get
|
||||
{
|
||||
const std::string test_request_id = "test_request_id_123";
|
||||
ReadOptions read_opts;
|
||||
read_opts.request_id = &test_request_id;
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(read_opts, "k1", &value));
|
||||
|
||||
// Verify the request_id was propagated to the file system
|
||||
ASSERT_NE(captured_request_id_dbg, nullptr);
|
||||
ASSERT_EQ(*captured_request_id_dbg, test_request_id);
|
||||
|
||||
ASSERT_NE(dbgCopy.request_id, nullptr);
|
||||
ASSERT_NE(dbgCopy.request_id, captured_request_id_dbg);
|
||||
ASSERT_EQ(*dbgCopy.request_id, test_request_id);
|
||||
}
|
||||
|
||||
captured_request_id_dbg = nullptr;
|
||||
|
||||
// test request_id plumbing during iterator seek
|
||||
ASSERT_OK(Put("k2", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
{
|
||||
ReadOptions read_opts;
|
||||
const std::string request_id = "test_request_id_456";
|
||||
read_opts.request_id = &request_id;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
iter->Seek("k2");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
|
||||
// Verify the request_id was propagated to the file system
|
||||
ASSERT_NE(captured_request_id_dbg, nullptr);
|
||||
ASSERT_EQ(*captured_request_id_dbg, request_id);
|
||||
|
||||
ASSERT_NE(dbgCopy.request_id, nullptr);
|
||||
ASSERT_NE(dbgCopy.request_id, captured_request_id_dbg);
|
||||
ASSERT_EQ(*dbgCopy.request_id, request_id);
|
||||
|
||||
// Test IODebugContext copy constructor
|
||||
IODebugContext dbgCopy2(dbgCopy);
|
||||
ASSERT_NE(dbgCopy2.request_id, nullptr);
|
||||
ASSERT_NE(dbgCopy2.request_id, captured_request_id_dbg);
|
||||
ASSERT_NE(dbgCopy2.request_id, dbgCopy.request_id);
|
||||
ASSERT_EQ(*dbgCopy2.request_id, request_id);
|
||||
}
|
||||
|
||||
// test request_id plumbing during multiget
|
||||
captured_request_id_dbg = nullptr;
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::MultiRead:IODebugContext", [&](void* arg) {
|
||||
IODebugContext* dbg = static_cast<IODebugContext*>(arg);
|
||||
if (dbg == nullptr) {
|
||||
captured_request_id_dbg = nullptr;
|
||||
} else {
|
||||
captured_request_id_dbg = dbg->request_id;
|
||||
}
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("k3", "v3"));
|
||||
ASSERT_OK(Put("k4", "v4"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
{
|
||||
ReadOptions read_opts;
|
||||
const std::string multiget_request_id = "test_request_id_789";
|
||||
read_opts.request_id = &multiget_request_id;
|
||||
|
||||
std::vector<std::string> values;
|
||||
std::vector<Slice> keys = {Slice("k3"), Slice("k4")};
|
||||
|
||||
values.resize(keys.size());
|
||||
|
||||
std::vector<ColumnFamilyHandle*> cfhs(keys.size(),
|
||||
db_->DefaultColumnFamily());
|
||||
db_->MultiGet(read_opts, cfhs, keys, &values);
|
||||
|
||||
ASSERT_NE(captured_request_id_dbg, nullptr);
|
||||
ASSERT_EQ(*captured_request_id_dbg, multiget_request_id);
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBTest, MemEnvTest) {
|
||||
std::unique_ptr<Env> env{NewMemEnv(Env::Default())};
|
||||
Options options;
|
||||
@@ -1163,12 +1278,6 @@ class DelayFilterFactory : public CompactionFilterFactory {
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
static std::string CompressibleString(Random* rnd, int len) {
|
||||
std::string r;
|
||||
test::CompressibleString(rnd, 0.8, len, &r);
|
||||
return r;
|
||||
}
|
||||
|
||||
TEST_F(DBTest, FailMoreDbPaths) {
|
||||
Options options = CurrentOptions();
|
||||
options.db_paths.emplace_back(dbname_, 10000000);
|
||||
@@ -5292,272 +5401,6 @@ TEST_F(DBTest, FlushOnDestroy) {
|
||||
CancelAllBackgroundWork(db_);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
if (!Snappy_Supported()) {
|
||||
return;
|
||||
}
|
||||
const int kNKeys = 120;
|
||||
int keys[kNKeys];
|
||||
for (int i = 0; i < kNKeys; i++) {
|
||||
keys[i] = i;
|
||||
}
|
||||
|
||||
Random rnd(301);
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.db_write_buffer_size = 20480;
|
||||
options.write_buffer_size = 20480;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
options.level0_stop_writes_trigger = 2;
|
||||
options.target_file_size_base = 20480;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.max_bytes_for_level_base = 102400;
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
options.max_background_compactions = 1;
|
||||
options.num_levels = 5;
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
options.compression_per_level.resize(3);
|
||||
// No compression for L0
|
||||
options.compression_per_level[0] = kNoCompression;
|
||||
// No compression for the Ln whre L0 is compacted to
|
||||
options.compression_per_level[1] = kNoCompression;
|
||||
// Snappy compression for Ln+1
|
||||
options.compression_per_level[2] = kSnappyCompression;
|
||||
|
||||
OnFileDeletionListener* listener = new OnFileDeletionListener();
|
||||
options.listeners.emplace_back(listener);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Insert more than 80K. L4 should be base level. Neither L0 nor L4 should
|
||||
// be compressed, so there shouldn't be any compression.
|
||||
for (int i = 0; i < 20; i++) {
|
||||
ASSERT_OK(Put(Key(keys[i]), CompressibleString(&rnd, 4000)));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(3), 0);
|
||||
ASSERT_TRUE(NumTableFilesAtLevel(0) > 0 || NumTableFilesAtLevel(4) > 0);
|
||||
|
||||
// Verify there was no compression
|
||||
auto num_block_compressed =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
|
||||
ASSERT_EQ(num_block_compressed, 0);
|
||||
|
||||
// Insert 400KB and there will be some files end up in L3. According to the
|
||||
// above compression settings for each level, there will be some compression.
|
||||
ASSERT_OK(options.statistics->Reset());
|
||||
ASSERT_EQ(num_block_compressed, 0);
|
||||
for (int i = 20; i < 120; i++) {
|
||||
ASSERT_OK(Put(Key(keys[i]), CompressibleString(&rnd, 4000)));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
|
||||
ASSERT_GE(NumTableFilesAtLevel(3), 1);
|
||||
ASSERT_GE(NumTableFilesAtLevel(4), 1);
|
||||
|
||||
// Verify there was compression
|
||||
num_block_compressed =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
|
||||
ASSERT_GT(num_block_compressed, 0);
|
||||
|
||||
// Make sure data in files in L3 is not compacted by removing all files
|
||||
// in L4 and calculate number of rows
|
||||
ASSERT_OK(dbfull()->SetOptions({
|
||||
{"disable_auto_compactions", "true"},
|
||||
}));
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
db_->GetColumnFamilyMetaData(&cf_meta);
|
||||
|
||||
// Ensure that L1+ files are non-overlapping and together with L0 encompass
|
||||
// full key range between smallestkey and largestkey from CF file metadata.
|
||||
int largestkey_in_prev_level = -1;
|
||||
int keys_found = 0;
|
||||
for (int level = (int)cf_meta.levels.size() - 1; level >= 0; level--) {
|
||||
int files_in_level = (int)cf_meta.levels[level].files.size();
|
||||
int largestkey_in_prev_file = -1;
|
||||
for (int j = 0; j < files_in_level; j++) {
|
||||
int smallestkey = IdFromKey(cf_meta.levels[level].files[j].smallestkey);
|
||||
int largestkey = IdFromKey(cf_meta.levels[level].files[j].largestkey);
|
||||
int num_entries = (int)cf_meta.levels[level].files[j].num_entries;
|
||||
ASSERT_EQ(num_entries, largestkey - smallestkey + 1);
|
||||
keys_found += num_entries;
|
||||
if (level > 0) {
|
||||
if (j == 0) {
|
||||
ASSERT_GT(smallestkey, largestkey_in_prev_level);
|
||||
}
|
||||
if (j > 0) {
|
||||
ASSERT_GT(smallestkey, largestkey_in_prev_file);
|
||||
}
|
||||
if (j == files_in_level - 1) {
|
||||
largestkey_in_prev_level = largestkey;
|
||||
}
|
||||
}
|
||||
largestkey_in_prev_file = largestkey;
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(keys_found, kNKeys);
|
||||
|
||||
for (const auto& file : cf_meta.levels[4].files) {
|
||||
listener->SetExpectedFileName(dbname_ + file.name);
|
||||
const RangeOpt ranges(file.smallestkey, file.largestkey);
|
||||
// Given verification from above, we're guaranteed that by deleting all the
|
||||
// files in [<smallestkey>, <largestkey>] range, we're effectively deleting
|
||||
// that very single file and nothing more.
|
||||
EXPECT_OK(dbfull()->DeleteFilesInRanges(dbfull()->DefaultColumnFamily(),
|
||||
&ranges, true /* include_end */));
|
||||
}
|
||||
listener->VerifyMatchedCount(cf_meta.levels[4].files.size());
|
||||
|
||||
int num_keys = 0;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
num_keys++;
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
|
||||
ASSERT_GE(NumTableFilesAtLevel(3), 1);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(4), 0);
|
||||
|
||||
ASSERT_GT(SizeAtLevel(0) + SizeAtLevel(3), num_keys * 4000U + num_keys * 10U);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
if (!Snappy_Supported() || !LZ4_Supported() || !Zlib_Supported()) {
|
||||
return;
|
||||
}
|
||||
const int kNKeys = 500;
|
||||
int keys[kNKeys];
|
||||
for (int i = 0; i < kNKeys; i++) {
|
||||
keys[i] = i;
|
||||
}
|
||||
RandomShuffle(std::begin(keys), std::end(keys));
|
||||
|
||||
Random rnd(301);
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.db_write_buffer_size = 6000000;
|
||||
options.write_buffer_size = 600000;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
options.level0_stop_writes_trigger = 2;
|
||||
options.soft_pending_compaction_bytes_limit = 1024 * 1024;
|
||||
options.target_file_size_base = 20;
|
||||
options.env = env_;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.max_bytes_for_level_base = 200;
|
||||
options.max_bytes_for_level_multiplier = 8;
|
||||
options.max_background_compactions = 1;
|
||||
options.num_levels = 5;
|
||||
options.compaction_verify_record_count = false;
|
||||
std::shared_ptr<mock::MockTableFactory> mtf(new mock::MockTableFactory);
|
||||
options.table_factory = mtf;
|
||||
|
||||
options.compression_per_level.resize(3);
|
||||
options.compression_per_level[0] = kNoCompression;
|
||||
options.compression_per_level[1] = kLZ4Compression;
|
||||
options.compression_per_level[2] = kZlibCompression;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// When base level is L4, L4 is LZ4.
|
||||
std::atomic<int> num_zlib(0);
|
||||
std::atomic<int> num_lz4(0);
|
||||
std::atomic<int> num_no(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
if (compaction->output_level() == 4) {
|
||||
ASSERT_TRUE(compaction->output_compression() == kLZ4Compression);
|
||||
num_lz4.fetch_add(1);
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:output_compression", [&](void* arg) {
|
||||
auto* compression = static_cast<CompressionType*>(arg);
|
||||
ASSERT_TRUE(*compression == kNoCompression);
|
||||
num_no.fetch_add(1);
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
std::string value = rnd.RandomString(200);
|
||||
ASSERT_OK(Put(Key(keys[i]), value));
|
||||
if (i % 25 == 24) {
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(3), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(4), 0);
|
||||
ASSERT_GT(num_no.load(), 2);
|
||||
ASSERT_GT(num_lz4.load(), 0);
|
||||
int prev_num_files_l4 = NumTableFilesAtLevel(4);
|
||||
|
||||
// After base level turn L4->L3, L3 becomes LZ4 and L4 becomes Zlib
|
||||
num_lz4.store(0);
|
||||
num_no.store(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
if (compaction->output_level() == 4 && compaction->start_level() == 3) {
|
||||
ASSERT_TRUE(compaction->output_compression() == kZlibCompression);
|
||||
num_zlib.fetch_add(1);
|
||||
} else {
|
||||
ASSERT_TRUE(compaction->output_compression() == kLZ4Compression);
|
||||
num_lz4.fetch_add(1);
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:output_compression", [&](void* arg) {
|
||||
auto* compression = static_cast<CompressionType*>(arg);
|
||||
ASSERT_TRUE(*compression == kNoCompression);
|
||||
num_no.fetch_add(1);
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
for (int i = 101; i < 500; i++) {
|
||||
std::string value = rnd.RandomString(200);
|
||||
ASSERT_OK(Put(Key(keys[i]), value));
|
||||
if (i % 100 == 99) {
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(3), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(4), prev_num_files_l4);
|
||||
ASSERT_GT(num_no.load(), 2);
|
||||
ASSERT_GT(num_lz4.load(), 0);
|
||||
ASSERT_GT(num_zlib.load(), 0);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, DynamicCompactionOptions) {
|
||||
// minimum write buffer size is enforced at 64KB
|
||||
const uint64_t k32KB = 1 << 15;
|
||||
@@ -6095,8 +5938,8 @@ TEST_F(DBTest, L0L1L2AndUpHitCounter) {
|
||||
}
|
||||
|
||||
TEST_F(DBTest, EncodeDecompressedBlockSizeTest) {
|
||||
bool& allow_unsupported_fv =
|
||||
BlockBasedTableFactory::AllowUnsupportedFormatVersion();
|
||||
// Allow testing format_version=1
|
||||
bool& allow_unsupported_fv = TEST_AllowUnsupportedFormatVersion();
|
||||
SaveAndRestore guard(&allow_unsupported_fv);
|
||||
ASSERT_FALSE(allow_unsupported_fv);
|
||||
|
||||
@@ -7235,27 +7078,6 @@ TEST_F(DBTest, LastWriteBufferDelay) {
|
||||
}
|
||||
#endif // !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
|
||||
|
||||
TEST_F(DBTest, FailWhenCompressionNotSupportedTest) {
|
||||
CompressionType compressions[] = {kZlibCompression, kBZip2Compression,
|
||||
kLZ4Compression, kLZ4HCCompression,
|
||||
kXpressCompression};
|
||||
for (auto comp : compressions) {
|
||||
if (!CompressionTypeSupported(comp)) {
|
||||
// not supported, we should fail the Open()
|
||||
Options options = CurrentOptions();
|
||||
options.compression = comp;
|
||||
ASSERT_TRUE(!TryReopen(options).ok());
|
||||
// Try if CreateColumnFamily also fails
|
||||
options.compression = kNoCompression;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
cf_options.compression = comp;
|
||||
ColumnFamilyHandle* handle;
|
||||
ASSERT_TRUE(!db_->CreateColumnFamily(cf_options, "name", &handle).ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest, CreateColumnFamilyShouldFailOnIncompatibleOptions) {
|
||||
Options options = CurrentOptions();
|
||||
options.max_open_files = 100;
|
||||
|
||||
+99
-701
@@ -10,7 +10,6 @@
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
@@ -1186,705 +1185,6 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
ASSERT_EQ(index, keys_cf.size());
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, PresetCompressionDict) {
|
||||
// Verifies that compression ratio improves when dictionary is enabled, and
|
||||
// improves even further when the dictionary is trained by ZSTD.
|
||||
const size_t kBlockSizeBytes = 4 << 10;
|
||||
const size_t kL0FileBytes = 128 << 10;
|
||||
const size_t kApproxPerBlockOverheadBytes = 50;
|
||||
const int kNumL0Files = 5;
|
||||
|
||||
Options options;
|
||||
// Make sure to use any custom env that the test is configured with.
|
||||
options.env = CurrentOptions().env;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
options.arena_block_size = kBlockSizeBytes;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kL0FileBytes / kBlockSizeBytes));
|
||||
options.num_levels = 2;
|
||||
options.target_file_size_base = kL0FileBytes;
|
||||
options.target_file_size_multiplier = 2;
|
||||
options.write_buffer_size = kL0FileBytes;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = kBlockSizeBytes;
|
||||
std::vector<CompressionType> compression_types;
|
||||
if (Zlib_Supported()) {
|
||||
compression_types.push_back(kZlibCompression);
|
||||
}
|
||||
#if LZ4_VERSION_NUMBER >= 10400 // r124+
|
||||
compression_types.push_back(kLZ4Compression);
|
||||
compression_types.push_back(kLZ4HCCompression);
|
||||
#endif // LZ4_VERSION_NUMBER >= 10400
|
||||
if (ZSTD_Supported()) {
|
||||
compression_types.push_back(kZSTD);
|
||||
}
|
||||
|
||||
enum DictionaryTypes : int {
|
||||
kWithoutDict,
|
||||
kWithDict,
|
||||
kWithZSTDfinalizeDict,
|
||||
kWithZSTDTrainedDict,
|
||||
kDictEnd,
|
||||
};
|
||||
|
||||
for (auto compression_type : compression_types) {
|
||||
options.compression = compression_type;
|
||||
size_t bytes_without_dict = 0;
|
||||
size_t bytes_with_dict = 0;
|
||||
size_t bytes_with_zstd_finalize_dict = 0;
|
||||
size_t bytes_with_zstd_trained_dict = 0;
|
||||
for (int i = kWithoutDict; i < kDictEnd; i++) {
|
||||
// First iteration: compress without preset dictionary
|
||||
// Second iteration: compress with preset dictionary
|
||||
// Third iteration (zstd only): compress with zstd-trained dictionary
|
||||
//
|
||||
// To make sure the compression dictionary has the intended effect, we
|
||||
// verify the compressed size is smaller in successive iterations. Also in
|
||||
// the non-first iterations, verify the data we get out is the same data
|
||||
// we put in.
|
||||
switch (i) {
|
||||
case kWithoutDict:
|
||||
options.compression_opts.max_dict_bytes = 0;
|
||||
options.compression_opts.zstd_max_train_bytes = 0;
|
||||
break;
|
||||
case kWithDict:
|
||||
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
|
||||
options.compression_opts.zstd_max_train_bytes = 0;
|
||||
break;
|
||||
case kWithZSTDfinalizeDict:
|
||||
if (compression_type != kZSTD ||
|
||||
!ZSTD_FinalizeDictionarySupported()) {
|
||||
continue;
|
||||
}
|
||||
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
|
||||
options.compression_opts.zstd_max_train_bytes = kL0FileBytes;
|
||||
options.compression_opts.use_zstd_dict_trainer = false;
|
||||
break;
|
||||
case kWithZSTDTrainedDict:
|
||||
if (compression_type != kZSTD || !ZSTD_TrainDictionarySupported()) {
|
||||
continue;
|
||||
}
|
||||
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
|
||||
options.compression_opts.zstd_max_train_bytes = kL0FileBytes;
|
||||
options.compression_opts.use_zstd_dict_trainer = true;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Random rnd(301);
|
||||
std::string seq_datas[10];
|
||||
for (int j = 0; j < 10; ++j) {
|
||||
seq_datas[j] =
|
||||
rnd.RandomString(kBlockSizeBytes - kApproxPerBlockOverheadBytes);
|
||||
}
|
||||
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0, 1));
|
||||
for (int j = 0; j < kNumL0Files; ++j) {
|
||||
for (size_t k = 0; k < kL0FileBytes / kBlockSizeBytes + 1; ++k) {
|
||||
auto key_num = j * (kL0FileBytes / kBlockSizeBytes) + k;
|
||||
ASSERT_OK(Put(1, Key(static_cast<int>(key_num)),
|
||||
seq_datas[(key_num / 10) % 10]));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[1]));
|
||||
ASSERT_EQ(j + 1, NumTableFilesAtLevel(0, 1));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1],
|
||||
true /* disallow_trivial_move */));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0, 1));
|
||||
ASSERT_GT(NumTableFilesAtLevel(1, 1), 0);
|
||||
|
||||
// Get the live sst files size
|
||||
size_t total_sst_bytes = TotalSize(1);
|
||||
if (i == kWithoutDict) {
|
||||
bytes_without_dict = total_sst_bytes;
|
||||
} else if (i == kWithDict) {
|
||||
bytes_with_dict = total_sst_bytes;
|
||||
} else if (i == kWithZSTDfinalizeDict) {
|
||||
bytes_with_zstd_finalize_dict = total_sst_bytes;
|
||||
} else if (i == kWithZSTDTrainedDict) {
|
||||
bytes_with_zstd_trained_dict = total_sst_bytes;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < kNumL0Files * (kL0FileBytes / kBlockSizeBytes);
|
||||
j++) {
|
||||
ASSERT_EQ(seq_datas[(j / 10) % 10], Get(1, Key(static_cast<int>(j))));
|
||||
}
|
||||
if (i == kWithDict) {
|
||||
ASSERT_GT(bytes_without_dict, bytes_with_dict);
|
||||
} else if (i == kWithZSTDTrainedDict) {
|
||||
// In zstd compression, it is sometimes possible that using a finalized
|
||||
// dictionary does not get as good a compression ratio as raw content
|
||||
// dictionary. But using a dictionary should always get better
|
||||
// compression ratio than not using one.
|
||||
ASSERT_TRUE(bytes_with_dict > bytes_with_zstd_finalize_dict ||
|
||||
bytes_without_dict > bytes_with_zstd_finalize_dict);
|
||||
} else if (i == kWithZSTDTrainedDict) {
|
||||
// In zstd compression, it is sometimes possible that using a trained
|
||||
// dictionary does not get as good a compression ratio as without
|
||||
// training.
|
||||
// But using a dictionary (with or without training) should always get
|
||||
// better compression ratio than not using one.
|
||||
ASSERT_TRUE(bytes_with_dict > bytes_with_zstd_trained_dict ||
|
||||
bytes_without_dict > bytes_with_zstd_trained_dict);
|
||||
}
|
||||
|
||||
DestroyAndReopen(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, PresetCompressionDictLocality) {
|
||||
if (!ZSTD_Supported()) {
|
||||
return;
|
||||
}
|
||||
// Verifies that compression dictionary is generated from local data. The
|
||||
// verification simply checks all output SSTs have different compression
|
||||
// dictionaries. We do not verify effectiveness as that'd likely be flaky in
|
||||
// the future.
|
||||
const int kNumEntriesPerFile = 1 << 10; // 1KB
|
||||
const int kNumBytesPerEntry = 1 << 10; // 1KB
|
||||
const int kNumFiles = 4;
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kZSTD;
|
||||
options.compression_opts.max_dict_bytes = 1 << 14; // 16KB
|
||||
options.compression_opts.zstd_max_train_bytes = 1 << 18; // 256KB
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.target_file_size_base = kNumEntriesPerFile * kNumBytesPerEntry;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
for (int j = 0; j < kNumEntriesPerFile; ++j) {
|
||||
ASSERT_OK(Put(Key(i * kNumEntriesPerFile + j),
|
||||
rnd.RandomString(kNumBytesPerEntry)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), i + 1);
|
||||
}
|
||||
|
||||
// Store all the dictionaries generated during a full compaction.
|
||||
std::vector<std::string> compression_dicts;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::WriteCompressionDictBlock:RawDict",
|
||||
[&](void* arg) {
|
||||
compression_dicts.emplace_back(static_cast<Slice*>(arg)->ToString());
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
CompactRangeOptions compact_range_opts;
|
||||
compact_range_opts.bottommost_level_compaction =
|
||||
BottommostLevelCompaction::kForceOptimized;
|
||||
ASSERT_OK(db_->CompactRange(compact_range_opts, nullptr, nullptr));
|
||||
|
||||
// Dictionary compression should not be so good as to compress four totally
|
||||
// random files into one. If it does then there's probably something wrong
|
||||
// with the test.
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 1);
|
||||
|
||||
// Furthermore, there should be one compression dictionary generated per file.
|
||||
// And they should all be different from each other.
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1),
|
||||
static_cast<int>(compression_dicts.size()));
|
||||
for (size_t i = 1; i < compression_dicts.size(); ++i) {
|
||||
std::string& a = compression_dicts[i - 1];
|
||||
std::string& b = compression_dicts[i];
|
||||
size_t alen = a.size();
|
||||
size_t blen = b.size();
|
||||
ASSERT_TRUE(alen != blen || memcmp(a.data(), b.data(), alen) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
class PresetCompressionDictTest
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<CompressionType, bool>> {
|
||||
public:
|
||||
PresetCompressionDictTest()
|
||||
: DBTestBase("db_test2", false /* env_do_fsync */),
|
||||
compression_type_(std::get<0>(GetParam())),
|
||||
bottommost_(std::get<1>(GetParam())) {}
|
||||
|
||||
protected:
|
||||
const CompressionType compression_type_;
|
||||
const bool bottommost_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTest2, PresetCompressionDictTest,
|
||||
::testing::Combine(::testing::ValuesIn(GetSupportedDictCompressions()),
|
||||
::testing::Bool()));
|
||||
|
||||
TEST_P(PresetCompressionDictTest, Flush) {
|
||||
// Verifies that dictionary is generated and written during flush only when
|
||||
// `ColumnFamilyOptions::compression` enables dictionary. Also verifies the
|
||||
// size of the dictionary is within expectations according to the limit on
|
||||
// buffering set by `CompressionOptions::max_dict_buffer_bytes`.
|
||||
const size_t kValueLen = 256;
|
||||
const size_t kKeysPerFile = 1 << 10;
|
||||
const size_t kDictLen = 16 << 10;
|
||||
const size_t kBlockLen = 4 << 10;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
if (bottommost_) {
|
||||
options.bottommost_compression = compression_type_;
|
||||
options.bottommost_compression_opts.enabled = true;
|
||||
options.bottommost_compression_opts.max_dict_bytes = kDictLen;
|
||||
options.bottommost_compression_opts.max_dict_buffer_bytes = kBlockLen;
|
||||
} else {
|
||||
options.compression = compression_type_;
|
||||
options.compression_opts.max_dict_bytes = kDictLen;
|
||||
options.compression_opts.max_dict_buffer_bytes = kBlockLen;
|
||||
}
|
||||
options.memtable_factory.reset(test::NewSpecialSkipListFactory(kKeysPerFile));
|
||||
options.statistics = CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = kBlockLen;
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (size_t i = 0; i <= kKeysPerFile; ++i) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(i)), rnd.RandomString(kValueLen)));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
|
||||
// We can use `BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT` to detect whether a
|
||||
// compression dictionary exists since dictionaries would be preloaded when
|
||||
// the flush finishes.
|
||||
if (bottommost_) {
|
||||
// Flush is never considered bottommost. This should change in the future
|
||||
// since flushed files may have nothing underneath them, like the one in
|
||||
// this test case.
|
||||
ASSERT_EQ(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
0);
|
||||
} else {
|
||||
ASSERT_GT(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
0);
|
||||
// TODO(ajkr): fix the below assertion to work with ZSTD. The expectation on
|
||||
// number of bytes needs to be adjusted in case the cached block is in
|
||||
// ZSTD's digested dictionary format.
|
||||
if (compression_type_ != kZSTD) {
|
||||
// Although we limited buffering to `kBlockLen`, there may be up to two
|
||||
// blocks of data included in the dictionary since we only check limit
|
||||
// after each block is built.
|
||||
ASSERT_LE(TestGetTickerCount(options,
|
||||
BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
2 * kBlockLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(PresetCompressionDictTest, CompactNonBottommost) {
|
||||
// Verifies that dictionary is generated and written during compaction to
|
||||
// non-bottommost level only when `ColumnFamilyOptions::compression` enables
|
||||
// dictionary. Also verifies the size of the dictionary is within expectations
|
||||
// according to the limit on buffering set by
|
||||
// `CompressionOptions::max_dict_buffer_bytes`.
|
||||
const size_t kValueLen = 256;
|
||||
const size_t kKeysPerFile = 1 << 10;
|
||||
const size_t kDictLen = 16 << 10;
|
||||
const size_t kBlockLen = 4 << 10;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
if (bottommost_) {
|
||||
options.bottommost_compression = compression_type_;
|
||||
options.bottommost_compression_opts.enabled = true;
|
||||
options.bottommost_compression_opts.max_dict_bytes = kDictLen;
|
||||
options.bottommost_compression_opts.max_dict_buffer_bytes = kBlockLen;
|
||||
} else {
|
||||
options.compression = compression_type_;
|
||||
options.compression_opts.max_dict_bytes = kDictLen;
|
||||
options.compression_opts.max_dict_buffer_bytes = kBlockLen;
|
||||
}
|
||||
options.disable_auto_compactions = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = kBlockLen;
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (size_t j = 0; j <= kKeysPerFile; ++j) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(j)), rnd.RandomString(kValueLen)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (size_t j = 0; j <= kKeysPerFile; ++j) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(j)), rnd.RandomString(kValueLen)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ("2,0,1", FilesPerLevel(0));
|
||||
|
||||
uint64_t prev_compression_dict_bytes_inserted =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT);
|
||||
// This L0->L1 compaction merges the two L0 files into L1. The produced L1
|
||||
// file is not bottommost due to the existing L2 file covering the same key-
|
||||
// range.
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr));
|
||||
ASSERT_EQ("0,1,1", FilesPerLevel(0));
|
||||
// We can use `BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT` to detect whether a
|
||||
// compression dictionary exists since dictionaries would be preloaded when
|
||||
// the compaction finishes.
|
||||
if (bottommost_) {
|
||||
ASSERT_EQ(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
prev_compression_dict_bytes_inserted);
|
||||
} else {
|
||||
ASSERT_GT(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
prev_compression_dict_bytes_inserted);
|
||||
// TODO(ajkr): fix the below assertion to work with ZSTD. The expectation on
|
||||
// number of bytes needs to be adjusted in case the cached block is in
|
||||
// ZSTD's digested dictionary format.
|
||||
if (compression_type_ != kZSTD) {
|
||||
// Although we limited buffering to `kBlockLen`, there may be up to two
|
||||
// blocks of data included in the dictionary since we only check limit
|
||||
// after each block is built.
|
||||
ASSERT_LE(TestGetTickerCount(options,
|
||||
BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
prev_compression_dict_bytes_inserted + 2 * kBlockLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(PresetCompressionDictTest, CompactBottommost) {
|
||||
// Verifies that dictionary is generated and written during compaction to
|
||||
// non-bottommost level only when either `ColumnFamilyOptions::compression` or
|
||||
// `ColumnFamilyOptions::bottommost_compression` enables dictionary. Also
|
||||
// verifies the size of the dictionary is within expectations according to the
|
||||
// limit on buffering set by `CompressionOptions::max_dict_buffer_bytes`.
|
||||
const size_t kValueLen = 256;
|
||||
const size_t kKeysPerFile = 1 << 10;
|
||||
const size_t kDictLen = 16 << 10;
|
||||
const size_t kBlockLen = 4 << 10;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
if (bottommost_) {
|
||||
options.bottommost_compression = compression_type_;
|
||||
options.bottommost_compression_opts.enabled = true;
|
||||
options.bottommost_compression_opts.max_dict_bytes = kDictLen;
|
||||
options.bottommost_compression_opts.max_dict_buffer_bytes = kBlockLen;
|
||||
} else {
|
||||
options.compression = compression_type_;
|
||||
options.compression_opts.max_dict_bytes = kDictLen;
|
||||
options.compression_opts.max_dict_buffer_bytes = kBlockLen;
|
||||
}
|
||||
options.disable_auto_compactions = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = kBlockLen;
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (size_t j = 0; j <= kKeysPerFile; ++j) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(j)), rnd.RandomString(kValueLen)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ("2", FilesPerLevel(0));
|
||||
|
||||
uint64_t prev_compression_dict_bytes_inserted =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT);
|
||||
CompactRangeOptions cro;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
ASSERT_GT(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
prev_compression_dict_bytes_inserted);
|
||||
// TODO(ajkr): fix the below assertion to work with ZSTD. The expectation on
|
||||
// number of bytes needs to be adjusted in case the cached block is in ZSTD's
|
||||
// digested dictionary format.
|
||||
if (compression_type_ != kZSTD) {
|
||||
// Although we limited buffering to `kBlockLen`, there may be up to two
|
||||
// blocks of data included in the dictionary since we only check limit after
|
||||
// each block is built.
|
||||
ASSERT_LE(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
prev_compression_dict_bytes_inserted + 2 * kBlockLen);
|
||||
}
|
||||
}
|
||||
|
||||
class CompactionCompressionListener : public EventListener {
|
||||
public:
|
||||
explicit CompactionCompressionListener(Options* db_options)
|
||||
: db_options_(db_options) {}
|
||||
|
||||
void OnCompactionCompleted(DB* db, const CompactionJobInfo& ci) override {
|
||||
// Figure out last level with files
|
||||
int bottommost_level = 0;
|
||||
for (int level = 0; level < db->NumberLevels(); level++) {
|
||||
std::string files_at_level;
|
||||
ASSERT_TRUE(
|
||||
db->GetProperty("rocksdb.num-files-at-level" + std::to_string(level),
|
||||
&files_at_level));
|
||||
if (files_at_level != "0") {
|
||||
bottommost_level = level;
|
||||
}
|
||||
}
|
||||
|
||||
if (db_options_->bottommost_compression != kDisableCompressionOption &&
|
||||
ci.output_level == bottommost_level) {
|
||||
ASSERT_EQ(ci.compression, db_options_->bottommost_compression);
|
||||
} else if (db_options_->compression_per_level.size() != 0) {
|
||||
ASSERT_EQ(ci.compression,
|
||||
db_options_->compression_per_level[ci.output_level]);
|
||||
} else {
|
||||
ASSERT_EQ(ci.compression, db_options_->compression);
|
||||
}
|
||||
max_level_checked = std::max(max_level_checked, ci.output_level);
|
||||
}
|
||||
|
||||
int max_level_checked = 0;
|
||||
const Options* db_options_;
|
||||
};
|
||||
|
||||
enum CompressionFailureType {
|
||||
kTestCompressionFail,
|
||||
kTestDecompressionFail,
|
||||
kTestDecompressionCorruption
|
||||
};
|
||||
|
||||
class CompressionFailuresTest
|
||||
: public DBTest2,
|
||||
public testing::WithParamInterface<std::tuple<
|
||||
CompressionFailureType, CompressionType, uint32_t, uint32_t>> {
|
||||
public:
|
||||
CompressionFailuresTest() {
|
||||
std::tie(compression_failure_type_, compression_type_,
|
||||
compression_max_dict_bytes_, compression_parallel_threads_) =
|
||||
GetParam();
|
||||
}
|
||||
|
||||
CompressionFailureType compression_failure_type_ = kTestCompressionFail;
|
||||
CompressionType compression_type_ = kNoCompression;
|
||||
uint32_t compression_max_dict_bytes_ = 0;
|
||||
uint32_t compression_parallel_threads_ = 0;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTest2, CompressionFailuresTest,
|
||||
::testing::Combine(::testing::Values(kTestCompressionFail,
|
||||
kTestDecompressionFail,
|
||||
kTestDecompressionCorruption),
|
||||
::testing::ValuesIn(GetSupportedCompressions()),
|
||||
::testing::Values(0, 10), ::testing::Values(1, 4)));
|
||||
|
||||
TEST_P(CompressionFailuresTest, CompressionFailures) {
|
||||
if (compression_type_ == kNoCompression) {
|
||||
return;
|
||||
}
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.max_bytes_for_level_base = 1024;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.num_levels = 7;
|
||||
options.max_background_compactions = 1;
|
||||
options.target_file_size_base = 512;
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 512;
|
||||
table_options.verify_compression = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.compression = compression_type_;
|
||||
options.compression_opts.parallel_threads = compression_parallel_threads_;
|
||||
options.compression_opts.max_dict_bytes = compression_max_dict_bytes_;
|
||||
options.bottommost_compression_opts.parallel_threads =
|
||||
compression_parallel_threads_;
|
||||
options.bottommost_compression_opts.max_dict_bytes =
|
||||
compression_max_dict_bytes_;
|
||||
|
||||
if (compression_failure_type_ == kTestCompressionFail) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompressData:TamperWithReturnValue", [](void* arg) {
|
||||
bool* ret = static_cast<bool*>(arg);
|
||||
*ret = false;
|
||||
});
|
||||
} else if (compression_failure_type_ == kTestDecompressionFail) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UncompressBlockData:TamperWithReturnValue", [](void* arg) {
|
||||
Status* ret = static_cast<Status*>(arg);
|
||||
ASSERT_OK(*ret);
|
||||
*ret = Status::Corruption("kTestDecompressionFail");
|
||||
});
|
||||
} else if (compression_failure_type_ == kTestDecompressionCorruption) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UncompressBlockData:"
|
||||
"TamperWithDecompressionOutput",
|
||||
[](void* arg) {
|
||||
BlockContents* contents = static_cast<BlockContents*>(arg);
|
||||
// Ensure uncompressed data != original data
|
||||
const size_t len = contents->data.size() + 1;
|
||||
std::unique_ptr<char[]> fake_data(new char[len]());
|
||||
*contents = BlockContents(std::move(fake_data), len);
|
||||
});
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> key_value_written;
|
||||
|
||||
const int kKeySize = 5;
|
||||
const int kValUnitSize = 16;
|
||||
const int kValSize = 256;
|
||||
Random rnd(405);
|
||||
|
||||
Status s = Status::OK();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// Write 10 random files
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
std::string key = rnd.RandomString(kKeySize);
|
||||
// Ensure good compression ratio
|
||||
std::string valueUnit = rnd.RandomString(kValUnitSize);
|
||||
std::string value;
|
||||
for (int k = 0; k < kValSize; k += kValUnitSize) {
|
||||
value += valueUnit;
|
||||
}
|
||||
s = Put(key, value);
|
||||
if (compression_failure_type_ == kTestCompressionFail) {
|
||||
key_value_written[key] = value;
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
s = Flush();
|
||||
if (compression_failure_type_ == kTestCompressionFail) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
s = dbfull()->TEST_WaitForCompact();
|
||||
if (compression_failure_type_ == kTestCompressionFail) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
if (i == 4) {
|
||||
// Make compression fail at the mid of table building
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
}
|
||||
}
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
if (compression_failure_type_ == kTestCompressionFail) {
|
||||
// Should be kNoCompression, check content consistency
|
||||
std::unique_ptr<Iterator> db_iter(db_->NewIterator(ReadOptions()));
|
||||
for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
|
||||
std::string key = db_iter->key().ToString();
|
||||
std::string value = db_iter->value().ToString();
|
||||
ASSERT_NE(key_value_written.find(key), key_value_written.end());
|
||||
ASSERT_EQ(key_value_written[key], value);
|
||||
key_value_written.erase(key);
|
||||
}
|
||||
ASSERT_OK(db_iter->status());
|
||||
ASSERT_EQ(0, key_value_written.size());
|
||||
} else if (compression_failure_type_ == kTestDecompressionFail) {
|
||||
ASSERT_EQ(std::string(s.getState()),
|
||||
"Could not decompress: kTestDecompressionFail");
|
||||
} else if (compression_failure_type_ == kTestDecompressionCorruption) {
|
||||
ASSERT_EQ(std::string(s.getState()),
|
||||
"Decompressed block did not match pre-compression block");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, CompressionOptions) {
|
||||
if (!Zlib_Supported() || !Snappy_Supported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.max_bytes_for_level_base = 100;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.num_levels = 7;
|
||||
options.max_background_compactions = 1;
|
||||
|
||||
CompactionCompressionListener* listener =
|
||||
new CompactionCompressionListener(&options);
|
||||
options.listeners.emplace_back(listener);
|
||||
|
||||
const int kKeySize = 5;
|
||||
const int kValSize = 20;
|
||||
Random rnd(301);
|
||||
|
||||
std::vector<uint32_t> compression_parallel_threads = {1, 4};
|
||||
|
||||
std::map<std::string, std::string> key_value_written;
|
||||
|
||||
for (int iter = 0; iter <= 2; iter++) {
|
||||
listener->max_level_checked = 0;
|
||||
|
||||
if (iter == 0) {
|
||||
// Use different compression algorithms for different levels but
|
||||
// always use Zlib for bottommost level
|
||||
options.compression_per_level = {kNoCompression, kNoCompression,
|
||||
kNoCompression, kSnappyCompression,
|
||||
kSnappyCompression, kSnappyCompression,
|
||||
kZlibCompression};
|
||||
options.compression = kNoCompression;
|
||||
options.bottommost_compression = kZlibCompression;
|
||||
} else if (iter == 1) {
|
||||
// Use Snappy except for bottommost level use ZLib
|
||||
options.compression_per_level = {};
|
||||
options.compression = kSnappyCompression;
|
||||
options.bottommost_compression = kZlibCompression;
|
||||
} else if (iter == 2) {
|
||||
// Use Snappy everywhere
|
||||
options.compression_per_level = {};
|
||||
options.compression = kSnappyCompression;
|
||||
options.bottommost_compression = kDisableCompressionOption;
|
||||
}
|
||||
|
||||
for (auto num_threads : compression_parallel_threads) {
|
||||
options.compression_opts.parallel_threads = num_threads;
|
||||
options.bottommost_compression_opts.parallel_threads = num_threads;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// Write 10 random files
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
std::string key = rnd.RandomString(kKeySize);
|
||||
std::string value = rnd.RandomString(kValSize);
|
||||
key_value_written[key] = value;
|
||||
ASSERT_OK(Put(key, value));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
|
||||
// Make sure that we wrote enough to check all 7 levels
|
||||
ASSERT_EQ(listener->max_level_checked, 6);
|
||||
|
||||
// Make sure database content is the same as key_value_written
|
||||
std::unique_ptr<Iterator> db_iter(db_->NewIterator(ReadOptions()));
|
||||
for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
|
||||
std::string key = db_iter->key().ToString();
|
||||
std::string value = db_iter->value().ToString();
|
||||
ASSERT_NE(key_value_written.find(key), key_value_written.end());
|
||||
ASSERT_EQ(key_value_written[key], value);
|
||||
key_value_written.erase(key);
|
||||
}
|
||||
ASSERT_OK(db_iter->status());
|
||||
ASSERT_EQ(0, key_value_written.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CompactionStallTestListener : public EventListener {
|
||||
public:
|
||||
CompactionStallTestListener()
|
||||
@@ -3010,7 +2310,7 @@ TEST_F(DBTest2, PausingManualCompaction1) {
|
||||
"TestCompactFiles:PausingManualCompaction:3", [&](void* arg) {
|
||||
auto paused = static_cast<std::atomic<int>*>(arg);
|
||||
// CompactFiles() relies on manual_compactions_paused to
|
||||
// determine if thie compaction should be paused or not
|
||||
// determine if this compaction should be paused or not
|
||||
ASSERT_EQ(0, paused->load(std::memory_order_acquire));
|
||||
paused->fetch_add(1, std::memory_order_release);
|
||||
});
|
||||
@@ -3122,6 +2422,7 @@ TEST_F(DBTest2, PausingManualCompaction3) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
dbfull()->DisableManualCompaction();
|
||||
|
||||
ASSERT_TRUE(dbfull()
|
||||
->CompactRange(compact_options, nullptr, nullptr)
|
||||
.IsManualCompactionPaused());
|
||||
@@ -5421,6 +4722,103 @@ TEST_F(DBTest2, TestCompactFiles) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TestCancelCompactFiles) {
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.num_levels = 2;
|
||||
options.disable_auto_compactions = true;
|
||||
Reopen(options);
|
||||
|
||||
auto* handle = db_->DefaultColumnFamily();
|
||||
ASSERT_EQ(db_->NumberLevels(handle), 2);
|
||||
|
||||
ROCKSDB_NAMESPACE::SstFileWriter sst_file_writer{
|
||||
ROCKSDB_NAMESPACE::EnvOptions(), options};
|
||||
|
||||
// ingest large SST files
|
||||
std::vector<std::string> external_sst_file_names;
|
||||
int key_counter = 0;
|
||||
const int num_keys_per_file = 100000;
|
||||
const int num_files = 10;
|
||||
for (int i = 0; i < num_files; ++i) {
|
||||
std::string file_name =
|
||||
dbname_ + "/test_compact_files" + std::to_string(i) + ".sst_t";
|
||||
external_sst_file_names.push_back(file_name);
|
||||
ASSERT_OK(sst_file_writer.Open(file_name));
|
||||
for (int j = 0; j < num_keys_per_file; ++j) {
|
||||
ASSERT_OK(sst_file_writer.Put(Key(j + num_keys_per_file * key_counter),
|
||||
std::to_string(j)));
|
||||
}
|
||||
key_counter += 1;
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
}
|
||||
|
||||
ASSERT_OK(db_->IngestExternalFile(handle, external_sst_file_names,
|
||||
IngestExternalFileOptions()));
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1, 0), num_files);
|
||||
std::vector<std::string> files;
|
||||
GetSstFiles(env_, dbname_, &files);
|
||||
ASSERT_EQ(files.size(), num_files);
|
||||
|
||||
// Test that 0 compactions happen - canceled is set to True initially
|
||||
CompactionOptions compaction_options;
|
||||
std::atomic<bool> canceled(true);
|
||||
compaction_options.canceled = &canceled;
|
||||
|
||||
ASSERT_TRUE(db_->CompactFiles(compaction_options, handle, files, 1)
|
||||
.IsManualCompactionPaused());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1, 0), num_files);
|
||||
|
||||
// Test cancellation before the check to cancel compaction happens -
|
||||
// compaction should not occur
|
||||
bool disable_compaction = false;
|
||||
compaction_options.canceled->store(false, std::memory_order_release);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"TestCancelCompactFiles:SuccessfulCompaction", [&](void* arg) {
|
||||
auto paused = static_cast<std::atomic<int>*>(arg);
|
||||
if (disable_compaction) {
|
||||
db_->DisableManualCompaction();
|
||||
ASSERT_EQ(1, paused->load(std::memory_order_acquire));
|
||||
} else {
|
||||
compaction_options.canceled->store(true, std::memory_order_release);
|
||||
ASSERT_EQ(0, paused->load(std::memory_order_acquire));
|
||||
}
|
||||
});
|
||||
|
||||
ASSERT_TRUE(db_->CompactFiles(compaction_options, handle, files, 1)
|
||||
.IsManualCompactionPaused());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1, 0), num_files);
|
||||
|
||||
// DisableManualCompaction() should successfully cancel compaction
|
||||
disable_compaction = true;
|
||||
compaction_options.canceled->store(false, std::memory_order_release);
|
||||
ASSERT_TRUE(db_->CompactFiles(compaction_options, handle, files, 1)
|
||||
.IsManualCompactionPaused());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1, 0), num_files);
|
||||
// unlike CompactRange, value of compaction_options.canceled will be
|
||||
// unaffected by calling DisableManualCompactions()
|
||||
ASSERT_FALSE(compaction_options.canceled->load());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
db_->EnableManualCompaction();
|
||||
|
||||
// Test cancelation after the check to cancel compaction - compaction should
|
||||
// occur, leaving only 1 file
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactFilesImpl:0", [&](void* /*arg*/) {
|
||||
compaction_options.canceled->store(true, std::memory_order_release);
|
||||
});
|
||||
|
||||
compaction_options.canceled->store(false, std::memory_order_release);
|
||||
ASSERT_OK(db_->CompactFiles(compaction_options, handle, files, 1));
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1, 0), 1);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, MultiDBParallelOpenTest) {
|
||||
const int kNumDbs = 2;
|
||||
Options options = CurrentOptions();
|
||||
|
||||
@@ -452,6 +452,10 @@ class SpecialEnv : public EnvWrapper {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status GetFileSize(uint64_t* s) override {
|
||||
return target_->GetFileSize(s);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<RandomAccessFile> target_;
|
||||
anon::AtomicCounter* counter_;
|
||||
@@ -478,6 +482,10 @@ class SpecialEnv : public EnvWrapper {
|
||||
return target_->Prefetch(offset, n);
|
||||
}
|
||||
|
||||
Status GetFileSize(uint64_t* s) override {
|
||||
return target_->GetFileSize(s);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<RandomAccessFile> target_;
|
||||
std::atomic<uint64_t>* fail_cnt_;
|
||||
|
||||
@@ -1672,55 +1672,75 @@ TEST_P(DBTestUniversalCompaction, ConcurrentBottomPriLowPriCompactions) {
|
||||
}
|
||||
const int kNumFilesTrigger = 3;
|
||||
Env::Default()->SetBackgroundThreads(1, Env::Priority::BOTTOM);
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.max_background_compactions = 2;
|
||||
options.num_levels = num_levels_;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
options.level0_file_num_compaction_trigger = kNumFilesTrigger;
|
||||
// Trigger compaction if size amplification exceeds 110%
|
||||
options.compaction_options_universal.max_size_amplification_percent = 110;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Need to get a token to enable compaction parallelism up to
|
||||
// `max_background_compactions` jobs.
|
||||
auto pressure_token =
|
||||
dbfull()->TEST_write_controler().GetCompactionPressureToken();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{// wait for the full compaction to be picked before adding files intended
|
||||
// for the second one.
|
||||
{"DBImpl::BackgroundCompaction:ForwardToBottomPriPool",
|
||||
"DBTestUniversalCompaction:ConcurrentBottomPriLowPriCompactions:0"},
|
||||
// the full (bottom-pri) compaction waits until a partial (low-pri)
|
||||
// compaction has started to verify they can run in parallel.
|
||||
{"DBImpl::BackgroundCompaction:NonTrivial",
|
||||
"DBImpl::BGWorkBottomCompaction"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
for (bool universal_reduce_file_locking : {true, false}) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.compaction_options_universal.reduce_file_locking =
|
||||
universal_reduce_file_locking;
|
||||
options.max_background_compactions = 2;
|
||||
options.num_levels = num_levels_;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
options.level0_file_num_compaction_trigger = kNumFilesTrigger;
|
||||
// Trigger compaction if size amplification exceeds 110%
|
||||
options.compaction_options_universal.max_size_amplification_percent = 110;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (int num = 0; num < kNumFilesTrigger; num++) {
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx, true /* no_wait */);
|
||||
// use no_wait above because that one waits for flush and compaction. We
|
||||
// don't want to wait for compaction because the full compaction is
|
||||
// intentionally blocked while more files are flushed.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
// Need to get a token to enable compaction parallelism up to
|
||||
// `max_background_compactions` jobs.
|
||||
auto pressure_token =
|
||||
dbfull()->TEST_write_controler().GetCompactionPressureToken();
|
||||
if (universal_reduce_file_locking) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{// Wait for the full compaction to be repicked before adding files
|
||||
// intended for the second compaction.
|
||||
{"DBImpl::BackgroundCompaction():AfterPickCompactionBottomPri",
|
||||
"DBTestUniversalCompaction:ConcurrentBottomPriLowPriCompactions:0"},
|
||||
// Wait for the second compaction to run before running the full
|
||||
// compaction to verify they can run in parallel
|
||||
{"DBImpl::BackgroundCompaction:NonTrivial:BeforeRun",
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:BeforeRunBottomPri"}});
|
||||
} else {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{// Wait for the full compaction to be forwarded before adding files
|
||||
// intended for the second compaction.
|
||||
{"DBImpl::BackgroundCompaction:ForwardToBottomPriPool",
|
||||
"DBTestUniversalCompaction:ConcurrentBottomPriLowPriCompactions:0"},
|
||||
// Wait for the second compaction to run before running the full
|
||||
// compaction to verify they can run in parallel
|
||||
{"DBImpl::BackgroundCompaction:NonTrivial:BeforeRun",
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:BeforeRunBottomPri"}});
|
||||
}
|
||||
if (i == 0) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBTestUniversalCompaction:ConcurrentBottomPriLowPriCompactions:0");
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (int num = 0; num < kNumFilesTrigger; num++) {
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx, true /* no_wait */);
|
||||
// use no_wait above because that one waits for flush and compaction. We
|
||||
// don't want to wait for compaction because the full compaction is
|
||||
// intentionally blocked while more files are flushed.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
}
|
||||
if (i == 0) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBTestUniversalCompaction:ConcurrentBottomPriLowPriCompactions:0");
|
||||
}
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// First compaction should output to bottom level. Second should output to
|
||||
// L0 since older L0 files pending compaction prevent it from being placed
|
||||
// lower.
|
||||
ASSERT_EQ(NumSortedRuns(), 2);
|
||||
ASSERT_GT(NumTableFilesAtLevel(0), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(num_levels_ - 1), 0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// First compaction should output to bottom level. Second should output to L0
|
||||
// since older L0 files pending compaction prevent it from being placed lower.
|
||||
ASSERT_EQ(NumSortedRuns(), 2);
|
||||
ASSERT_GT(NumTableFilesAtLevel(0), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(num_levels_ - 1), 0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
Env::Default()->SetBackgroundThreads(0, Env::Priority::BOTTOM);
|
||||
}
|
||||
|
||||
|
||||
@@ -2400,7 +2400,6 @@ class DataVisibilityTest : public DBBasicTestWithTimestampBase {
|
||||
}
|
||||
}
|
||||
};
|
||||
constexpr int DataVisibilityTest::kTestDataSize;
|
||||
|
||||
// Application specifies timestamp but not snapshot.
|
||||
// reader writer
|
||||
|
||||
+33
-15
@@ -77,7 +77,12 @@ void EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
TableFileCreationReason reason, const Status& s,
|
||||
const std::string& file_checksum,
|
||||
const std::string& file_checksum_func_name) {
|
||||
if (s.ok() && event_logger) {
|
||||
if (!event_logger && listeners.empty()) {
|
||||
s.PermitUncheckedError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_logger) {
|
||||
JSONWriter jwriter;
|
||||
AppendCurrentTime(&jwriter);
|
||||
jwriter << "cf_name" << cf_name << "job" << job_id << "event"
|
||||
@@ -165,6 +170,8 @@ void EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
jwriter << "oldest_blob_file_number" << oldest_blob_file_number;
|
||||
}
|
||||
|
||||
jwriter << "status" << s.ToString();
|
||||
|
||||
jwriter.EndObject();
|
||||
|
||||
event_logger->Log(jwriter);
|
||||
@@ -195,18 +202,22 @@ void EventHelpers::LogAndNotifyTableFileDeletion(
|
||||
const std::string& file_path, const Status& status,
|
||||
const std::string& dbname,
|
||||
const std::vector<std::shared_ptr<EventListener>>& listeners) {
|
||||
JSONWriter jwriter;
|
||||
AppendCurrentTime(&jwriter);
|
||||
|
||||
jwriter << "job" << job_id << "event" << "table_file_deletion"
|
||||
<< "file_number" << file_number;
|
||||
if (!status.ok()) {
|
||||
jwriter << "status" << status.ToString();
|
||||
if (!event_logger && listeners.empty()) {
|
||||
status.PermitUncheckedError();
|
||||
return;
|
||||
}
|
||||
|
||||
jwriter.EndObject();
|
||||
if (event_logger) {
|
||||
JSONWriter jwriter;
|
||||
AppendCurrentTime(&jwriter);
|
||||
|
||||
event_logger->Log(jwriter);
|
||||
jwriter << "job" << job_id << "event" << "table_file_deletion"
|
||||
<< "file_number" << file_number << "status" << status.ToString();
|
||||
|
||||
jwriter.EndObject();
|
||||
|
||||
event_logger->Log(jwriter);
|
||||
}
|
||||
|
||||
if (listeners.empty()) {
|
||||
return;
|
||||
@@ -274,7 +285,12 @@ void EventHelpers::LogAndNotifyBlobFileCreationFinished(
|
||||
const std::string& file_checksum,
|
||||
const std::string& file_checksum_func_name, uint64_t total_blob_count,
|
||||
uint64_t total_blob_bytes) {
|
||||
if (s.ok() && event_logger) {
|
||||
if (!event_logger && listeners.empty()) {
|
||||
s.PermitUncheckedError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_logger) {
|
||||
JSONWriter jwriter;
|
||||
AppendCurrentTime(&jwriter);
|
||||
jwriter << "cf_name" << cf_name << "job" << job_id << "event"
|
||||
@@ -305,15 +321,17 @@ void EventHelpers::LogAndNotifyBlobFileDeletion(
|
||||
const std::vector<std::shared_ptr<EventListener>>& listeners, int job_id,
|
||||
uint64_t file_number, const std::string& file_path, const Status& status,
|
||||
const std::string& dbname) {
|
||||
if (!event_logger && listeners.empty()) {
|
||||
status.PermitUncheckedError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_logger) {
|
||||
JSONWriter jwriter;
|
||||
AppendCurrentTime(&jwriter);
|
||||
|
||||
jwriter << "job" << job_id << "event" << "blob_file_deletion"
|
||||
<< "file_number" << file_number;
|
||||
if (!status.ok()) {
|
||||
jwriter << "status" << status.ToString();
|
||||
}
|
||||
<< "file_number" << file_number << "status" << status.ToString();
|
||||
|
||||
jwriter.EndObject();
|
||||
event_logger->Log(jwriter);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/file_checksum_helper.h"
|
||||
#include "util/random.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
|
||||
@@ -260,55 +261,6 @@ TEST_F(ExternalSSTFileBasicTest, Basic) {
|
||||
s = sst_file_writer.DeleteRange(Key(100), Key(200));
|
||||
ASSERT_NOK(s) << s.ToString();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::IngestExternalFile:AfterIncIngestFileCounter",
|
||||
"ExternalSSTFileBasicTest.LiveWriteStart"},
|
||||
{"WriteThread::JoinBatchGroup:Wait",
|
||||
"DBImpl::IngestExternalFile:AfterIncIngestFileCounter:2"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
PerfContext* write_thread_perf_context;
|
||||
std::thread write_thread([&] {
|
||||
TEST_SYNC_POINT("ExternalSSTFileBasicTest.LiveWriteStart");
|
||||
SetPerfLevel(kEnableWait);
|
||||
write_thread_perf_context = get_perf_context();
|
||||
write_thread_perf_context->Reset();
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "bar", "v2"));
|
||||
ASSERT_GT(write_thread_perf_context->write_thread_wait_nanos, 0);
|
||||
// Test sync points were used to make sure this live write enter write
|
||||
// thread after the file ingestion entered write thread. So by the time this
|
||||
// live write finishes, the latest seqno is 1 means file ingestion used
|
||||
// seqno 0.
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 1U);
|
||||
});
|
||||
|
||||
// Add file using file path
|
||||
SetPerfLevel(kEnableTimeExceptForMutex);
|
||||
PerfContext* perf_ctx = get_perf_context();
|
||||
perf_ctx->Reset();
|
||||
s = DeprecatedAddFile({file1});
|
||||
ASSERT_GT(perf_context.file_ingestion_nanos, 0);
|
||||
ASSERT_GT(perf_context.file_ingestion_blocking_live_writes_nanos, 0);
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
for (int k = 0; k < 100; k++) {
|
||||
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
||||
}
|
||||
|
||||
write_thread.join();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// Re-ingest the file just to check the perf context not enabled at and below
|
||||
// kEnableWait.
|
||||
SetPerfLevel(kEnableWait);
|
||||
perf_ctx->Reset();
|
||||
IngestExternalFileOptions opts;
|
||||
opts.allow_global_seqno = true;
|
||||
opts.allow_blocking_flush = true;
|
||||
ASSERT_OK(db_->IngestExternalFile({file1}, opts));
|
||||
ASSERT_EQ(perf_context.file_ingestion_nanos, 0);
|
||||
ASSERT_EQ(perf_context.file_ingestion_blocking_live_writes_nanos, 0);
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
@@ -395,7 +347,8 @@ class ChecksumVerifyHelper {
|
||||
|
||||
Status GetSingleFileChecksumAndFuncName(
|
||||
const std::string& file_path, std::string* file_checksum,
|
||||
std::string* file_checksum_func_name) {
|
||||
std::string* file_checksum_func_name,
|
||||
const std::string& requested_func_name = {}) {
|
||||
Status s;
|
||||
EnvOptions soptions;
|
||||
std::unique_ptr<SequentialFile> file_reader;
|
||||
@@ -413,6 +366,8 @@ class ChecksumVerifyHelper {
|
||||
return Status::OK();
|
||||
} else {
|
||||
FileChecksumGenContext gen_context;
|
||||
gen_context.file_name = file_path;
|
||||
gen_context.requested_checksum_func_name = requested_func_name;
|
||||
std::unique_ptr<FileChecksumGenerator> file_checksum_gen =
|
||||
file_checksum_gen_factory->CreateFileChecksumGenerator(gen_context);
|
||||
*file_checksum_func_name = file_checksum_gen->Name();
|
||||
@@ -488,10 +443,50 @@ TEST_F(ExternalSSTFileBasicTest, BasicWithFileChecksumCrc32c) {
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
namespace {
|
||||
class VariousFileChecksumGenerator : public FileChecksumGenCrc32c {
|
||||
public:
|
||||
explicit VariousFileChecksumGenerator(const std::string& name)
|
||||
: FileChecksumGenCrc32c({}), name_(name) {}
|
||||
|
||||
const char* Name() const override { return name_.c_str(); }
|
||||
|
||||
std::string GetChecksum() const override {
|
||||
return FileChecksumGenCrc32c::GetChecksum() + "_" + name_;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string name_;
|
||||
};
|
||||
|
||||
class VariousFileChecksumGenFactory : public FileChecksumGenFactory {
|
||||
public:
|
||||
std::unique_ptr<FileChecksumGenerator> CreateFileChecksumGenerator(
|
||||
const FileChecksumGenContext& context) override {
|
||||
static RelaxedAtomic<int> counter{0};
|
||||
if (Slice(context.requested_checksum_func_name).starts_with("Various")) {
|
||||
return std::make_unique<VariousFileChecksumGenerator>(
|
||||
context.requested_checksum_func_name);
|
||||
} else if (context.requested_checksum_func_name.empty()) {
|
||||
// Lacking a specific request, use a different function name for each
|
||||
// result.
|
||||
return std::make_unique<VariousFileChecksumGenerator>(
|
||||
"Various" + std::to_string(counter.FetchAddRelaxed(1)));
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kClassName() { return "VariousFileChecksumGenFactory"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
Options old_options = CurrentOptions();
|
||||
Options options = CurrentOptions();
|
||||
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
|
||||
options.file_checksum_gen_factory =
|
||||
std::make_shared<VariousFileChecksumGenFactory>();
|
||||
const ImmutableCFOptions ioptions(options);
|
||||
ChecksumVerifyHelper checksum_helper(options);
|
||||
|
||||
@@ -512,7 +507,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
ASSERT_EQ(file1_info.largest_key, Key(1099));
|
||||
std::string file_checksum1, file_checksum_func_name1;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file1, &file_checksum1, &file_checksum_func_name1));
|
||||
file1, &file_checksum1, &file_checksum_func_name1,
|
||||
file1_info.file_checksum_func_name));
|
||||
ASSERT_EQ(file1_info.file_checksum, file_checksum1);
|
||||
ASSERT_EQ(file1_info.file_checksum_func_name, file_checksum_func_name1);
|
||||
|
||||
@@ -531,7 +527,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
ASSERT_EQ(file2_info.largest_key, Key(1299));
|
||||
std::string file_checksum2, file_checksum_func_name2;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file2, &file_checksum2, &file_checksum_func_name2));
|
||||
file2, &file_checksum2, &file_checksum_func_name2,
|
||||
file2_info.file_checksum_func_name));
|
||||
ASSERT_EQ(file2_info.file_checksum, file_checksum2);
|
||||
ASSERT_EQ(file2_info.file_checksum_func_name, file_checksum_func_name2);
|
||||
|
||||
@@ -550,7 +547,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
ASSERT_EQ(file3_info.largest_key, Key(1499));
|
||||
std::string file_checksum3, file_checksum_func_name3;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file3, &file_checksum3, &file_checksum_func_name3));
|
||||
file3, &file_checksum3, &file_checksum_func_name3,
|
||||
file3_info.file_checksum_func_name));
|
||||
ASSERT_EQ(file3_info.file_checksum, file_checksum3);
|
||||
ASSERT_EQ(file3_info.file_checksum_func_name, file_checksum_func_name3);
|
||||
|
||||
@@ -569,7 +567,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
ASSERT_EQ(file4_info.largest_key, Key(1799));
|
||||
std::string file_checksum4, file_checksum_func_name4;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file4, &file_checksum4, &file_checksum_func_name4));
|
||||
file4, &file_checksum4, &file_checksum_func_name4,
|
||||
file4_info.file_checksum_func_name));
|
||||
ASSERT_EQ(file4_info.file_checksum, file_checksum4);
|
||||
ASSERT_EQ(file4_info.file_checksum_func_name, file_checksum_func_name4);
|
||||
|
||||
@@ -588,7 +587,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
ASSERT_EQ(file5_info.largest_key, Key(1999));
|
||||
std::string file_checksum5, file_checksum_func_name5;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file5, &file_checksum5, &file_checksum_func_name5));
|
||||
file5, &file_checksum5, &file_checksum_func_name5,
|
||||
file5_info.file_checksum_func_name));
|
||||
ASSERT_EQ(file5_info.file_checksum, file_checksum5);
|
||||
ASSERT_EQ(file5_info.file_checksum_func_name, file_checksum_func_name5);
|
||||
|
||||
@@ -607,7 +607,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
ASSERT_EQ(file6_info.largest_key, Key(2199));
|
||||
std::string file_checksum6, file_checksum_func_name6;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file6, &file_checksum6, &file_checksum_func_name6));
|
||||
file6, &file_checksum6, &file_checksum_func_name6,
|
||||
file6_info.file_checksum_func_name));
|
||||
ASSERT_EQ(file6_info.file_checksum, file_checksum6);
|
||||
ASSERT_EQ(file6_info.file_checksum_func_name, file_checksum_func_name6);
|
||||
|
||||
@@ -677,18 +678,23 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
}
|
||||
ASSERT_OK(env_->FileExists(file2));
|
||||
|
||||
// Enable verify_file_checksum option
|
||||
// No checksum information is provided, generate it when ingesting
|
||||
std::vector<std::string> checksum, checksum_func;
|
||||
s = AddFileWithFileChecksum({file3}, checksum, checksum_func, true, false,
|
||||
false, false);
|
||||
// Enable verify_file_checksum option. No checksum information is provided,
|
||||
// so it is generated when ingesting. The configured checksum factory will
|
||||
// use a different function than before.
|
||||
s = AddFileWithFileChecksum({file3}, {}, {}, true, false, false, false);
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
std::vector<LiveFileMetaData> live_files2;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files2);
|
||||
for (const auto& f : live_files2) {
|
||||
if (set1.find(f.name) == set1.end()) {
|
||||
ASSERT_EQ(f.file_checksum, file_checksum3);
|
||||
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name3);
|
||||
// Recomputed checksum, different function
|
||||
EXPECT_NE(f.file_checksum_func_name, file_checksum_func_name3);
|
||||
std::string cur_checksum3, cur_checksum_func_name3;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
dbname_ + f.name, &cur_checksum3, &cur_checksum_func_name3,
|
||||
f.file_checksum_func_name));
|
||||
EXPECT_EQ(f.file_checksum, cur_checksum3);
|
||||
EXPECT_EQ(f.file_checksum_func_name, cur_checksum_func_name3);
|
||||
set1.insert(f.name);
|
||||
}
|
||||
}
|
||||
@@ -702,8 +708,9 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
ASSERT_NOK(s) << s.ToString();
|
||||
|
||||
// Does not enable verify_file_checksum options
|
||||
// Checksum function name matches, store the checksum being ingested.
|
||||
s = AddFileWithFileChecksum({file4}, {"asd"}, {file_checksum_func_name4},
|
||||
// Checksum function name is recognized, so store the checksum being ingested.
|
||||
std::string file_checksum_func_name4alt = "VariousABCD";
|
||||
s = AddFileWithFileChecksum({file4}, {"asd"}, {file_checksum_func_name4alt},
|
||||
false, false, false, false);
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
std::vector<LiveFileMetaData> live_files3;
|
||||
@@ -712,7 +719,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
if (set1.find(f.name) == set1.end()) {
|
||||
ASSERT_FALSE(f.file_checksum == file_checksum4);
|
||||
ASSERT_EQ(f.file_checksum, "asd");
|
||||
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name4);
|
||||
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name4alt);
|
||||
set1.insert(f.name);
|
||||
}
|
||||
}
|
||||
@@ -721,7 +728,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
|
||||
// enable verify_file_checksum options, DB enable checksum, and enable
|
||||
// write_global_seq. So the checksum stored is different from the one
|
||||
// ingested due to the sequence number changes.
|
||||
// ingested due to the sequence number changes. The checksum function name
|
||||
// may also change since the checksum is recomputed.
|
||||
s = AddFileWithFileChecksum({file5}, {file_checksum5},
|
||||
{file_checksum_func_name5}, true, false, false,
|
||||
true);
|
||||
@@ -730,11 +738,14 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
dbfull()->GetLiveFilesMetaData(&live_files4);
|
||||
for (const auto& f : live_files4) {
|
||||
if (set1.find(f.name) == set1.end()) {
|
||||
// Recomputed checksum, different function
|
||||
EXPECT_NE(f.file_checksum_func_name, file_checksum_func_name5);
|
||||
std::string cur_checksum5, cur_checksum_func_name5;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
dbname_ + f.name, &cur_checksum5, &cur_checksum_func_name5));
|
||||
ASSERT_EQ(f.file_checksum, cur_checksum5);
|
||||
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name5);
|
||||
dbname_ + f.name, &cur_checksum5, &cur_checksum_func_name5,
|
||||
f.file_checksum_func_name));
|
||||
EXPECT_EQ(f.file_checksum, cur_checksum5);
|
||||
EXPECT_EQ(f.file_checksum_func_name, cur_checksum_func_name5);
|
||||
set1.insert(f.name);
|
||||
}
|
||||
}
|
||||
@@ -742,18 +753,22 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
ASSERT_OK(env_->FileExists(file5));
|
||||
|
||||
// Does not enable verify_file_checksum options and also the ingested file
|
||||
// checksum information is empty. DB will generate and store the checksum
|
||||
// in Manifest.
|
||||
std::vector<std::string> files_c6, files_name6;
|
||||
s = AddFileWithFileChecksum({file6}, files_c6, files_name6, false, false,
|
||||
false, false);
|
||||
// checksum information is empty. DB will generate and store file checksum
|
||||
// in Manifest, which could be different from the previous invocation.
|
||||
s = AddFileWithFileChecksum({file6}, {}, {}, false, false, false, false);
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
std::vector<LiveFileMetaData> live_files6;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files6);
|
||||
for (const auto& f : live_files6) {
|
||||
if (set1.find(f.name) == set1.end()) {
|
||||
ASSERT_EQ(f.file_checksum, file_checksum6);
|
||||
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name6);
|
||||
// Recomputed checksum, different function
|
||||
EXPECT_NE(f.file_checksum_func_name, file_checksum_func_name6);
|
||||
std::string cur_checksum6, cur_checksum_func_name6;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
dbname_ + f.name, &cur_checksum6, &cur_checksum_func_name6,
|
||||
f.file_checksum_func_name));
|
||||
EXPECT_EQ(f.file_checksum, cur_checksum6);
|
||||
EXPECT_EQ(f.file_checksum_func_name, cur_checksum_func_name6);
|
||||
set1.insert(f.name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,6 +156,14 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
// It is unsafe to assume application had sync the file and file
|
||||
// directory before ingest the file. For integrity of RocksDB we need
|
||||
// to sync the file.
|
||||
|
||||
// TODO(xingbo), We should in general be moving away from production
|
||||
// uses of ReuseWritableFile (except explicitly for WAL recycling),
|
||||
// ReopenWritableFile, and NewRandomRWFile. We should create a
|
||||
// FileSystem::SyncFile/FsyncFile API that by default does the
|
||||
// re-open+sync+close combo but can (a) be reused easily, and (b) be
|
||||
// overridden to do that more cleanly, e.g. in EncryptedEnv.
|
||||
// https://github.com/facebook/rocksdb/issues/13741
|
||||
std::unique_ptr<FSWritableFile> file_to_sync;
|
||||
Status s = fs_->ReopenWritableFile(path_inside_db, env_options_,
|
||||
&file_to_sync, nullptr);
|
||||
@@ -260,10 +268,6 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
} else {
|
||||
need_generate_file_checksum_ = true;
|
||||
}
|
||||
FileChecksumGenContext gen_context;
|
||||
std::unique_ptr<FileChecksumGenerator> file_checksum_gen =
|
||||
db_options_.file_checksum_gen_factory->CreateFileChecksumGenerator(
|
||||
gen_context);
|
||||
std::vector<std::string> generated_checksums;
|
||||
std::vector<std::string> generated_checksum_func_names;
|
||||
// Step 1: generate the checksum for ingested sst file.
|
||||
@@ -271,7 +275,9 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
for (size_t i = 0; i < files_to_ingest_.size(); i++) {
|
||||
std::string generated_checksum;
|
||||
std::string generated_checksum_func_name;
|
||||
std::string requested_checksum_func_name;
|
||||
std::string requested_checksum_func_name =
|
||||
i < files_checksum_func_names.size() ? files_checksum_func_names[i]
|
||||
: "";
|
||||
// TODO: rate limit file reads for checksum calculation during file
|
||||
// ingestion.
|
||||
// TODO: plumb Env::IOActivity
|
||||
@@ -314,40 +320,50 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
if (files_checksum_func_names[i] !=
|
||||
generated_checksum_func_names[i]) {
|
||||
status = Status::InvalidArgument(
|
||||
"Checksum function name does not match with the checksum "
|
||||
"function name of this DB");
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"Sst file checksum verification of file: %s failed: %s",
|
||||
external_files_paths[i].c_str(), status.ToString().c_str());
|
||||
"DB file checksum gen factory " +
|
||||
std::string(db_options_.file_checksum_gen_factory->Name()) +
|
||||
" generated checksum function name " +
|
||||
generated_checksum_func_names[i] + " for file " +
|
||||
external_files_paths[i] +
|
||||
" which does not match requested/provided " +
|
||||
files_checksum_func_names[i]);
|
||||
break;
|
||||
}
|
||||
if (files_checksums[i] != generated_checksums[i]) {
|
||||
status = Status::Corruption(
|
||||
"Ingested checksum does not match with the generated "
|
||||
"checksum");
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"Sst file checksum verification of file: %s failed: %s",
|
||||
files_to_ingest_[i].internal_file_path.c_str(),
|
||||
status.ToString().c_str());
|
||||
"Checksum verification mismatch for ingestion file " +
|
||||
external_files_paths[i] + " using function " +
|
||||
generated_checksum_func_names[i] + ". Expected: " +
|
||||
Slice(files_checksums[i]).ToString(/*hex=*/true) +
|
||||
" Computed: " +
|
||||
Slice(generated_checksums[i]).ToString(/*hex=*/true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If verify_file_checksum is not enabled, we only verify the
|
||||
// checksum function name. If it does not match, fail the ingestion.
|
||||
// If matches, we trust the ingested checksum information and store
|
||||
// in the Manifest.
|
||||
// If verify_file_checksum is not enabled, we only verify the factory
|
||||
// recognizes the checksum function name. If it does not match, fail
|
||||
// the ingestion. If matches, we trust the ingested checksum
|
||||
// information and store in the Manifest.
|
||||
for (size_t i = 0; i < files_to_ingest_.size(); i++) {
|
||||
if (files_checksum_func_names[i] != file_checksum_gen->Name()) {
|
||||
FileChecksumGenContext gen_context;
|
||||
gen_context.file_name = files_to_ingest_[i].internal_file_path;
|
||||
gen_context.requested_checksum_func_name =
|
||||
files_checksum_func_names[i];
|
||||
auto file_checksum_gen =
|
||||
db_options_.file_checksum_gen_factory
|
||||
->CreateFileChecksumGenerator(gen_context);
|
||||
|
||||
if (file_checksum_gen == nullptr ||
|
||||
files_checksum_func_names[i] != file_checksum_gen->Name()) {
|
||||
status = Status::InvalidArgument(
|
||||
"Checksum function name does not match with the checksum "
|
||||
"function name of this DB");
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"Sst file checksum verification of file: %s failed: %s",
|
||||
external_files_paths[i].c_str(), status.ToString().c_str());
|
||||
"Checksum function name " + files_checksum_func_names[i] +
|
||||
" for file " + external_files_paths[i] +
|
||||
" not recognized by DB checksum gen factory" +
|
||||
db_options_.file_checksum_gen_factory->Name() +
|
||||
(file_checksum_gen ? (" Returned function " +
|
||||
std::string(file_checksum_gen->Name()))
|
||||
: ""));
|
||||
break;
|
||||
}
|
||||
files_to_ingest_[i].file_checksum = files_checksums[i];
|
||||
@@ -362,12 +378,11 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
status = Status::InvalidArgument(
|
||||
"The checksum information of ingested sst files are nonempty and "
|
||||
"the size of checksums or the size of the checksum function "
|
||||
"names "
|
||||
"does not match with the number of ingested sst files");
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"The ingested sst files checksum information is incomplete: %s",
|
||||
status.ToString().c_str());
|
||||
"names does not match with the number of ingested sst files");
|
||||
}
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log, "Ingestion failed: %s",
|
||||
status.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -682,10 +697,9 @@ void ExternalSstFileIngestionJob::CreateEquivalentFileIngestingCompactions() {
|
||||
0 /* max_subcompaction, not applicable */,
|
||||
{} /* grandparents, not applicable */,
|
||||
std::nullopt /* earliest_snapshot */, nullptr /* snapshot_checker */,
|
||||
false /* is manual */, "" /* trim_ts */, -1 /* score, not applicable */,
|
||||
false /* is deletion compaction, not applicable */,
|
||||
files_overlap_ /* l0_files_might_overlap, not applicable */,
|
||||
CompactionReason::kExternalSstIngestion));
|
||||
CompactionReason::kExternalSstIngestion, "" /* trim_ts */,
|
||||
-1 /* score, not applicable */,
|
||||
files_overlap_ /* l0_files_might_overlap, not applicable */));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -834,7 +848,8 @@ Status ExternalSstFileIngestionJob::ResetTableReader(
|
||||
ro,
|
||||
TableReaderOptions(
|
||||
cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor,
|
||||
env_options_, cfd_->internal_comparator(),
|
||||
sv->mutable_cf_options.compression_manager.get(), env_options_,
|
||||
cfd_->internal_comparator(),
|
||||
sv->mutable_cf_options.block_protection_bytes_per_key,
|
||||
/*skip_filters*/ false, /*immortal*/ false,
|
||||
/*force_direct_prefetch*/ false, /*level*/ -1,
|
||||
|
||||
+73
-57
@@ -92,12 +92,10 @@ FlushJob::FlushJob(
|
||||
const MutableCFOptions& mutable_cf_options, uint64_t max_memtable_id,
|
||||
const FileOptions& file_options, VersionSet* versions,
|
||||
InstrumentedMutex* db_mutex, std::atomic<bool>* shutting_down,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||
FlushReason flush_reason, LogBuffer* log_buffer, FSDirectory* db_directory,
|
||||
FSDirectory* output_file_directory, CompressionType output_compression,
|
||||
Statistics* stats, EventLogger* event_logger, bool measure_io_stats,
|
||||
JobContext* job_context, FlushReason flush_reason, LogBuffer* log_buffer,
|
||||
FSDirectory* db_directory, FSDirectory* output_file_directory,
|
||||
CompressionType output_compression, Statistics* stats,
|
||||
EventLogger* event_logger, bool measure_io_stats,
|
||||
const bool sync_output_directory, const bool write_manifest,
|
||||
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
std::shared_ptr<const SeqnoToTimeMapping> seqno_to_time_mapping,
|
||||
@@ -114,12 +112,7 @@ FlushJob::FlushJob(
|
||||
versions_(versions),
|
||||
db_mutex_(db_mutex),
|
||||
shutting_down_(shutting_down),
|
||||
existing_snapshots_(std::move(existing_snapshots)),
|
||||
earliest_snapshot_(existing_snapshots_.empty()
|
||||
? kMaxSequenceNumber
|
||||
: existing_snapshots_.at(0)),
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
earliest_snapshot_(job_context->GetEarliestSnapshotSequence()),
|
||||
job_context_(job_context),
|
||||
flush_reason_(flush_reason),
|
||||
log_buffer_(log_buffer),
|
||||
@@ -140,6 +133,7 @@ FlushJob::FlushJob(
|
||||
full_history_ts_low_(std::move(full_history_ts_low)),
|
||||
blob_callback_(blob_callback),
|
||||
seqno_to_time_mapping_(std::move(seqno_to_time_mapping)) {
|
||||
assert(job_context->snapshot_context_initialized);
|
||||
// Update the thread status to indicate flush.
|
||||
ReportStartedFlush();
|
||||
TEST_SYNC_POINT("FlushJob::FlushJob()");
|
||||
@@ -456,7 +450,7 @@ Status FlushJob::MemPurge() {
|
||||
const std::string* const full_history_ts_low = &(cfd_->GetFullHistoryTsLow());
|
||||
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg(
|
||||
new CompactionRangeDelAggregator(&(cfd_->internal_comparator()),
|
||||
existing_snapshots_,
|
||||
job_context_->snapshot_seqs,
|
||||
full_history_ts_low));
|
||||
for (auto& rd_iter : range_del_iters) {
|
||||
range_del_agg->AddTombstones(std::move(rd_iter));
|
||||
@@ -495,19 +489,19 @@ Status FlushJob::MemPurge() {
|
||||
|
||||
Env* env = db_options_.env;
|
||||
assert(env);
|
||||
MergeHelper merge(
|
||||
env, (cfd_->internal_comparator()).user_comparator(),
|
||||
(ioptions.merge_operator).get(), compaction_filter.get(),
|
||||
ioptions.logger, true /* internal key corruption is not ok */,
|
||||
existing_snapshots_.empty() ? 0 : existing_snapshots_.back(),
|
||||
snapshot_checker_);
|
||||
MergeHelper merge(env, (cfd_->internal_comparator()).user_comparator(),
|
||||
(ioptions.merge_operator).get(), compaction_filter.get(),
|
||||
ioptions.logger,
|
||||
true /* internal key corruption is not ok */,
|
||||
job_context_->GetLatestSnapshotSequence(),
|
||||
job_context_->snapshot_checker);
|
||||
assert(job_context_);
|
||||
SequenceNumber job_snapshot_seq = job_context_->GetJobSnapshotSequence();
|
||||
const std::atomic<bool> kManualCompactionCanceledFalse{false};
|
||||
CompactionIterator c_iter(
|
||||
iter.get(), (cfd_->internal_comparator()).user_comparator(), &merge,
|
||||
kMaxSequenceNumber, &existing_snapshots_, earliest_snapshot_,
|
||||
earliest_write_conflict_snapshot_, job_snapshot_seq, snapshot_checker_,
|
||||
kMaxSequenceNumber, &job_context_->snapshot_seqs, earliest_snapshot_,
|
||||
job_context_->earliest_write_conflict_snapshot,
|
||||
job_context_->GetJobSnapshotSequence(), job_context_->snapshot_checker,
|
||||
env, ShouldReportDetailedTime(env, ioptions.stats),
|
||||
true /* internal key corruption is not ok */, range_del_agg.get(),
|
||||
nullptr, ioptions.allow_data_in_errors,
|
||||
@@ -761,7 +755,7 @@ bool FlushJob::MemPurgeDecider(double threshold) {
|
||||
// Pick the oldest existing snapshot that is more recent
|
||||
// than the sequence number of the sampled entry.
|
||||
min_seqno_snapshot = kMaxSequenceNumber;
|
||||
for (SequenceNumber seq_num : existing_snapshots_) {
|
||||
for (SequenceNumber seq_num : job_context_->snapshot_seqs) {
|
||||
if (seq_num > res.sequence && seq_num < min_seqno_snapshot) {
|
||||
min_seqno_snapshot = seq_num;
|
||||
}
|
||||
@@ -868,7 +862,9 @@ Status FlushJob::WriteLevel0Table() {
|
||||
ts_sz > 0 && !cfd_->ioptions().persist_user_defined_timestamps;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats flush_stats(CompactionReason::kFlush,
|
||||
1 /* count**/);
|
||||
{
|
||||
auto write_hint = base_->storage_info()->CalculateSSTWriteHint(
|
||||
/*level=*/0, db_options_.calculate_sst_write_lifetime_hint_set);
|
||||
@@ -887,7 +883,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
ro.total_order_seek = true;
|
||||
ro.io_activity = Env::IOActivity::kFlush;
|
||||
Arena arena;
|
||||
uint64_t total_num_entries = 0, total_num_deletes = 0;
|
||||
uint64_t total_num_input_entries = 0, total_num_deletes = 0;
|
||||
uint64_t total_data_size = 0;
|
||||
size_t total_memory_usage = 0;
|
||||
uint64_t total_num_range_deletes = 0;
|
||||
@@ -922,7 +918,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
if (range_del_iter != nullptr) {
|
||||
range_del_iters.emplace_back(range_del_iter);
|
||||
}
|
||||
total_num_entries += m->NumEntries();
|
||||
total_num_input_entries += m->NumEntries();
|
||||
total_num_deletes += m->NumDeletion();
|
||||
total_data_size += m->GetDataSize();
|
||||
total_memory_usage += m->ApproximateMemoryUsage();
|
||||
@@ -934,11 +930,12 @@ Status FlushJob::WriteLevel0Table() {
|
||||
// "Write Buffer Full", should make update flush_reason_ accordingly.
|
||||
event_logger_->Log() << "job" << job_context_->job_id << "event"
|
||||
<< "flush_started" << "num_memtables" << mems_.size()
|
||||
<< "num_entries" << total_num_entries << "num_deletes"
|
||||
<< total_num_deletes << "total_data_size"
|
||||
<< total_data_size << "memory_usage"
|
||||
<< total_memory_usage << "num_range_deletes"
|
||||
<< total_num_range_deletes << "flush_reason"
|
||||
<< "total_num_input_entries" << total_num_input_entries
|
||||
<< "num_deletes" << total_num_deletes
|
||||
<< "total_data_size" << total_data_size
|
||||
<< "memory_usage" << total_memory_usage
|
||||
<< "num_range_deletes" << total_num_range_deletes
|
||||
<< "flush_reason"
|
||||
<< GetFlushReasonString(flush_reason_);
|
||||
|
||||
{
|
||||
@@ -976,7 +973,6 @@ Status FlushJob::WriteLevel0Table() {
|
||||
meta_.oldest_ancester_time = oldest_ancester_time;
|
||||
meta_.file_creation_time = current_time;
|
||||
|
||||
uint64_t num_input_entries = 0;
|
||||
uint64_t memtable_payload_bytes = 0;
|
||||
uint64_t memtable_garbage_bytes = 0;
|
||||
IOStatus io_s;
|
||||
@@ -998,28 +994,49 @@ Status FlushJob::WriteLevel0Table() {
|
||||
preclude_last_level_min_seqno_ == kMaxSequenceNumber
|
||||
? preclude_last_level_min_seqno_
|
||||
: std::min(earliest_snapshot_, preclude_last_level_min_seqno_));
|
||||
const SequenceNumber job_snapshot_seq =
|
||||
job_context_->GetJobSnapshotSequence();
|
||||
|
||||
s = BuildTable(
|
||||
dbname_, versions_, db_options_, tboptions, file_options_,
|
||||
cfd_->table_cache(), iter.get(), std::move(range_del_iters), &meta_,
|
||||
&blob_file_additions, existing_snapshots_, earliest_snapshot_,
|
||||
earliest_write_conflict_snapshot_, job_snapshot_seq,
|
||||
snapshot_checker_, mutable_cf_options_.paranoid_file_checks,
|
||||
cfd_->internal_stats(), &io_s, io_tracer_,
|
||||
BlobFileCreationReason::kFlush, seqno_to_time_mapping_.get(),
|
||||
event_logger_, job_context_->job_id, &table_properties_, write_hint,
|
||||
full_history_ts_low, blob_callback_, base_, &num_input_entries,
|
||||
&memtable_payload_bytes, &memtable_garbage_bytes);
|
||||
&blob_file_additions, job_context_->snapshot_seqs, earliest_snapshot_,
|
||||
job_context_->earliest_write_conflict_snapshot,
|
||||
job_context_->GetJobSnapshotSequence(),
|
||||
job_context_->snapshot_checker,
|
||||
mutable_cf_options_.paranoid_file_checks, cfd_->internal_stats(),
|
||||
&io_s, io_tracer_, BlobFileCreationReason::kFlush,
|
||||
seqno_to_time_mapping_.get(), event_logger_, job_context_->job_id,
|
||||
&table_properties_, write_hint, full_history_ts_low, blob_callback_,
|
||||
base_, &memtable_payload_bytes, &memtable_garbage_bytes,
|
||||
&flush_stats);
|
||||
TEST_SYNC_POINT_CALLBACK("FlushJob::WriteLevel0Table:s", &s);
|
||||
// TODO: Cleanup io_status in BuildTable and table builders
|
||||
assert(!s.ok() || io_s.ok());
|
||||
io_s.PermitUncheckedError();
|
||||
if (num_input_entries != total_num_entries && s.ok()) {
|
||||
std::string msg = "Expected " + std::to_string(total_num_entries) +
|
||||
if (s.ok() && total_num_input_entries != flush_stats.num_input_records) {
|
||||
std::string msg = "Expected " +
|
||||
std::to_string(total_num_input_entries) +
|
||||
" entries in memtables, but read " +
|
||||
std::to_string(num_input_entries);
|
||||
std::to_string(flush_stats.num_input_records);
|
||||
ROCKS_LOG_WARN(db_options_.info_log, "[%s] [JOB %d] Level-0 flush %s",
|
||||
cfd_->GetName().c_str(), job_context_->job_id,
|
||||
msg.c_str());
|
||||
if (db_options_.flush_verify_memtable_count) {
|
||||
s = Status::Corruption(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Only verify on table with format collects table properties
|
||||
if (s.ok() &&
|
||||
(mutable_cf_options_.table_factory->IsInstanceOf(
|
||||
TableFactory::kBlockBasedTableName()) ||
|
||||
mutable_cf_options_.table_factory->IsInstanceOf(
|
||||
TableFactory::kPlainTableName())) &&
|
||||
flush_stats.num_output_records != table_properties_.num_entries) {
|
||||
std::string msg =
|
||||
"Number of keys in flush output SST files does not match "
|
||||
"number of keys added to the table. Expected " +
|
||||
std::to_string(flush_stats.num_output_records) + " but there are " +
|
||||
std::to_string(table_properties_.num_entries) +
|
||||
" in output SST files";
|
||||
ROCKS_LOG_WARN(db_options_.info_log, "[%s] [JOB %d] Level-0 flush %s",
|
||||
cfd_->GetName().c_str(), job_context_->job_id,
|
||||
msg.c_str());
|
||||
@@ -1085,12 +1102,10 @@ Status FlushJob::WriteLevel0Table() {
|
||||
// Piggyback FlushJobInfo on the first first flushed memtable.
|
||||
mems_[0]->SetFlushJobInfo(GetFlushJobInfo());
|
||||
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
const uint64_t micros = clock_->NowMicros() - start_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUMicros() - start_cpu_micros;
|
||||
stats.micros = micros;
|
||||
stats.cpu_micros = cpu_micros;
|
||||
flush_stats.micros = micros;
|
||||
flush_stats.cpu_micros = cpu_micros;
|
||||
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Flush lasted %" PRIu64
|
||||
@@ -1099,22 +1114,23 @@ Status FlushJob::WriteLevel0Table() {
|
||||
cpu_micros);
|
||||
|
||||
if (has_output) {
|
||||
stats.bytes_written = meta_.fd.GetFileSize();
|
||||
stats.num_output_files = 1;
|
||||
flush_stats.bytes_written = meta_.fd.GetFileSize();
|
||||
flush_stats.num_output_files = 1;
|
||||
}
|
||||
|
||||
const auto& blobs = edit_->GetBlobFileAdditions();
|
||||
for (const auto& blob : blobs) {
|
||||
stats.bytes_written_blob += blob.GetTotalBlobBytes();
|
||||
flush_stats.bytes_written_blob += blob.GetTotalBlobBytes();
|
||||
}
|
||||
|
||||
stats.num_output_files_blob = static_cast<int>(blobs.size());
|
||||
flush_stats.num_output_files_blob = static_cast<int>(blobs.size());
|
||||
|
||||
RecordTimeToHistogram(stats_, FLUSH_TIME, stats.micros);
|
||||
cfd_->internal_stats()->AddCompactionStats(0 /* level */, thread_pri_, stats);
|
||||
RecordTimeToHistogram(stats_, FLUSH_TIME, flush_stats.micros);
|
||||
cfd_->internal_stats()->AddCompactionStats(0 /* level */, thread_pri_,
|
||||
flush_stats);
|
||||
cfd_->internal_stats()->AddCFStats(
|
||||
InternalStats::BYTES_FLUSHED,
|
||||
stats.bytes_written + stats.bytes_written_blob);
|
||||
flush_stats.bytes_written + flush_stats.bytes_written_blob);
|
||||
RecordFlushIOStats();
|
||||
|
||||
return s;
|
||||
|
||||
+3
-8
@@ -63,11 +63,9 @@ class FlushJob {
|
||||
const MutableCFOptions& mutable_cf_options, uint64_t max_memtable_id,
|
||||
const FileOptions& file_options, VersionSet* versions,
|
||||
InstrumentedMutex* db_mutex, std::atomic<bool>* shutting_down,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||
FlushReason flush_reason, LogBuffer* log_buffer,
|
||||
FSDirectory* db_directory, FSDirectory* output_file_directory,
|
||||
JobContext* job_context, FlushReason flush_reason,
|
||||
LogBuffer* log_buffer, FSDirectory* db_directory,
|
||||
FSDirectory* output_file_directory,
|
||||
CompressionType output_compression, Statistics* stats,
|
||||
EventLogger* event_logger, bool measure_io_stats,
|
||||
const bool sync_output_directory, const bool write_manifest,
|
||||
@@ -167,10 +165,7 @@ class FlushJob {
|
||||
VersionSet* versions_;
|
||||
InstrumentedMutex* db_mutex_;
|
||||
std::atomic<bool>* shutting_down_;
|
||||
std::vector<SequenceNumber> existing_snapshots_;
|
||||
SequenceNumber earliest_snapshot_;
|
||||
SequenceNumber earliest_write_conflict_snapshot_;
|
||||
SnapshotChecker* snapshot_checker_;
|
||||
JobContext* job_context_;
|
||||
FlushReason flush_reason_;
|
||||
LogBuffer* log_buffer_;
|
||||
|
||||
+65
-65
@@ -186,16 +186,16 @@ TEST_F(FlushJobTest, Empty) {
|
||||
JobContext job_context(0);
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, nullptr, &event_logger, false,
|
||||
true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
job_context.InitSnapshotContext(nullptr, nullptr, kMaxSequenceNumber, {});
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */,
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
&job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, nullptr, &event_logger, false,
|
||||
true /* sync_output_directory */,
|
||||
true /* write_manifest */, Env::Priority::USER,
|
||||
nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
flush_job.PickMemTable();
|
||||
@@ -272,16 +272,16 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
}
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
job_context.InitSnapshotContext(nullptr, nullptr, kMaxSequenceNumber, {});
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */,
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
&job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(),
|
||||
&event_logger, true, true /* sync_output_directory */,
|
||||
true /* write_manifest */, Env::Priority::USER,
|
||||
nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
|
||||
HistogramData hist;
|
||||
FileMetaData file_meta;
|
||||
@@ -332,18 +332,18 @@ TEST_F(FlushJobTest, FlushMemTablesSingleColumnFamily) {
|
||||
}
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
|
||||
assert(memtable_ids.size() == num_mems);
|
||||
uint64_t smallest_memtable_id = memtable_ids.front();
|
||||
uint64_t flush_memtable_id = smallest_memtable_id + num_mems_to_flush - 1;
|
||||
job_context.InitSnapshotContext(nullptr, nullptr, kMaxSequenceNumber, {});
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
cfd->GetLatestMutableCFOptions(), flush_memtable_id, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
versions_.get(), &mutex_, &shutting_down_, &job_context,
|
||||
FlushReason::kTest, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true,
|
||||
true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
HistogramData hist;
|
||||
FileMetaData file_meta;
|
||||
@@ -405,18 +405,17 @@ TEST_F(FlushJobTest, FlushMemtablesMultipleColumnFamilies) {
|
||||
}
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relevant
|
||||
std::vector<std::unique_ptr<FlushJob>> flush_jobs;
|
||||
k = 0;
|
||||
job_context.InitSnapshotContext(nullptr, nullptr, kMaxSequenceNumber, {});
|
||||
for (auto cfd : all_cfds) {
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
flush_jobs.emplace_back(new FlushJob(
|
||||
dbname_, cfd, db_options_, cfd->GetLatestMutableCFOptions(),
|
||||
memtable_ids[k], env_options_, versions_.get(), &mutex_,
|
||||
&shutting_down_, snapshot_seqs, kMaxSequenceNumber, snapshot_checker,
|
||||
&job_context, FlushReason::kTest, nullptr, nullptr, nullptr,
|
||||
kNoCompression, db_options_.statistics.get(), &event_logger, true,
|
||||
false /* sync_output_directory */, false /* write_manifest */,
|
||||
&shutting_down_, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, false /* sync_output_directory */, false /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/,
|
||||
empty_seqno_to_time_mapping_));
|
||||
k++;
|
||||
@@ -532,16 +531,17 @@ TEST_F(FlushJobTest, Snapshots) {
|
||||
}
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, snapshots, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
job_context.InitSnapshotContext(nullptr, nullptr, kMaxSequenceNumber,
|
||||
std::move(snapshots));
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */,
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
&job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(),
|
||||
&event_logger, true, true /* sync_output_directory */,
|
||||
true /* write_manifest */, Env::Priority::USER,
|
||||
nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run());
|
||||
@@ -585,18 +585,18 @@ TEST_F(FlushJobTest, GetRateLimiterPriorityForWrite) {
|
||||
}
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
|
||||
assert(memtable_ids.size() == num_mems);
|
||||
uint64_t smallest_memtable_id = memtable_ids.front();
|
||||
uint64_t flush_memtable_id = smallest_memtable_id + num_mems_to_flush - 1;
|
||||
job_context.InitSnapshotContext(nullptr, nullptr, kMaxSequenceNumber, {});
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
cfd->GetLatestMutableCFOptions(), flush_memtable_id, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
versions_.get(), &mutex_, &shutting_down_, &job_context,
|
||||
FlushReason::kTest, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true,
|
||||
true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
|
||||
// When the state from WriteController is normal.
|
||||
@@ -658,16 +658,16 @@ TEST_F(FlushJobTest, ReplaceTimedPutWriteTimeWithPreferredSeqno) {
|
||||
}
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relevant
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, seqno_to_time_mapping);
|
||||
job_context.InitSnapshotContext(nullptr, nullptr, kMaxSequenceNumber, {});
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */,
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
&job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(),
|
||||
&event_logger, true, true /* sync_output_directory */,
|
||||
true /* write_manifest */, Env::Priority::USER,
|
||||
nullptr /*IOTracer*/, seqno_to_time_mapping);
|
||||
|
||||
FileMetaData file_meta;
|
||||
mutex_.Lock();
|
||||
@@ -761,19 +761,19 @@ TEST_P(FlushJobTimestampTest, AllKeysExpired) {
|
||||
}
|
||||
|
||||
std::vector<SequenceNumber> snapshots;
|
||||
constexpr SnapshotChecker* const snapshot_checker = nullptr;
|
||||
JobContext job_context(0);
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
std::string full_history_ts_low;
|
||||
PutFixed64(&full_history_ts_low, std::numeric_limits<uint64_t>::max());
|
||||
cfd->SetFullHistoryTsLow(full_history_ts_low);
|
||||
job_context.InitSnapshotContext(nullptr, nullptr, kMaxSequenceNumber, {});
|
||||
FlushJob flush_job(
|
||||
dbname_, cfd, db_options_, cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, snapshots, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
versions_.get(), &mutex_, &shutting_down_, &job_context,
|
||||
FlushReason::kTest, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true,
|
||||
true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_,
|
||||
/*db_id=*/"",
|
||||
/*db_session_id=*/"", full_history_ts_low);
|
||||
@@ -823,8 +823,8 @@ TEST_P(FlushJobTimestampTest, NoKeyExpired) {
|
||||
}
|
||||
|
||||
std::vector<SequenceNumber> snapshots;
|
||||
SnapshotChecker* const snapshot_checker = nullptr;
|
||||
JobContext job_context(0);
|
||||
job_context.InitSnapshotContext(nullptr, nullptr, kMaxSequenceNumber, {});
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
std::string full_history_ts_low;
|
||||
PutFixed64(&full_history_ts_low, 0);
|
||||
@@ -832,10 +832,10 @@ TEST_P(FlushJobTimestampTest, NoKeyExpired) {
|
||||
FlushJob flush_job(
|
||||
dbname_, cfd, db_options_, cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, snapshots, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
versions_.get(), &mutex_, &shutting_down_, &job_context,
|
||||
FlushReason::kTest, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true,
|
||||
true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_,
|
||||
/*db_id=*/"",
|
||||
/*db_session_id=*/"", full_history_ts_low);
|
||||
|
||||
@@ -42,6 +42,7 @@ using MinIterHeap =
|
||||
std::priority_queue<InternalIterator*, std::vector<InternalIterator*>,
|
||||
MinIterComparator>;
|
||||
|
||||
// TODO: name to TailingIterator
|
||||
/**
|
||||
* ForwardIterator is a special type of iterator that only supports Seek()
|
||||
* and Next(). It is expected to perform better than TailingIterator by
|
||||
|
||||
@@ -324,7 +324,8 @@ Status ImportColumnFamilyJob::GetIngestedFileInfo(
|
||||
status = sv->mutable_cf_options.table_factory->NewTableReader(
|
||||
TableReaderOptions(
|
||||
cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor,
|
||||
env_options_, cfd_->internal_comparator(),
|
||||
sv->mutable_cf_options.compression_manager.get(), env_options_,
|
||||
cfd_->internal_comparator(),
|
||||
sv->mutable_cf_options.block_protection_bytes_per_key,
|
||||
/*skip_filters*/ false, /*immortal*/ false,
|
||||
/*force_direct_prefetch*/ false, /*level*/ -1,
|
||||
|
||||
+15
-8
@@ -45,6 +45,8 @@ const std::map<LevelStatType, LevelStat> InternalStats::compaction_level_stats =
|
||||
{LevelStatType::RN_GB, LevelStat{"RnGB", "Rn(GB)"}},
|
||||
{LevelStatType::RNP1_GB, LevelStat{"Rnp1GB", "Rnp1(GB)"}},
|
||||
{LevelStatType::WRITE_GB, LevelStat{"WriteGB", "Write(GB)"}},
|
||||
{LevelStatType::WRITE_PRE_COMP_GB,
|
||||
LevelStat{"WPreCompGB", "WPreComp(GB)"}},
|
||||
{LevelStatType::W_NEW_GB, LevelStat{"WnewGB", "Wnew(GB)"}},
|
||||
{LevelStatType::MOVED_GB, LevelStat{"MovedGB", "Moved(GB)"}},
|
||||
{LevelStatType::WRITE_AMP, LevelStat{"WriteAmp", "W-Amp"}},
|
||||
@@ -100,19 +102,20 @@ void PrintLevelStatsHeader(char* buf, size_t len, const std::string& cf_name,
|
||||
int line_size = snprintf(
|
||||
buf + written_size, len - written_size,
|
||||
"%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s "
|
||||
"%s "
|
||||
"%s\n",
|
||||
// Note that we skip COMPACTED_FILES and merge it with Files column
|
||||
group_by.c_str(), hdr(LevelStatType::NUM_FILES),
|
||||
hdr(LevelStatType::SIZE_BYTES), hdr(LevelStatType::SCORE),
|
||||
hdr(LevelStatType::READ_GB), hdr(LevelStatType::RN_GB),
|
||||
hdr(LevelStatType::RNP1_GB), hdr(LevelStatType::WRITE_GB),
|
||||
hdr(LevelStatType::W_NEW_GB), hdr(LevelStatType::MOVED_GB),
|
||||
hdr(LevelStatType::WRITE_AMP), hdr(LevelStatType::READ_MBPS),
|
||||
hdr(LevelStatType::WRITE_MBPS), hdr(LevelStatType::COMP_SEC),
|
||||
hdr(LevelStatType::COMP_CPU_SEC), hdr(LevelStatType::COMP_COUNT),
|
||||
hdr(LevelStatType::AVG_SEC), hdr(LevelStatType::KEY_IN),
|
||||
hdr(LevelStatType::KEY_DROP), hdr(LevelStatType::R_BLOB_GB),
|
||||
hdr(LevelStatType::W_BLOB_GB));
|
||||
hdr(LevelStatType::WRITE_PRE_COMP_GB), hdr(LevelStatType::W_NEW_GB),
|
||||
hdr(LevelStatType::MOVED_GB), hdr(LevelStatType::WRITE_AMP),
|
||||
hdr(LevelStatType::READ_MBPS), hdr(LevelStatType::WRITE_MBPS),
|
||||
hdr(LevelStatType::COMP_SEC), hdr(LevelStatType::COMP_CPU_SEC),
|
||||
hdr(LevelStatType::COMP_COUNT), hdr(LevelStatType::AVG_SEC),
|
||||
hdr(LevelStatType::KEY_IN), hdr(LevelStatType::KEY_DROP),
|
||||
hdr(LevelStatType::R_BLOB_GB), hdr(LevelStatType::W_BLOB_GB));
|
||||
|
||||
written_size += line_size;
|
||||
written_size = std::min(written_size, static_cast<int>(len));
|
||||
@@ -140,6 +143,8 @@ void PrepareLevelStats(std::map<LevelStatType, double>* level_stats,
|
||||
stats.bytes_read_non_output_levels / kGB;
|
||||
(*level_stats)[LevelStatType::RNP1_GB] = stats.bytes_read_output_level / kGB;
|
||||
(*level_stats)[LevelStatType::WRITE_GB] = stats.bytes_written / kGB;
|
||||
(*level_stats)[LevelStatType::WRITE_PRE_COMP_GB] =
|
||||
stats.bytes_written_pre_comp / kGB;
|
||||
(*level_stats)[LevelStatType::W_NEW_GB] = bytes_new / kGB;
|
||||
(*level_stats)[LevelStatType::MOVED_GB] = stats.bytes_moved / kGB;
|
||||
(*level_stats)[LevelStatType::WRITE_AMP] = w_amp;
|
||||
@@ -164,12 +169,13 @@ void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
buf, len,
|
||||
"%4s " /* Level */
|
||||
"%6d/%-3d " /* Files */
|
||||
"%8s " /* Size */
|
||||
"%10s " /* Size */
|
||||
"%5.1f " /* Score */
|
||||
"%8.1f " /* Read(GB) */
|
||||
"%7.1f " /* Rn(GB) */
|
||||
"%8.1f " /* Rnp1(GB) */
|
||||
"%9.1f " /* Write(GB) */
|
||||
"%9.1f " /* WPreComp(GB) */
|
||||
"%8.1f " /* Wnew(GB) */
|
||||
"%9.1f " /* Moved(GB) */
|
||||
"%5.1f " /* W-Amp */
|
||||
@@ -193,6 +199,7 @@ void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
stat_value.at(LevelStatType::RN_GB),
|
||||
stat_value.at(LevelStatType::RNP1_GB),
|
||||
stat_value.at(LevelStatType::WRITE_GB),
|
||||
stat_value.at(LevelStatType::WRITE_PRE_COMP_GB),
|
||||
stat_value.at(LevelStatType::W_NEW_GB),
|
||||
stat_value.at(LevelStatType::MOVED_GB),
|
||||
stat_value.at(LevelStatType::WRITE_AMP),
|
||||
|
||||
+15
-1
@@ -71,6 +71,7 @@ enum class LevelStatType {
|
||||
RN_GB,
|
||||
RNP1_GB,
|
||||
WRITE_GB,
|
||||
WRITE_PRE_COMP_GB,
|
||||
W_NEW_GB,
|
||||
MOVED_GB,
|
||||
WRITE_AMP,
|
||||
@@ -179,6 +180,9 @@ class InternalStats {
|
||||
// Total number of bytes written to table files during compaction
|
||||
uint64_t bytes_written;
|
||||
|
||||
// Total number of bytes written pre-compression during compaction
|
||||
uint64_t bytes_written_pre_comp;
|
||||
|
||||
// Total number of bytes written to blob files during compaction
|
||||
uint64_t bytes_written_blob;
|
||||
|
||||
@@ -231,6 +235,7 @@ class InternalStats {
|
||||
bytes_skipped_output_level(0),
|
||||
bytes_read_blob(0),
|
||||
bytes_written(0),
|
||||
bytes_written_pre_comp(0),
|
||||
bytes_written_blob(0),
|
||||
bytes_moved(0),
|
||||
num_input_files_in_non_output_levels(0),
|
||||
@@ -258,6 +263,7 @@ class InternalStats {
|
||||
bytes_skipped_output_level(0),
|
||||
bytes_read_blob(0),
|
||||
bytes_written(0),
|
||||
bytes_written_pre_comp(0),
|
||||
bytes_written_blob(0),
|
||||
bytes_moved(0),
|
||||
num_input_files_in_non_output_levels(0),
|
||||
@@ -291,6 +297,7 @@ class InternalStats {
|
||||
bytes_skipped_output_level(c.bytes_skipped_output_level),
|
||||
bytes_read_blob(c.bytes_read_blob),
|
||||
bytes_written(c.bytes_written),
|
||||
bytes_written_pre_comp(c.bytes_written_pre_comp),
|
||||
bytes_written_blob(c.bytes_written_blob),
|
||||
bytes_moved(c.bytes_moved),
|
||||
num_input_files_in_non_output_levels(
|
||||
@@ -321,6 +328,7 @@ class InternalStats {
|
||||
bytes_skipped_output_level = c.bytes_skipped_output_level;
|
||||
bytes_read_blob = c.bytes_read_blob;
|
||||
bytes_written = c.bytes_written;
|
||||
bytes_written_pre_comp = c.bytes_written_pre_comp;
|
||||
bytes_written_blob = c.bytes_written_blob;
|
||||
bytes_moved = c.bytes_moved;
|
||||
num_input_files_in_non_output_levels =
|
||||
@@ -353,6 +361,7 @@ class InternalStats {
|
||||
this->bytes_skipped_output_level = 0;
|
||||
this->bytes_read_blob = 0;
|
||||
this->bytes_written = 0;
|
||||
this->bytes_written_pre_comp = 0;
|
||||
this->bytes_written_blob = 0;
|
||||
this->bytes_moved = 0;
|
||||
this->num_input_files_in_non_output_levels = 0;
|
||||
@@ -381,6 +390,7 @@ class InternalStats {
|
||||
this->bytes_skipped_output_level += c.bytes_skipped_output_level;
|
||||
this->bytes_read_blob += c.bytes_read_blob;
|
||||
this->bytes_written += c.bytes_written;
|
||||
this->bytes_written_pre_comp += c.bytes_written_pre_comp;
|
||||
this->bytes_written_blob += c.bytes_written_blob;
|
||||
this->bytes_moved += c.bytes_moved;
|
||||
this->num_input_files_in_non_output_levels +=
|
||||
@@ -413,6 +423,7 @@ class InternalStats {
|
||||
this->bytes_skipped_output_level -= c.bytes_skipped_output_level;
|
||||
this->bytes_read_blob -= c.bytes_read_blob;
|
||||
this->bytes_written -= c.bytes_written;
|
||||
this->bytes_written_pre_comp -= c.bytes_written_pre_comp;
|
||||
this->bytes_written_blob -= c.bytes_written_blob;
|
||||
this->bytes_moved -= c.bytes_moved;
|
||||
this->num_input_files_in_non_output_levels -=
|
||||
@@ -697,7 +708,10 @@ class InternalStats {
|
||||
// a full cache, which would force a re-scan on the next GetStats.
|
||||
std::shared_ptr<CacheEntryStatsCollector<CacheEntryRoleStats>>
|
||||
cache_entry_stats_collector_;
|
||||
// Per-ColumnFamily/level compaction stats
|
||||
|
||||
// Per-column family and level compaction statistics, including flush and file
|
||||
// ingestion. These are treated as compactions to L0 or the level where the
|
||||
// file was ingested.
|
||||
std::vector<CompactionStats> comp_stats_;
|
||||
std::vector<CompactionStats> comp_stats_by_pri_;
|
||||
CompactionStats per_key_placement_comp_stats_;
|
||||
|
||||
+49
-11
@@ -135,6 +135,37 @@ struct JobContext {
|
||||
return kMaxSequenceNumber;
|
||||
}
|
||||
|
||||
SequenceNumber GetLatestSnapshotSequence() const {
|
||||
assert(snapshot_context_initialized);
|
||||
if (snapshot_seqs.empty()) {
|
||||
return 0;
|
||||
}
|
||||
return snapshot_seqs.back();
|
||||
}
|
||||
|
||||
SequenceNumber GetEarliestSnapshotSequence() const {
|
||||
assert(snapshot_context_initialized);
|
||||
if (snapshot_seqs.empty()) {
|
||||
return kMaxSequenceNumber;
|
||||
}
|
||||
return snapshot_seqs.front();
|
||||
}
|
||||
|
||||
void InitSnapshotContext(SnapshotChecker* checker,
|
||||
std::unique_ptr<ManagedSnapshot> managed_snapshot,
|
||||
SequenceNumber earliest_write_conflict,
|
||||
std::vector<SequenceNumber>&& snapshots) {
|
||||
if (snapshot_context_initialized) {
|
||||
return;
|
||||
}
|
||||
snapshot_context_initialized = true;
|
||||
snapshot_checker = checker;
|
||||
assert(!job_snapshot);
|
||||
job_snapshot = std::move(managed_snapshot);
|
||||
earliest_write_conflict_snapshot = earliest_write_conflict;
|
||||
snapshot_seqs = std::move(snapshots);
|
||||
}
|
||||
|
||||
// Structure to store information for candidate files to delete.
|
||||
struct CandidateFileInfo {
|
||||
std::string file_name;
|
||||
@@ -146,9 +177,6 @@ struct JobContext {
|
||||
}
|
||||
};
|
||||
|
||||
// Unique job id
|
||||
int job_id;
|
||||
|
||||
// a list of all files that we'll consider deleting
|
||||
// (every once in a while this is filled up with all files
|
||||
// in the DB directory)
|
||||
@@ -197,14 +225,14 @@ struct JobContext {
|
||||
|
||||
// the current manifest_file_number, log_number and prev_log_number
|
||||
// that corresponds to the set of files in 'live'.
|
||||
uint64_t manifest_file_number;
|
||||
uint64_t pending_manifest_file_number;
|
||||
uint64_t manifest_file_number = 0;
|
||||
uint64_t pending_manifest_file_number = 0;
|
||||
|
||||
// Used for remote compaction. To prevent OPTIONS files from getting
|
||||
// purged by PurgeObsoleteFiles() of the primary host
|
||||
uint64_t min_options_file_number;
|
||||
uint64_t log_number;
|
||||
uint64_t prev_log_number;
|
||||
uint64_t log_number = 0;
|
||||
uint64_t prev_log_number = 0;
|
||||
|
||||
uint64_t min_pending_output = 0;
|
||||
uint64_t prev_wals_total_size = 0;
|
||||
@@ -213,17 +241,27 @@ struct JobContext {
|
||||
|
||||
// Snapshot taken before flush/compaction job.
|
||||
std::unique_ptr<ManagedSnapshot> job_snapshot;
|
||||
SnapshotChecker* snapshot_checker = nullptr;
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
// This is the earliest snapshot that could be used for write-conflict
|
||||
// checking by a transaction. For any user-key newer than this snapshot, we
|
||||
// should make sure not to remove evidence that a write occurred.
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber;
|
||||
|
||||
// Unique job id
|
||||
int job_id;
|
||||
|
||||
bool snapshot_context_initialized = false;
|
||||
|
||||
explicit JobContext(int _job_id, bool create_superversion = false) {
|
||||
job_id = _job_id;
|
||||
manifest_file_number = 0;
|
||||
pending_manifest_file_number = 0;
|
||||
log_number = 0;
|
||||
prev_log_number = 0;
|
||||
superversion_contexts.emplace_back(
|
||||
SuperVersionContext(create_superversion));
|
||||
}
|
||||
|
||||
// Delete the default constructor
|
||||
JobContext() = delete;
|
||||
|
||||
// For non-empty JobContext Clean() has to be called at least once before
|
||||
// before destruction (see asserts in ~JobContext()). Should be called with
|
||||
// unlocked DB mutex. Destructor doesn't call Clean() to avoid accidentally
|
||||
|
||||
@@ -537,6 +537,47 @@ TEST_F(EventListenerTest, DisableBGCompaction) {
|
||||
ASSERT_GE(listener->slowdown_count, kSlowdownTrigger * 9);
|
||||
}
|
||||
|
||||
class TestNumInputFilesTotalInputBytesPouplatedInListener
|
||||
: public EventListener {
|
||||
public:
|
||||
void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
num_input_files = ci.stats.num_input_files;
|
||||
total_num_of_bytes = ci.stats.total_input_bytes;
|
||||
}
|
||||
size_t num_input_files = 0;
|
||||
size_t total_num_of_bytes = 0;
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
TEST_F(EventListenerTest, NumInputFilesTotalBytesPopulated) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = CurrentOptions().env;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(test::NewSpecialSkipListFactory(
|
||||
DBTestBase::kNumKeysByGenerateNewRandomFile));
|
||||
|
||||
TestNumInputFilesTotalInputBytesPouplatedInListener* listener =
|
||||
new TestNumInputFilesTotalInputBytesPouplatedInListener();
|
||||
options.listeners.emplace_back(listener);
|
||||
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
ASSERT_EQ(listener->num_input_files, 0);
|
||||
ASSERT_EQ(listener->total_num_of_bytes, 0);
|
||||
// Write 4 files in L0
|
||||
for (int i = 0; i < 4; i++) {
|
||||
GenerateNewRandomFile(&rnd);
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(listener->num_input_files, 4);
|
||||
ASSERT_NE(listener->total_num_of_bytes, 0);
|
||||
}
|
||||
|
||||
class TestCompactionReasonListener : public EventListener {
|
||||
public:
|
||||
void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
|
||||
+11
-11
@@ -95,7 +95,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
uint64_t prospective_record_offset = 0;
|
||||
|
||||
Slice fragment;
|
||||
while (true) {
|
||||
for (;;) {
|
||||
uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size();
|
||||
size_t drop_size = 0;
|
||||
const uint8_t record_type =
|
||||
@@ -140,7 +140,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
prospective_record_offset = physical_record_offset;
|
||||
scratch->assign(fragment.data(), fragment.size());
|
||||
in_fragmented_record = true;
|
||||
break;
|
||||
break; // switch
|
||||
|
||||
case kMiddleType:
|
||||
case kRecyclableMiddleType:
|
||||
@@ -153,7 +153,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
}
|
||||
scratch->append(fragment.data(), fragment.size());
|
||||
}
|
||||
break;
|
||||
break; // switch
|
||||
|
||||
case kLastType:
|
||||
case kRecyclableLastType:
|
||||
@@ -171,7 +171,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
first_record_read_ = true;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
break; // switch
|
||||
|
||||
case kSetCompressionType: {
|
||||
if (compression_type_record_read_) {
|
||||
@@ -193,7 +193,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
} else {
|
||||
InitCompression(compression_record);
|
||||
}
|
||||
break;
|
||||
break; // switch
|
||||
}
|
||||
case kPredecessorWALInfoType:
|
||||
case kRecyclePredecessorWALInfoType: {
|
||||
@@ -210,7 +210,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
MaybeVerifyPredecessorWALInfo(wal_recovery_mode, fragment,
|
||||
recorded_predecessor_wal_info);
|
||||
}
|
||||
break;
|
||||
break; // switch
|
||||
}
|
||||
case kUserDefinedTimestampSizeType:
|
||||
case kRecyclableUserDefinedTimestampSizeType: {
|
||||
@@ -235,7 +235,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
ReportCorruption(fragment.size(), s.getState());
|
||||
}
|
||||
}
|
||||
break;
|
||||
break; // switch
|
||||
}
|
||||
|
||||
case kBadHeader:
|
||||
@@ -304,7 +304,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
in_fragmented_record = false;
|
||||
scratch->clear();
|
||||
}
|
||||
break;
|
||||
break; // switch
|
||||
|
||||
case kBadRecordLen:
|
||||
if (eof_) {
|
||||
@@ -337,7 +337,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
in_fragmented_record = false;
|
||||
scratch->clear();
|
||||
}
|
||||
break;
|
||||
break; // switch
|
||||
|
||||
default: {
|
||||
if ((record_type & kRecordTypeSafeIgnoreMask) == 0) {
|
||||
@@ -349,11 +349,11 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
}
|
||||
in_fragmented_record = false;
|
||||
scratch->clear();
|
||||
break;
|
||||
break; // switch
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
// unreachable
|
||||
}
|
||||
|
||||
void Reader::MaybeVerifyPredecessorWALInfo(
|
||||
|
||||
+2
-1
@@ -196,10 +196,11 @@ bool MemTable::ShouldFlushNow() {
|
||||
// allocate one more block.
|
||||
const double kAllowOverAllocationRatio = 0.6;
|
||||
|
||||
// range deletion use skip list which allocates all memeory through `arena_`
|
||||
assert(range_del_table_->ApproximateMemoryUsage() == 0);
|
||||
// If arena still have room for new block allocation, we can safely say it
|
||||
// shouldn't flush.
|
||||
auto allocated_memory = table_->ApproximateMemoryUsage() +
|
||||
range_del_table_->ApproximateMemoryUsage() +
|
||||
arena_.MemoryAllocatedBytes();
|
||||
|
||||
approximate_memory_usage_.store(allocated_memory, std::memory_order_relaxed);
|
||||
|
||||
@@ -688,6 +688,7 @@ class MemTable final : public ReadOnlyMemTable {
|
||||
// Update counters and flush status after inserting a whole write batch
|
||||
// Used in concurrent memtable inserts.
|
||||
void BatchPostProcess(const MemTablePostProcessInfo& update_counters) {
|
||||
table_->BatchPostProcess();
|
||||
num_entries_.fetch_add(update_counters.num_entries,
|
||||
std::memory_order_relaxed);
|
||||
data_size_.fetch_add(update_counters.data_size, std::memory_order_relaxed);
|
||||
@@ -698,6 +699,10 @@ class MemTable final : public ReadOnlyMemTable {
|
||||
if (update_counters.num_range_deletes > 0) {
|
||||
num_range_deletes_.fetch_add(update_counters.num_range_deletes,
|
||||
std::memory_order_relaxed);
|
||||
// noop for skip-list memtable
|
||||
// Besides correctness test in stress test, memtable flush record count
|
||||
// check will catch this if it were not noop.
|
||||
// range_del_table_->BatchPostProcess();
|
||||
}
|
||||
UpdateFlushState();
|
||||
}
|
||||
|
||||
+1
-1
@@ -250,7 +250,7 @@ class MergeHelper {
|
||||
// Parallel with keys_; stores the operands
|
||||
mutable MergeContext merge_context_;
|
||||
|
||||
StopWatchNano filter_timer_;
|
||||
StopWatchNano<> filter_timer_;
|
||||
uint64_t total_filter_time_;
|
||||
Statistics* stats_;
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "rocksdb/db.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
using MultiScanIterator = MultiScan::MultiScanIterator;
|
||||
|
||||
MultiScan::MultiScan(const ReadOptions& read_options,
|
||||
const std::vector<ScanOptions>& scan_opts, DB* db,
|
||||
ColumnFamilyHandle* cfh)
|
||||
: read_options_(read_options), scan_opts_(scan_opts), db_(db), cfh_(cfh) {
|
||||
bool slow_path = false;
|
||||
// Setup read_options with iterate_uuper_bound based on the first scan.
|
||||
// Subsequent scans will update and allocate a new DB iterator as necessary
|
||||
if (scan_opts[0].range.limit) {
|
||||
upper_bound_ = *scan_opts[0].range.limit;
|
||||
read_options_.iterate_upper_bound = &upper_bound_;
|
||||
} else {
|
||||
read_options_.iterate_upper_bound = nullptr;
|
||||
}
|
||||
for (auto opts : scan_opts) {
|
||||
// Check that all the ScanOptions either specify an upper bound or not. If
|
||||
// its mixed we take the slow path which avoids calling Prepare: we have to
|
||||
// reallocate the Iterator with updated read_options everytime we switch
|
||||
// between upper bound or no upper bound, which complicates Prepare.
|
||||
if (opts.range.limit.has_value() != scan_opts[0].range.limit.has_value()) {
|
||||
slow_path = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
db_iter_.reset(db->NewIterator(read_options_, cfh));
|
||||
if (!slow_path) {
|
||||
db_iter_->Prepare(scan_opts);
|
||||
}
|
||||
}
|
||||
|
||||
MultiScanIterator& MultiScanIterator::operator++() {
|
||||
if (idx_ >= scan_opts_.size()) {
|
||||
throw std::logic_error("Index out of range");
|
||||
}
|
||||
idx_++;
|
||||
if (idx_ < scan_opts_.size()) {
|
||||
// Check if we need to update read_options_
|
||||
if (scan_opts_[idx_].range.limit.has_value() !=
|
||||
(read_options_.iterate_upper_bound != nullptr)) {
|
||||
if (scan_opts_[idx_].range.limit) {
|
||||
*upper_bound_ = *scan_opts_[idx_].range.limit;
|
||||
read_options_.iterate_upper_bound = upper_bound_;
|
||||
} else {
|
||||
read_options_.iterate_upper_bound = nullptr;
|
||||
}
|
||||
db_iter_.reset(db_->NewIterator(read_options_, cfh_));
|
||||
scan_.Reset(db_iter_.get());
|
||||
} else if (scan_opts_[idx_].range.limit) {
|
||||
*upper_bound_ = *scan_opts_[idx_].range.limit;
|
||||
}
|
||||
db_iter_->Seek(*scan_opts_[idx_].range.start);
|
||||
status_ = db_iter_->status();
|
||||
if (!status_.ok()) {
|
||||
throw MultiScanException(status_);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -26,6 +26,7 @@ static const std::map<PeriodicTaskType, uint64_t> kDefaultPeriodSeconds = {
|
||||
{PeriodicTaskType::kPersistStats, kInvalidPeriodSec},
|
||||
{PeriodicTaskType::kFlushInfoLog, 10},
|
||||
{PeriodicTaskType::kRecordSeqnoTime, kInvalidPeriodSec},
|
||||
{PeriodicTaskType::kTriggerCompaction, 12 * 60 * 60} // 12 hours
|
||||
};
|
||||
|
||||
static const std::map<PeriodicTaskType, std::string> kPeriodicTaskTypeNames = {
|
||||
@@ -33,16 +34,20 @@ static const std::map<PeriodicTaskType, std::string> kPeriodicTaskTypeNames = {
|
||||
{PeriodicTaskType::kPersistStats, "pst_st"},
|
||||
{PeriodicTaskType::kFlushInfoLog, "flush_info_log"},
|
||||
{PeriodicTaskType::kRecordSeqnoTime, "record_seq_time"},
|
||||
{PeriodicTaskType::kTriggerCompaction, "trigger_compaction"},
|
||||
};
|
||||
|
||||
Status PeriodicTaskScheduler::Register(PeriodicTaskType task_type,
|
||||
const PeriodicTaskFunc& fn) {
|
||||
return Register(task_type, fn, kDefaultPeriodSeconds.at(task_type));
|
||||
const PeriodicTaskFunc& fn,
|
||||
bool run_immediately) {
|
||||
return Register(task_type, fn, kDefaultPeriodSeconds.at(task_type),
|
||||
run_immediately);
|
||||
}
|
||||
|
||||
Status PeriodicTaskScheduler::Register(PeriodicTaskType task_type,
|
||||
const PeriodicTaskFunc& fn,
|
||||
uint64_t repeat_period_seconds) {
|
||||
uint64_t repeat_period_seconds,
|
||||
bool run_immediately) {
|
||||
MutexLock l(&timer_mutex);
|
||||
static std::atomic<uint64_t> initial_delay(0);
|
||||
|
||||
@@ -65,10 +70,13 @@ Status PeriodicTaskScheduler::Register(PeriodicTaskType task_type,
|
||||
std::string unique_id =
|
||||
kPeriodicTaskTypeNames.at(task_type) + std::to_string(id_++);
|
||||
|
||||
bool succeeded = timer_->Add(
|
||||
fn, unique_id,
|
||||
(initial_delay.fetch_add(1) % repeat_period_seconds) * kMicrosInSecond,
|
||||
repeat_period_seconds * kMicrosInSecond);
|
||||
uint64_t initial_delay_micros =
|
||||
(initial_delay.fetch_add(1) % repeat_period_seconds) * kMicrosInSecond;
|
||||
if (!run_immediately) {
|
||||
initial_delay_micros += repeat_period_seconds * kMicrosInSecond;
|
||||
}
|
||||
bool succeeded = timer_->Add(fn, unique_id, initial_delay_micros,
|
||||
repeat_period_seconds * kMicrosInSecond);
|
||||
if (!succeeded) {
|
||||
return Status::Aborted("Failed to register periodic task");
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ enum class PeriodicTaskType : uint8_t {
|
||||
kPersistStats,
|
||||
kFlushInfoLog,
|
||||
kRecordSeqnoTime,
|
||||
kTriggerCompaction,
|
||||
kMax,
|
||||
};
|
||||
|
||||
@@ -42,13 +43,16 @@ class PeriodicTaskScheduler {
|
||||
PeriodicTaskScheduler& operator=(PeriodicTaskScheduler&&) = delete;
|
||||
|
||||
// Register a task with its default repeat period. Thread safe call.
|
||||
Status Register(PeriodicTaskType task_type, const PeriodicTaskFunc& fn);
|
||||
// @param run_immediately If true, the task will run soon after it's
|
||||
// scheduled, instead of waiting for the repeat period.
|
||||
Status Register(PeriodicTaskType task_type, const PeriodicTaskFunc& fn,
|
||||
bool run_immediately);
|
||||
|
||||
// Register a task with specified repeat period. 0 is an invalid argument
|
||||
// (kInvalidPeriodSec). To stop the task, please use Unregister().
|
||||
// Thread safe call.
|
||||
Status Register(PeriodicTaskType task_type, const PeriodicTaskFunc& fn,
|
||||
uint64_t repeat_period_seconds);
|
||||
uint64_t repeat_period_seconds, bool run_immediately);
|
||||
|
||||
// Unregister the task. Thread safe call.
|
||||
Status Unregister(PeriodicTaskType task_type);
|
||||
|
||||
@@ -56,6 +56,12 @@ TEST_F(PeriodicTaskSchedulerTest, Basic) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushInfoLog:StartRunning",
|
||||
[&](void*) { flush_info_log_counter++; });
|
||||
|
||||
int trigger_compaction_counter = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::TriggerPeriodicCompaction:StartRunning",
|
||||
[&](void*) { trigger_compaction_counter++; });
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Reopen(options);
|
||||
@@ -70,7 +76,7 @@ TEST_F(PeriodicTaskSchedulerTest, Basic) {
|
||||
|
||||
const PeriodicTaskScheduler& scheduler =
|
||||
dbfull()->TEST_GetPeriodicTaskScheduler();
|
||||
ASSERT_EQ(3, scheduler.TEST_GetValidTaskNum());
|
||||
ASSERT_EQ((int)PeriodicTaskType::kMax - 1, scheduler.TEST_GetValidTaskNum());
|
||||
|
||||
ASSERT_EQ(1, dump_st_counter);
|
||||
ASSERT_EQ(1, pst_st_counter);
|
||||
@@ -103,14 +109,14 @@ TEST_F(PeriodicTaskSchedulerTest, Basic) {
|
||||
ASSERT_EQ(3, pst_st_counter);
|
||||
ASSERT_EQ(4, flush_info_log_counter);
|
||||
|
||||
ASSERT_EQ(1u, scheduler.TEST_GetValidTaskNum());
|
||||
ASSERT_EQ(2u, scheduler.TEST_GetValidTaskNum());
|
||||
|
||||
// Re-enable one task
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_dump_period_sec", "5"}}));
|
||||
ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_dump_period_sec);
|
||||
ASSERT_EQ(0u, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
|
||||
ASSERT_EQ(2, scheduler.TEST_GetValidTaskNum());
|
||||
ASSERT_EQ(3, scheduler.TEST_GetValidTaskNum());
|
||||
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(static_cast<int>(kPeriodSec)); });
|
||||
@@ -118,6 +124,16 @@ TEST_F(PeriodicTaskSchedulerTest, Basic) {
|
||||
ASSERT_EQ(3, pst_st_counter);
|
||||
ASSERT_EQ(5, flush_info_log_counter);
|
||||
|
||||
ASSERT_EQ(0, trigger_compaction_counter);
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun([&] {
|
||||
mock_clock_->MockSleepForSeconds(static_cast<int>(12 * 60 * 60));
|
||||
});
|
||||
ASSERT_EQ(1, trigger_compaction_counter);
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun([&] {
|
||||
mock_clock_->MockSleepForSeconds(static_cast<int>(12 * 60 * 60));
|
||||
});
|
||||
ASSERT_EQ(2, trigger_compaction_counter);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -150,7 +166,9 @@ TEST_F(PeriodicTaskSchedulerTest, MultiInstances) {
|
||||
auto dbi = static_cast_with_check<DBImpl>(dbs[kInstanceNum - 1]);
|
||||
|
||||
const PeriodicTaskScheduler& scheduler = dbi->TEST_GetPeriodicTaskScheduler();
|
||||
ASSERT_EQ(kInstanceNum * 3, scheduler.TEST_GetValidTaskNum());
|
||||
// kRecordSeqnoTime is not registered since the feature is not enabled
|
||||
ASSERT_EQ(kInstanceNum * ((int)PeriodicTaskType::kMax - 1),
|
||||
scheduler.TEST_GetValidTaskNum());
|
||||
|
||||
int expected_run = kInstanceNum;
|
||||
dbi->TEST_WaitForPeriodicTaskRun(
|
||||
|
||||
+2
-1
@@ -146,7 +146,8 @@ Status TableCache::GetTableReader(
|
||||
s = mutable_cf_options.table_factory->NewTableReader(
|
||||
ro,
|
||||
TableReaderOptions(
|
||||
ioptions_, mutable_cf_options.prefix_extractor, file_options,
|
||||
ioptions_, mutable_cf_options.prefix_extractor,
|
||||
mutable_cf_options.compression_manager.get(), file_options,
|
||||
internal_comparator,
|
||||
mutable_cf_options.block_protection_bytes_per_key, skip_filters,
|
||||
immortal_tables_, false /* force_direct_prefetch */, level,
|
||||
|
||||
@@ -1135,6 +1135,15 @@ void DumpManifestHandler::CheckIterationResult(const log::Reader& reader,
|
||||
// Print out DebugStrings. Can include non-terminating null characters.
|
||||
fwrite(cfd->current()->DebugString(hex_).data(), sizeof(char),
|
||||
cfd->current()->DebugString(hex_).size(), stdout);
|
||||
|
||||
fprintf(stdout,
|
||||
"By default, manifest file dump prints LSM trees as if %d levels "
|
||||
"were configured, "
|
||||
"which is not necessarily true for the column family (CF) this "
|
||||
"manifest is associated with. "
|
||||
"Please consult other DB files, such as the OPTIONS file, to "
|
||||
"confirm.\n",
|
||||
cfd->ioptions().num_levels);
|
||||
}
|
||||
fprintf(stdout,
|
||||
"next_file_number %" PRIu64 " last_sequence %" PRIu64
|
||||
|
||||
+81
-14
@@ -95,6 +95,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
|
||||
using ScanOptionsMap = std::unordered_map<size_t, std::vector<ScanOptions>>;
|
||||
|
||||
// Find File in LevelFilesBrief data structure
|
||||
// Within an index range defined by left and right
|
||||
int FindFileInRange(const InternalKeyComparator& icmp,
|
||||
@@ -1100,9 +1102,47 @@ class LevelIterator final : public InternalIterator {
|
||||
}
|
||||
|
||||
void Prepare(const std::vector<ScanOptions>* scan_opts) override {
|
||||
// We assume here that scan_opts is sorted such that
|
||||
// scan_opts[0].range.start < scan_opts[1].range.start, and non overlapping
|
||||
scan_opts_ = scan_opts;
|
||||
if (file_iter_.iter()) {
|
||||
file_iter_.Prepare(scan_opts_);
|
||||
if (scan_opts_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
file_to_scan_opts_ = std::make_unique<ScanOptionsMap>();
|
||||
for (size_t k = 0; k < scan_opts_->size(); k++) {
|
||||
const ScanOptions& opt = scan_opts_->at(k);
|
||||
auto start = opt.range.start;
|
||||
auto end = opt.range.limit;
|
||||
|
||||
if (!start.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We can capture this case in the future, but for now lets skip this.
|
||||
if (!end.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
InternalKey istart(start.value(), kMaxSequenceNumber, kValueTypeForSeek);
|
||||
InternalKey iend(end.value(), 0, kValueTypeForSeekForPrev);
|
||||
|
||||
// TODO: This needs to be optimized, right now we iterate twice, which
|
||||
// we dont need to. We can do this in N rather than 2N.
|
||||
size_t fstart = FindFile(icomparator_, *flevel_, istart.Encode());
|
||||
size_t fend = FindFile(icomparator_, *flevel_, iend.Encode());
|
||||
|
||||
// We need to check the relevant cases
|
||||
// Cases:
|
||||
// 1. [ S E ]
|
||||
// 2. [ S ] [ E ]
|
||||
// 3. [ S ] ...... [ E ]
|
||||
for (auto i = fstart; i <= fend; i++) {
|
||||
if (i < flevel_->num_files) {
|
||||
(*file_to_scan_opts_)[i].emplace_back(start.value(), end.value());
|
||||
(*file_to_scan_opts_)[i].back().property_bag = opt.property_bag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1233,6 +1273,9 @@ class LevelIterator final : public InternalIterator {
|
||||
bool to_return_sentinel_ = false;
|
||||
const std::vector<ScanOptions>* scan_opts_;
|
||||
|
||||
// Our stored scan_opts for each prefix
|
||||
std::unique_ptr<ScanOptionsMap> file_to_scan_opts_ = nullptr;
|
||||
|
||||
// Sets flags for if we should return the sentinel key next.
|
||||
// The condition for returning sentinel is reaching the end of current
|
||||
// file_iter_: !Valid() && status.().ok().
|
||||
@@ -1494,7 +1537,18 @@ bool LevelIterator::SkipEmptyFileForward() {
|
||||
// LevelIterator::Seek*, it should also call Seek* into the corresponding
|
||||
// range tombstone iterator.
|
||||
if (file_iter_.iter() != nullptr) {
|
||||
file_iter_.SeekToFirst();
|
||||
// If we are doing prepared scan opts then we should seek to the values
|
||||
// specified by the scan opts
|
||||
if (scan_opts_ && (*file_to_scan_opts_)[file_index_].size()) {
|
||||
const ScanOptions& opts = file_to_scan_opts_->at(file_index_).front();
|
||||
if (opts.range.start.has_value()) {
|
||||
InternalKey target(*opts.range.start.AsPtr(), kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
file_iter_.Seek(target.Encode());
|
||||
}
|
||||
} else {
|
||||
file_iter_.SeekToFirst();
|
||||
}
|
||||
if (range_tombstone_iter_) {
|
||||
if (*range_tombstone_iter_) {
|
||||
(*range_tombstone_iter_)->SeekToFirst();
|
||||
@@ -1542,10 +1596,15 @@ void LevelIterator::SetFileIterator(InternalIterator* iter) {
|
||||
}
|
||||
|
||||
InternalIterator* old_iter = file_iter_.Set(iter);
|
||||
// Since this is a new table iterator, no need to call Prepare() if
|
||||
// scan_opts_ is null
|
||||
if (iter && scan_opts_) {
|
||||
file_iter_.Prepare(scan_opts_);
|
||||
if (file_to_scan_opts_.get() &&
|
||||
file_to_scan_opts_->find(file_index_) != file_to_scan_opts_->end()) {
|
||||
const std::vector<ScanOptions>& opts =
|
||||
file_to_scan_opts_->at(file_index_);
|
||||
file_iter_.Prepare(&opts);
|
||||
} else {
|
||||
file_iter_.Prepare(scan_opts_);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the read pattern for PrefetchBuffer.
|
||||
@@ -1582,6 +1641,7 @@ void LevelIterator::InitFileIterator(size_t new_file_index) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
Status Version::GetTableProperties(const ReadOptions& read_options,
|
||||
@@ -2741,9 +2801,10 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
RecordTick(db_statistics_, MULTIGET_COROUTINE_COUNT,
|
||||
mget_tasks.size());
|
||||
// Collect all results so far
|
||||
std::vector<Status> statuses = folly::coro::blockingWait(
|
||||
folly::coro::collectAllRange(std::move(mget_tasks))
|
||||
.scheduleOn(&range->context()->executor()));
|
||||
std::vector<Status> statuses =
|
||||
folly::coro::blockingWait(co_withExecutor(
|
||||
&range->context()->executor(),
|
||||
folly::coro::collectAllRange(std::move(mget_tasks))));
|
||||
if (s.ok()) {
|
||||
for (Status stat : statuses) {
|
||||
if (!stat.ok()) {
|
||||
@@ -3028,9 +3089,10 @@ Status Version::MultiGetAsync(
|
||||
assert(waiting.size());
|
||||
RecordTick(db_statistics_, MULTIGET_COROUTINE_COUNT, mget_tasks.size());
|
||||
// Collect all results so far
|
||||
std::vector<Status> statuses = folly::coro::blockingWait(
|
||||
folly::coro::collectAllRange(std::move(mget_tasks))
|
||||
.scheduleOn(&range->context()->executor()));
|
||||
std::vector<Status> statuses =
|
||||
folly::coro::blockingWait(co_withExecutor(
|
||||
&range->context()->executor(),
|
||||
folly::coro::collectAllRange(std::move(mget_tasks))));
|
||||
mget_tasks.clear();
|
||||
if (s.ok()) {
|
||||
for (Status stat : statuses) {
|
||||
@@ -3507,8 +3569,13 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
}
|
||||
|
||||
if (compaction_style_ == kCompactionStyleFIFO) {
|
||||
score = static_cast<double>(total_size) /
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size;
|
||||
auto max_table_files_size =
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size;
|
||||
if (max_table_files_size == 0) {
|
||||
// avoid divide 0
|
||||
max_table_files_size = 1;
|
||||
}
|
||||
score = static_cast<double>(total_size) / max_table_files_size;
|
||||
if (score < 1 &&
|
||||
mutable_cf_options.compaction_options_fifo.allow_compaction) {
|
||||
score = std::max(
|
||||
|
||||
+2
-6
@@ -551,9 +551,6 @@ Status WriteBatchInternal::Iterate(const WriteBatch* wb,
|
||||
|
||||
if (LIKELY(!s.IsTryAgain())) {
|
||||
last_was_try_again = false;
|
||||
tag = 0;
|
||||
column_family = 0; // default
|
||||
|
||||
s = ReadRecordFromWriteBatch(&input, &tag, &column_family, &key, &value,
|
||||
&blob, &xid, &write_unix_time);
|
||||
if (!s.ok()) {
|
||||
@@ -1897,7 +1894,6 @@ Status WriteBatch::VerifyChecksum() const {
|
||||
// ReadRecordFromWriteBatch
|
||||
key.clear();
|
||||
value.clear();
|
||||
column_family = 0;
|
||||
s = ReadRecordFromWriteBatch(&input, &tag, &column_family, &key, &value,
|
||||
&blob, &xid, /*write_unix_time=*/nullptr);
|
||||
if (!s.ok()) {
|
||||
@@ -3214,11 +3210,11 @@ Status WriteBatchInternal::InsertInto(
|
||||
ColumnFamilyMemTables* memtables, FlushScheduler* flush_scheduler,
|
||||
TrimHistoryScheduler* trim_history_scheduler,
|
||||
bool ignore_missing_column_families, uint64_t recovery_log_number, DB* db,
|
||||
bool concurrent_memtable_writes, bool seq_per_batch, bool batch_per_txn) {
|
||||
bool seq_per_batch, bool batch_per_txn) {
|
||||
MemTableInserter inserter(
|
||||
sequence, memtables, flush_scheduler, trim_history_scheduler,
|
||||
ignore_missing_column_families, recovery_log_number, db,
|
||||
concurrent_memtable_writes, nullptr /* prot_info */,
|
||||
/*concurrent_memtable_writes=*/false, nullptr /* prot_info */,
|
||||
nullptr /*has_valid_writes*/, seq_per_batch, batch_per_txn);
|
||||
for (auto w : write_group) {
|
||||
if (w->CallbackFailed()) {
|
||||
|
||||
@@ -185,18 +185,19 @@ class WriteBatchInternal {
|
||||
// If flush_scheduler is non-null, it will be invoked if the memtable
|
||||
// should be flushed.
|
||||
//
|
||||
// Under concurrent use, the caller is responsible for making sure that
|
||||
// the memtables object itself is thread-local.
|
||||
// This overload is for non-concurrent insertion only.
|
||||
static Status InsertInto(
|
||||
WriteThread::WriteGroup& write_group, SequenceNumber sequence,
|
||||
ColumnFamilyMemTables* memtables, FlushScheduler* flush_scheduler,
|
||||
TrimHistoryScheduler* trim_history_scheduler,
|
||||
bool ignore_missing_column_families = false, uint64_t log_number = 0,
|
||||
DB* db = nullptr, bool concurrent_memtable_writes = false,
|
||||
bool seq_per_batch = false, bool batch_per_txn = true);
|
||||
DB* db = nullptr, bool seq_per_batch = false, bool batch_per_txn = true);
|
||||
|
||||
// Convenience form of InsertInto when you have only one batch
|
||||
// next_seq returns the seq after last sequence number used in MemTable insert
|
||||
//
|
||||
// Under concurrent use, the caller is responsible for making sure that
|
||||
// the memtables object itself is thread-local.
|
||||
static Status InsertInto(
|
||||
const WriteBatch* batch, ColumnFamilyMemTables* memtables,
|
||||
FlushScheduler* flush_scheduler,
|
||||
|
||||
Loaded 100 of 288 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user