mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 02:52:35 -05:00
Compare commits
160
Commits
9.10.fb
...
rocksdb_fork
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a00304c7b | ||
|
|
7d971cbd33 | ||
|
|
9072f5db09 | ||
|
|
49b0cb64df | ||
|
|
82794e0a4f | ||
|
|
934cf2d40d | ||
|
|
0b815cf3b3 | ||
|
|
7f3ee34cdf | ||
|
|
d0374a0a72 | ||
|
|
12829883d7 | ||
|
|
2e175124d8 | ||
|
|
0a43d8a261 | ||
|
|
cc487ba367 | ||
|
|
17ac19f2c4 | ||
|
|
24952ff088 | ||
|
|
6ac13a5f0a | ||
|
|
0cc943c067 | ||
|
|
ca7367a003 | ||
|
|
c5921df3d7 | ||
|
|
8e16f8fecf | ||
|
|
22ca6e5e68 | ||
|
|
60c266658d | ||
|
|
b9c7481fc2 | ||
|
|
5d1c0a8832 | ||
|
|
d033c6a849 | ||
|
|
68b2d941be | ||
|
|
8e6d431153 | ||
|
|
14c949df8b | ||
|
|
f6bff87b92 | ||
|
|
ec8f1452f5 | ||
|
|
15873b1fdd | ||
|
|
da8eba8b49 | ||
|
|
5f9b7ccce3 | ||
|
|
0c7e5bd2f0 | ||
|
|
7e272d2032 | ||
|
|
1d6c33d2a5 | ||
|
|
ebaeb03648 | ||
|
|
d1f383b8eb | ||
|
|
3af905aa68 | ||
|
|
3740fccc4b | ||
|
|
4c975a7c22 | ||
|
|
5139ff5c29 | ||
|
|
d7aea6955c | ||
|
|
3c2c2689b9 | ||
|
|
1e1c199316 | ||
|
|
836e88ab7a | ||
|
|
129b7791f9 | ||
|
|
36838bbf51 | ||
|
|
920d25e34e | ||
|
|
a0edca32cf | ||
|
|
9be5e15a26 | ||
|
|
4b5f0a4fcc | ||
|
|
7069691f7e | ||
|
|
6aacec07dc | ||
|
|
affcad0cc9 | ||
|
|
f6b2cdd350 | ||
|
|
8234d67e5a | ||
|
|
a30c0204cc | ||
|
|
e6972196bc | ||
|
|
3f460ad0d2 | ||
|
|
c9ce4a3d6b | ||
|
|
1f36399a77 | ||
|
|
d48af21386 | ||
|
|
dd01f73e26 | ||
|
|
a377bded9f | ||
|
|
c1fb33e1d0 | ||
|
|
302254d928 | ||
|
|
62531da510 | ||
|
|
354025fc86 | ||
|
|
4ace09f6eb | ||
|
|
833a2266a3 | ||
|
|
ed2a87db07 | ||
|
|
242e69f067 | ||
|
|
61ee80faa0 | ||
|
|
f5f4f83fcc | ||
|
|
22270dea6f | ||
|
|
864964b008 | ||
|
|
7774a4de17 | ||
|
|
7f271a3fa8 | ||
|
|
a10b4aa9a3 | ||
|
|
1341c0c670 | ||
|
|
ce6065ef70 | ||
|
|
d000134cb5 | ||
|
|
78210c82ad | ||
|
|
1f0426c44b | ||
|
|
57c177be55 | ||
|
|
d4676bfadd | ||
|
|
c3b71a6706 | ||
|
|
d79a5e5854 | ||
|
|
a6322b9ec6 | ||
|
|
2e0dc21a9f | ||
|
|
f37ce33fcc | ||
|
|
b8d915c7fa | ||
|
|
880f85a162 | ||
|
|
591f5b1266 | ||
|
|
ac6c671308 | ||
|
|
a8bd6a3ffe | ||
|
|
ac2ad2160d | ||
|
|
4ac85f0a79 | ||
|
|
35a27d859b | ||
|
|
0e469c7f99 | ||
|
|
f6c1489c7a | ||
|
|
5405835505 | ||
|
|
2257f4fae5 | ||
|
|
602e19fd8d | ||
|
|
c86d6c5317 | ||
|
|
0013acacc2 | ||
|
|
b98c21b281 | ||
|
|
97988074f0 | ||
|
|
4c617a4b13 | ||
|
|
43bcee600a | ||
|
|
780280b52e | ||
|
|
77d4663447 | ||
|
|
b3333587eb | ||
|
|
1076caf5eb | ||
|
|
f9791d44d0 | ||
|
|
5938ad270b | ||
|
|
8bccd39bfd | ||
|
|
6e97a813dc | ||
|
|
541761eaaa | ||
|
|
3040868e5f | ||
|
|
6c6defe3b8 | ||
|
|
44b741e9cc | ||
|
|
b5e4162582 | ||
|
|
d4bd67fb09 | ||
|
|
b341dc8b05 | ||
|
|
9b1d0c02e9 | ||
|
|
c2de7832bc | ||
|
|
09d7f6a5c2 | ||
|
|
631b6796b0 | ||
|
|
f7d32c18a3 | ||
|
|
3579d323c0 | ||
|
|
d2db80caa2 | ||
|
|
00a3eb6990 | ||
|
|
601a6b59e8 | ||
|
|
159fa7741d | ||
|
|
e48ccc28f4 | ||
|
|
62a7ddb39c | ||
|
|
3570e4f5ff | ||
|
|
02b4197544 | ||
|
|
e3024e7b58 | ||
|
|
30d5162298 | ||
|
|
18cecb9c46 | ||
|
|
19e4aba3db | ||
|
|
54b614de5b | ||
|
|
cf768a2f9e | ||
|
|
cc30226b1f | ||
|
|
f7b4216628 | ||
|
|
c8bc2b63fc | ||
|
|
1acd315c20 | ||
|
|
2af12ea69c | ||
|
|
c72e79a262 | ||
|
|
1d919ac414 | ||
|
|
d957e1a33a | ||
|
|
6ae3412244 | ||
|
|
09c989fbcb | ||
|
|
a45b9478ee | ||
|
|
389f75c86c | ||
|
|
2ff9f95f59 | ||
|
|
2bf11e1e96 |
No files matched your search
@@ -1,905 +0,0 @@
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
win: circleci/windows@5.0.0
|
||||
|
||||
commands:
|
||||
install-cmake-on-macos:
|
||||
steps:
|
||||
- run:
|
||||
name: Install cmake on macos
|
||||
command: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
|
||||
|
||||
install-jdk8-on-macos:
|
||||
steps:
|
||||
- run:
|
||||
name: Install JDK 8 on macos
|
||||
command: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew tap bell-sw/liberica
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install --cask liberica-jdk8
|
||||
|
||||
increase-max-open-files-on-macos:
|
||||
steps:
|
||||
- run:
|
||||
name: Increase max open files
|
||||
command: |
|
||||
sudo sysctl -w kern.maxfiles=1048576
|
||||
sudo sysctl -w kern.maxfilesperproc=1048576
|
||||
sudo launchctl limit maxfiles 1048576
|
||||
|
||||
pre-steps:
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Setup Environment Variables
|
||||
command: |
|
||||
echo "export GTEST_THROW_ON_FAILURE=0" >> $BASH_ENV
|
||||
echo "export GTEST_OUTPUT=\"xml:/tmp/test-results/\"" >> $BASH_ENV
|
||||
echo "export SKIP_FORMAT_BUCK_CHECKS=1" >> $BASH_ENV
|
||||
echo "export GTEST_COLOR=1" >> $BASH_ENV
|
||||
echo "export CTEST_OUTPUT_ON_FAILURE=1" >> $BASH_ENV
|
||||
echo "export CTEST_TEST_TIMEOUT=300" >> $BASH_ENV
|
||||
echo "export ZLIB_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zlib" >> $BASH_ENV
|
||||
echo "export BZIP2_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/bzip2" >> $BASH_ENV
|
||||
echo "export SNAPPY_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/snappy" >> $BASH_ENV
|
||||
echo "export LZ4_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/lz4" >> $BASH_ENV
|
||||
echo "export ZSTD_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zstd" >> $BASH_ENV
|
||||
|
||||
windows-build-steps:
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Install thirdparty dependencies"
|
||||
command: |
|
||||
echo "Installing CMake..."
|
||||
choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' -y
|
||||
choco install liberica8jdk -y
|
||||
mkdir $Env:THIRDPARTY_HOME
|
||||
cd $Env:THIRDPARTY_HOME
|
||||
echo "Building Snappy dependency..."
|
||||
curl https://github.com/google/snappy/archive/refs/tags/1.1.8.zip -O snappy-1.1.8.zip
|
||||
unzip -q snappy-1.1.8.zip
|
||||
cd snappy-1.1.8
|
||||
mkdir build
|
||||
cd build
|
||||
& $Env:CMAKE_BIN -G "$Env:CMAKE_GENERATOR" ..
|
||||
msbuild.exe Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
- run:
|
||||
name: "Build RocksDB"
|
||||
command: |
|
||||
$env:Path = $env:JAVA_HOME + ";" + $env:Path
|
||||
mkdir build
|
||||
cd build
|
||||
& $Env:CMAKE_BIN -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DJNI=1 ..
|
||||
cd ..
|
||||
echo "Building with VS version: $Env:CMAKE_GENERATOR"
|
||||
msbuild.exe build/rocksdb.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
- run:
|
||||
name: "Test RocksDB"
|
||||
shell: powershell.exe
|
||||
command: |
|
||||
build_tools\run_ci_db_test.ps1 -SuiteRun arena_test,db_basic_test,db_test,db_test2,db_merge_operand_test,bloom_test,c_test,coding_test,crc32c_test,dynamic_bloom_test,env_basic_test,env_test,hash_test,random_test -Concurrency 16
|
||||
- run:
|
||||
name: "Test RocksJava"
|
||||
command: |
|
||||
cd build\java
|
||||
& $Env:CTEST_BIN -C Debug -j 16
|
||||
pre-steps-macos:
|
||||
steps:
|
||||
- pre-steps
|
||||
|
||||
post-steps:
|
||||
steps:
|
||||
- store_test_results: # store test result if there's any
|
||||
path: /tmp/test-results
|
||||
- store_artifacts: # store LOG for debugging if there's any
|
||||
path: LOG
|
||||
- run: # on fail, compress Test Logs for diagnosing the issue
|
||||
name: Compress Test Logs
|
||||
command: tar -cvzf t.tar.gz t
|
||||
when: on_fail
|
||||
- store_artifacts: # on fail, store Test Logs for diagnosing the issue
|
||||
path: t.tar.gz
|
||||
destination: test_logs
|
||||
when: on_fail
|
||||
- run: # store core dumps if there's any
|
||||
command: |
|
||||
mkdir -p /tmp/core_dumps
|
||||
cp core.* /tmp/core_dumps
|
||||
when: on_fail
|
||||
- store_artifacts:
|
||||
path: /tmp/core_dumps
|
||||
when: on_fail
|
||||
|
||||
post-pmd-steps:
|
||||
steps:
|
||||
- store_artifacts:
|
||||
path: /home/circleci/project/java/target/pmd.xml
|
||||
when: on_fail
|
||||
- store_artifacts:
|
||||
path: /home/circleci/project/java/target/site
|
||||
when: on_fail
|
||||
|
||||
upgrade-cmake:
|
||||
steps:
|
||||
- run:
|
||||
name: Upgrade cmake
|
||||
command: |
|
||||
sudo apt remove --purge cmake
|
||||
sudo snap install cmake --classic
|
||||
|
||||
install-gflags:
|
||||
steps:
|
||||
- run:
|
||||
name: Install gflags
|
||||
command: |
|
||||
sudo apt-get update -y && sudo apt-get install -y libgflags-dev
|
||||
|
||||
install-gflags-on-macos:
|
||||
steps:
|
||||
- run:
|
||||
name: Install gflags on macos
|
||||
command: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install gflags
|
||||
|
||||
install-maven:
|
||||
steps:
|
||||
- run:
|
||||
name: Install Maven
|
||||
command: |
|
||||
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" >> $BASH_ENV
|
||||
echo 'export PATH=$M2_HOME/bin:$PATH' >> $BASH_ENV
|
||||
|
||||
setup-folly:
|
||||
steps:
|
||||
- run:
|
||||
name: Checkout folly sources
|
||||
command: |
|
||||
make checkout_folly
|
||||
|
||||
build-folly:
|
||||
steps:
|
||||
- run:
|
||||
name: Build folly and dependencies
|
||||
command: |
|
||||
make build_folly
|
||||
|
||||
build-for-benchmarks:
|
||||
steps:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "Linux build for benchmarks"
|
||||
command: #sized for the resource-class rocksdb-benchmark-sys1
|
||||
make V=1 J=8 -j8 release
|
||||
|
||||
perform-benchmarks:
|
||||
steps:
|
||||
- run:
|
||||
name: "Test low-variance benchmarks"
|
||||
command: ./tools/benchmark_ci.py --db_dir /tmp/rocksdb-benchmark-datadir --output_dir /tmp/benchmark-results --num_keys 20000000
|
||||
environment:
|
||||
LD_LIBRARY_PATH: /usr/local/lib
|
||||
# How long to run parts of the test(s)
|
||||
DURATION_RO: 300
|
||||
DURATION_RW: 500
|
||||
# Keep threads within physical capacity of server (much lower than default)
|
||||
NUM_THREADS: 1
|
||||
MAX_BACKGROUND_JOBS: 4
|
||||
# Don't run a couple of "optional" initial tests
|
||||
CI_TESTS_ONLY: "true"
|
||||
# Reduce configured size of levels to ensure more levels in the leveled compaction LSM tree
|
||||
WRITE_BUFFER_SIZE_MB: 16
|
||||
TARGET_FILE_SIZE_BASE_MB: 16
|
||||
MAX_BYTES_FOR_LEVEL_BASE_MB: 64
|
||||
# The benchmark host has 32GB memory
|
||||
# The following values are tailored to work with that
|
||||
# Note, tests may not exercise the targeted issues if the memory is increased on new test hosts.
|
||||
COMPRESSION_TYPE: "none"
|
||||
CACHE_INDEX_AND_FILTER_BLOCKS: 1
|
||||
MIN_LEVEL_TO_COMPRESS: 3
|
||||
CACHE_SIZE_MB: 10240
|
||||
MB_WRITE_PER_SEC: 2
|
||||
|
||||
post-benchmarks:
|
||||
steps:
|
||||
- store_artifacts: # store the benchmark output
|
||||
path: /tmp/benchmark-results
|
||||
destination: test_logs
|
||||
- run:
|
||||
name: Send benchmark report to visualisation
|
||||
command: |
|
||||
set +e
|
||||
set +o pipefail
|
||||
./build_tools/benchmark_log_tool.py --tsvfile /tmp/benchmark-results/report.tsv --esdocument https://search-rocksdb-bench-k2izhptfeap2hjfxteolsgsynm.us-west-2.es.amazonaws.com/bench_test3_rix/_doc
|
||||
true
|
||||
|
||||
executors:
|
||||
linux-docker:
|
||||
docker:
|
||||
# The image configuration is build_tools/ubuntu20_image/Dockerfile
|
||||
# To update and build the image:
|
||||
# $ cd build_tools/ubuntu20_image
|
||||
# $ docker build -t zjay437/rocksdb:0.5 .
|
||||
# $ docker push zjay437/rocksdb:0.5
|
||||
# `zjay437` is the account name for zjay@meta.com which readwrite token is shared internally. To login:
|
||||
# $ docker login --username zjay437
|
||||
# Or please feel free to change it to your docker hub account for hosting the image, meta employee should already have the account and able to login with SSO.
|
||||
# To avoid impacting the existing CI runs, please bump the version every time creating a new image
|
||||
# to run the CI image environment locally:
|
||||
# $ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -it zjay437/rocksdb:0.5 bash
|
||||
# option `--cap-add=SYS_PTRACE --security-opt seccomp=unconfined` is used to enable gdb to attach an existing process
|
||||
- image: zjay437/rocksdb:0.6
|
||||
linux-java-docker:
|
||||
docker:
|
||||
# This is the Docker Image used for building RocksJava releases, see: https://github.com/evolvedbinary/docker-rocksjava
|
||||
- image: evolvedbinary/rocksjava:centos6_x64-be
|
||||
|
||||
jobs:
|
||||
build-macos:
|
||||
macos:
|
||||
xcode: 14.3.1
|
||||
resource_class: macos.m1.medium.gen1
|
||||
environment:
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1 # jemalloc cause env_test hang, disable it for now
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-gflags-on-macos
|
||||
- pre-steps-macos
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 J=16 -j16 all
|
||||
- post-steps
|
||||
|
||||
build-macos-cmake:
|
||||
macos:
|
||||
xcode: 14.3.1
|
||||
resource_class: macos.m1.medium.gen1
|
||||
parameters:
|
||||
run_even_tests:
|
||||
description: run even or odd tests, used to split tests to 2 groups
|
||||
type: boolean
|
||||
default: true
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-cmake-on-macos
|
||||
- install-gflags-on-macos
|
||||
- pre-steps-macos
|
||||
- run:
|
||||
name: "cmake generate project file"
|
||||
command: ulimit -S -n `ulimit -H -n` && mkdir build && cd build && cmake -DWITH_GFLAGS=1 ..
|
||||
- run:
|
||||
name: "Build tests"
|
||||
command: cd build && make V=1 -j16
|
||||
- when:
|
||||
condition: << parameters.run_even_tests >>
|
||||
steps:
|
||||
- run:
|
||||
name: "Run even tests"
|
||||
command: ulimit -S -n `ulimit -H -n` && cd build && ctest -j16 -I 0,,2
|
||||
- when:
|
||||
condition:
|
||||
not: << parameters.run_even_tests >>
|
||||
steps:
|
||||
- run:
|
||||
name: "Run odd tests"
|
||||
command: ulimit -S -n `ulimit -H -n` && cd build && ctest -j16 -I 1,,2
|
||||
- post-steps
|
||||
|
||||
build-linux:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: make V=1 J=32 -j32 check
|
||||
- post-steps
|
||||
|
||||
build-linux-encrypted_env-no_compression:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- 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
|
||||
- post-steps
|
||||
|
||||
build-linux-static_lib-alt_namespace-status_checked:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: ASSERT_STATUS_CHECKED=1 TEST_UINT128_COMPAT=1 ROCKSDB_MODIFY_NPHASH=1 LIB_MODE=static OPT="-DROCKSDB_NAMESPACE=alternative_rocksdb_ns" make V=1 -j24 check
|
||||
- post-steps
|
||||
|
||||
build-linux-release:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: make V=1 -j32 LIB_MODE=shared release
|
||||
- run: ls librocksdb.so # ensure shared lib built
|
||||
- run: ./db_stress --version # ensure with gflags
|
||||
- run: make clean
|
||||
- run: make V=1 -j32 release
|
||||
- run: ls librocksdb.a # ensure static lib built
|
||||
- run: ./db_stress --version # ensure with gflags
|
||||
- run: make clean
|
||||
- run: apt-get remove -y libgflags-dev
|
||||
- run: make V=1 -j32 LIB_MODE=shared release
|
||||
- run: ls librocksdb.so # ensure shared lib built
|
||||
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
|
||||
- run: make clean
|
||||
- run: make V=1 -j32 release
|
||||
- run: ls librocksdb.a # ensure static lib built
|
||||
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
|
||||
- post-steps
|
||||
|
||||
build-linux-release-rtti:
|
||||
executor: linux-docker
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j16 static_lib tools db_bench
|
||||
- run: ./db_stress --version # ensure with gflags
|
||||
- run: make clean
|
||||
- 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 # ensure without gflags
|
||||
|
||||
build-linux-clang-no_test_run:
|
||||
executor: linux-docker
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: CC=clang CXX=clang++ USE_CLANG=1 PORTABLE=1 make V=1 -j16 all
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-asan:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check # aligned new doesn't work for reason we haven't figured out
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-mini-tsan:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge+
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: COMPILE_WITH_TSAN=1 CC=clang-13 CXX=clang++-13 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-ubsan:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: COMPILE_WITH_UBSAN=1 OPT="-fsanitize-blacklist=.circleci/ubsan_suppression_list.txt" CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 ubsan_check # aligned new doesn't work for reason we haven't figured out
|
||||
- post-steps
|
||||
|
||||
build-linux-valgrind:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: PORTABLE=1 make V=1 -j32 valgrind_test
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-clang-analyze:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 CLANG_ANALYZER="/usr/bin/clang++-10" CLANG_SCAN_BUILD=scan-build-10 USE_CLANG=1 make V=1 -j32 analyze # aligned new doesn't work for reason we haven't figured out. For unknown, reason passing "clang++-10" as CLANG_ANALYZER doesn't work, and we need a full path.
|
||||
- post-steps
|
||||
- run:
|
||||
name: "compress test report"
|
||||
command: tar -cvzf scan_build_report.tar.gz scan_build_report
|
||||
when: on_fail
|
||||
- store_artifacts:
|
||||
path: scan_build_report.tar.gz
|
||||
destination: scan_build_report
|
||||
when: on_fail
|
||||
|
||||
build-linux-runner:
|
||||
machine: true
|
||||
resource_class: facebook/rocksdb-benchmark-sys1
|
||||
steps:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "Checked Linux build (Runner)"
|
||||
command: make V=1 J=8 -j8 check
|
||||
environment:
|
||||
LD_LIBRARY_PATH: /usr/local/lib
|
||||
- post-steps
|
||||
|
||||
build-linux-cmake-with-folly:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- setup-folly
|
||||
- build-folly
|
||||
- run: (mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make V=1 -j20 && ctest -j20)
|
||||
- post-steps
|
||||
|
||||
build-linux-cmake-with-folly-lite-no-test:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- setup-folly
|
||||
- run: (mkdir build && cd build && cmake -DUSE_FOLLY_LITE=1 -DWITH_GFLAGS=1 .. && make V=1 -j20)
|
||||
- post-steps
|
||||
|
||||
build-linux-cmake-with-benchmark:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: mkdir build && cd build && cmake -DWITH_GFLAGS=1 -DWITH_BENCHMARK=1 .. && make V=1 -j20 && ctest -j20
|
||||
- post-steps
|
||||
|
||||
build-linux-unity-and-headers:
|
||||
docker: # executor type
|
||||
- image: gcc:latest
|
||||
environment:
|
||||
EXTRA_CXXFLAGS: -mno-avx512f # Warnings-as-error in avx512fintrin.h, would be used on newer hardware
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: apt-get update -y && apt-get install -y libgflags-dev
|
||||
- run:
|
||||
name: "Unity build"
|
||||
command: make V=1 -j8 unity_test
|
||||
no_output_timeout: 20m
|
||||
- run: make V=1 -j8 -k check-headers # could be moved to a different build
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-7-with-folly:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- setup-folly
|
||||
- build-folly
|
||||
- run: USE_FOLLY=1 LIB_MODE=static CC=gcc-7 CXX=g++-7 V=1 make -j32 check # TODO: LIB_MODE only to work around unresolved linker failures
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-7-with-folly-lite-no-test:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- setup-folly
|
||||
- run: USE_FOLLY_LITE=1 CC=gcc-7 CXX=g++-7 V=1 make -j32 all
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-8-no_test_run:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: CC=gcc-8 CXX=g++-8 V=1 make -j32 all
|
||||
- post-steps
|
||||
|
||||
build-linux-cmake-with-folly-coroutines:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
environment:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- pre-steps
|
||||
- setup-folly
|
||||
- build-folly
|
||||
- run: (mkdir build && cd build && cmake -DUSE_COROUTINES=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make V=1 -j20 && ctest -j20)
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-10-cxx20-no_test_run:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: CC=gcc-10 CXX=g++-10 V=1 ROCKSDB_CXX_STANDARD=c++20 make -j32 all
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-11-no_test_run:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: LIB_MODE=static CC=gcc-11 CXX=g++-11 V=1 make -j32 all microbench # TODO: LIB_MODE only to work around unresolved linker failures
|
||||
- post-steps
|
||||
|
||||
build-linux-clang-13-no_test_run:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j32 all microbench
|
||||
- post-steps
|
||||
|
||||
# Ensure ASAN+UBSAN with folly, and full testsuite with clang 13
|
||||
build-linux-clang-13-asan-ubsan-with-folly:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- setup-folly
|
||||
- build-folly
|
||||
- run: CC=clang-13 CXX=clang++-13 LIB_MODE=static USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check # TODO: LIB_MODE only to work around unresolved linker failures
|
||||
- post-steps
|
||||
|
||||
# This job is only to make sure the microbench tests are able to run, the benchmark result is not meaningful as the CI host is changing.
|
||||
build-linux-run-microbench:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: DEBUG_LEVEL=0 make -j32 run_microbench
|
||||
- post-steps
|
||||
|
||||
build-linux-mini-crashtest:
|
||||
executor: linux-docker
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000' blackbox_crash_test_with_atomic_flush
|
||||
- post-steps
|
||||
|
||||
build-linux-crashtest-tiered-storage-bb:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "run crashtest"
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800' blackbox_crash_test_with_tiered_storage
|
||||
no_output_timeout: 100m
|
||||
- post-steps
|
||||
|
||||
build-linux-crashtest-tiered-storage-wb:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "run crashtest"
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800' whitebox_crash_test_with_tiered_storage
|
||||
no_output_timeout: 100m
|
||||
- post-steps
|
||||
|
||||
build-windows-vs2022-avx2:
|
||||
executor:
|
||||
name: win/server-2022
|
||||
size: 2xlarge
|
||||
environment:
|
||||
THIRDPARTY_HOME: C:/Users/circleci/thirdparty
|
||||
CMAKE_HOME: C:/Program Files/CMake
|
||||
CMAKE_BIN: C:/Program Files/CMake/bin/cmake.exe
|
||||
CTEST_BIN: C:/Program Files/CMake/bin/ctest.exe
|
||||
JAVA_HOME: C:/Program Files/BellSoft/LibericaJDK-8
|
||||
SNAPPY_HOME: C:/Users/circleci/thirdparty/snappy-1.1.8
|
||||
SNAPPY_INCLUDE: C:/Users/circleci/thirdparty/snappy-1.1.8;C:/Users/circleci/thirdparty/snappy-1.1.8/build
|
||||
SNAPPY_LIB_DEBUG: C:/Users/circleci/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
|
||||
CMAKE_GENERATOR: Visual Studio 17 2022
|
||||
CMAKE_PORTABLE: AVX2
|
||||
steps:
|
||||
- windows-build-steps
|
||||
|
||||
build-windows-vs2022:
|
||||
executor:
|
||||
name: win/server-2022
|
||||
size: 2xlarge
|
||||
environment:
|
||||
THIRDPARTY_HOME: C:/Users/circleci/thirdparty
|
||||
CMAKE_HOME: C:/Program Files/CMake
|
||||
CMAKE_BIN: C:/Program Files/CMake/bin/cmake.exe
|
||||
CTEST_BIN: C:/Program Files/CMake/bin/ctest.exe
|
||||
JAVA_HOME: C:/Program Files/BellSoft/LibericaJDK-8
|
||||
SNAPPY_HOME: C:/Users/circleci/thirdparty/snappy-1.1.8
|
||||
SNAPPY_INCLUDE: C:/Users/circleci/thirdparty/snappy-1.1.8;C:/Users/circleci/thirdparty/snappy-1.1.8/build
|
||||
SNAPPY_LIB_DEBUG: C:/Users/circleci/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
|
||||
CMAKE_GENERATOR: Visual Studio 17 2022
|
||||
CMAKE_PORTABLE: 1
|
||||
steps:
|
||||
- windows-build-steps
|
||||
|
||||
build-windows-vs2019:
|
||||
executor:
|
||||
name: win/server-2019
|
||||
size: 2xlarge
|
||||
environment:
|
||||
THIRDPARTY_HOME: C:/Users/circleci/thirdparty
|
||||
CMAKE_HOME: C:/Program Files/CMake
|
||||
CMAKE_BIN: C:/Program Files/CMake/bin/cmake.exe
|
||||
CTEST_BIN: C:/Program Files/CMake/bin/ctest.exe
|
||||
JAVA_HOME: C:/Program Files/BellSoft/LibericaJDK-8
|
||||
SNAPPY_HOME: C:/Users/circleci/thirdparty/snappy-1.1.8
|
||||
SNAPPY_INCLUDE: C:/Users/circleci/thirdparty/snappy-1.1.8;C:/Users/circleci/thirdparty/snappy-1.1.8/build
|
||||
SNAPPY_LIB_DEBUG: C:/Users/circleci/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
|
||||
CMAKE_GENERATOR: Visual Studio 16 2019
|
||||
CMAKE_PORTABLE: 1
|
||||
steps:
|
||||
- windows-build-steps
|
||||
|
||||
build-linux-java:
|
||||
executor: linux-java-docker
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
command: |
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Test RocksDBJava"
|
||||
command: scl enable devtoolset-7 'make V=1 J=8 -j8 jtest'
|
||||
- post-steps
|
||||
|
||||
build-linux-java-pmd:
|
||||
executor: linux-java-docker
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
command: |
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- install-maven
|
||||
- run:
|
||||
name: "PMD RocksDBJava"
|
||||
command: scl enable devtoolset-7 'make V=1 J=8 -j8 jpmd'
|
||||
- post-pmd-steps
|
||||
|
||||
build-linux-java-static:
|
||||
executor: linux-java-docker
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
command: |
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Static Library"
|
||||
command: scl enable devtoolset-7 'make V=1 J=8 -j8 rocksdbjavastatic'
|
||||
- post-steps
|
||||
|
||||
build-macos-java:
|
||||
macos:
|
||||
xcode: 14.3.1
|
||||
resource_class: macos.m1.medium.gen1
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1 # jemalloc causes java 8 crash
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-gflags-on-macos
|
||||
- install-jdk8-on-macos
|
||||
- pre-steps-macos
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
command: |
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Test RocksDBJava"
|
||||
command: make V=1 J=16 -j16 jtest
|
||||
no_output_timeout: 20m
|
||||
- post-steps
|
||||
|
||||
build-macos-java-static:
|
||||
macos:
|
||||
xcode: 14.3.1
|
||||
resource_class: macos.m1.medium.gen1
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-gflags-on-macos
|
||||
- install-cmake-on-macos
|
||||
- install-jdk8-on-macos
|
||||
- pre-steps-macos
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
command: |
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava x86 and ARM Static Libraries"
|
||||
command: make V=1 J=16 -j16 rocksdbjavastaticosx
|
||||
no_output_timeout: 20m
|
||||
- post-steps
|
||||
|
||||
build-macos-java-static-universal:
|
||||
macos:
|
||||
xcode: 14.3.1
|
||||
resource_class: macos.m1.medium.gen1
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-gflags-on-macos
|
||||
- install-cmake-on-macos
|
||||
- install-jdk8-on-macos
|
||||
- pre-steps-macos
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
command: |
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Universal Binary Static Library"
|
||||
command: make V=1 J=16 -j16 rocksdbjavastaticosx_ub
|
||||
no_output_timeout: 20m
|
||||
- post-steps
|
||||
|
||||
build-examples:
|
||||
executor: linux-docker
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "Build examples"
|
||||
command: |
|
||||
make V=1 -j4 static_lib && cd examples && make V=1 -j4
|
||||
- post-steps
|
||||
|
||||
build-cmake-mingw:
|
||||
executor: linux-docker
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
|
||||
- run:
|
||||
name: "Build cmake-mingw"
|
||||
command: |
|
||||
export PATH=$JAVA_HOME/bin:$PATH
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
mkdir build && cd build && cmake -DJNI=1 -DWITH_GFLAGS=OFF .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb rocksdbjni
|
||||
- post-steps
|
||||
|
||||
build-linux-non-shm:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
environment:
|
||||
TEST_TMPDIR: /tmp/rocksdb_test_tmp
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: make V=1 -j32 check
|
||||
- post-steps
|
||||
|
||||
build-linux-arm-test-full:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: arm.large
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: make V=1 J=4 -j4 check
|
||||
- post-steps
|
||||
|
||||
build-linux-arm:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: arm.large
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: ROCKSDBTESTS_PLATFORM_DEPENDENT=only make V=1 J=4 -j4 all_but_some_tests check_some
|
||||
- post-steps
|
||||
|
||||
build-linux-arm-cmake-no_test_run:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: arm.large
|
||||
environment:
|
||||
JAVA_HOME: /usr/lib/jvm/java-8-openjdk-arm64
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
command: |
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build with cmake"
|
||||
command: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=0 -DWITH_GFLAGS=1 -DWITH_BENCHMARK_TOOLS=0 -DWITH_TOOLS=0 -DWITH_CORE_TOOLS=1 ..
|
||||
make -j4
|
||||
- run:
|
||||
name: "Build Java with cmake"
|
||||
command: |
|
||||
rm -rf build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DJNI=1 -DCMAKE_BUILD_TYPE=Release -DWITH_GFLAGS=1 ..
|
||||
make -j4 rocksdb rocksdbjni
|
||||
- post-steps
|
||||
|
||||
build-format-compatible:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "test"
|
||||
command: |
|
||||
export TEST_TMPDIR=/dev/shm/rocksdb
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
tools/check_format_compatible.sh
|
||||
- post-steps
|
||||
|
||||
build-fuzzers:
|
||||
executor: linux-docker
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "Build rocksdb lib"
|
||||
command: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j4 static_lib
|
||||
- run:
|
||||
name: "Build fuzzers"
|
||||
command: cd fuzz && make sst_file_writer_fuzzer db_fuzzer db_map_fuzzer
|
||||
- post-steps
|
||||
|
||||
benchmark-linux: #use a private Circle CI runner (resource_class) to run the job
|
||||
machine: true
|
||||
resource_class: facebook/rocksdb-benchmark-sys1
|
||||
steps:
|
||||
- build-for-benchmarks
|
||||
- perform-benchmarks
|
||||
- post-benchmarks
|
||||
|
||||
workflows: # Only jobs that haven't been successfully migrated to GitHub Actions
|
||||
version: 2
|
||||
jobs-linux-arm:
|
||||
jobs:
|
||||
- build-linux-arm
|
||||
benchmark-linux:
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: "0 * * * *"
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
jobs:
|
||||
- benchmark-linux
|
||||
nightly:
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: "0 9 * * *"
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
jobs:
|
||||
- build-linux-arm-test-full
|
||||
@@ -1,6 +0,0 @@
|
||||
# Supress UBSAN warnings related to stl_tree.h, e.g.
|
||||
# UndefinedBehaviorSanitizer: undefined-behavior /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_tree.h:1505:43 in
|
||||
# /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_tree.h:1505:43:
|
||||
# runtime error: upcast of address 0x000001fa8820 with insufficient space for an object of type
|
||||
# 'std::_Rb_tree_node<std::pair<const std::__cxx11::basic_string<char>, rocksdb::(anonymous namespace)::LockHoldingInfo> >'
|
||||
src:*bits/stl_tree.h
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
# ======================== Fast Initial Checks ====================== #
|
||||
check-format-and-targets:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
with:
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
- name: Install argparse
|
||||
run: pip install argparse
|
||||
- name: Download clang-format-diff.py
|
||||
run: wget https://raw.githubusercontent.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
|
||||
run: wget https://rocksdb-deps.s3.us-west-2.amazonaws.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
|
||||
- name: Check format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
- name: Compare buckify output
|
||||
@@ -390,9 +390,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: COMPILE_WITH_UBSAN=1 OPT="-fsanitize-blacklist=.circleci/ubsan_suppression_list.txt" CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 ubsan_check
|
||||
- run: COMPILE_WITH_UBSAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 ubsan_check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang10-mini-tsan:
|
||||
build-linux-clang13-mini-tsan:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 32-core-ubuntu
|
||||
|
||||
@@ -91,6 +91,7 @@ buckifier/__pycache__
|
||||
.arcconfig
|
||||
|
||||
compile_commands.json
|
||||
meta_gen_cpp_compile_commands.json
|
||||
clang-format-diff.py
|
||||
.py3/
|
||||
|
||||
|
||||
@@ -214,6 +214,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"table/cuckoo/cuckoo_table_builder.cc",
|
||||
"table/cuckoo/cuckoo_table_factory.cc",
|
||||
"table/cuckoo/cuckoo_table_reader.cc",
|
||||
"table/external_table.cc",
|
||||
"table/format.cc",
|
||||
"table/get_context.cc",
|
||||
"table/iterator.cc",
|
||||
@@ -317,6 +318,8 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"utilities/persistent_cache/block_cache_tier_metadata.cc",
|
||||
"utilities/persistent_cache/persistent_cache_tier.cc",
|
||||
"utilities/persistent_cache/volatile_tier_impl.cc",
|
||||
"utilities/secondary_index/secondary_index_iterator.cc",
|
||||
"utilities/secondary_index/simple_secondary_index.cc",
|
||||
"utilities/simulator_cache/cache_simulator.cc",
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/table_properties_collectors/compact_for_tiering_collector.cc",
|
||||
@@ -406,6 +409,7 @@ cpp_library_wrapper(name="rocksdb_tools_lib", srcs=[
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
|
||||
"tools/db_bench_tool.cc",
|
||||
"tools/simulated_hybrid_file_system.cc",
|
||||
"tools/tool_hooks.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=False)
|
||||
|
||||
|
||||
@@ -835,6 +835,7 @@ set(SOURCES
|
||||
table/cuckoo/cuckoo_table_builder.cc
|
||||
table/cuckoo/cuckoo_table_factory.cc
|
||||
table/cuckoo/cuckoo_table_reader.cc
|
||||
table/external_table.cc
|
||||
table/format.cc
|
||||
table/get_context.cc
|
||||
table/iterator.cc
|
||||
@@ -939,6 +940,8 @@ set(SOURCES
|
||||
utilities/persistent_cache/block_cache_tier_metadata.cc
|
||||
utilities/persistent_cache/persistent_cache_tier.cc
|
||||
utilities/persistent_cache/volatile_tier_impl.cc
|
||||
utilities/secondary_index/secondary_index_iterator.cc
|
||||
utilities/secondary_index/simple_secondary_index.cc
|
||||
utilities/simulator_cache/cache_simulator.cc
|
||||
utilities/simulator_cache/sim_cache.cc
|
||||
utilities/table_properties_collectors/compact_for_tiering_collector.cc
|
||||
@@ -1571,6 +1574,7 @@ if(WITH_BENCHMARK_TOOLS)
|
||||
add_executable(db_bench${ARTIFACT_SUFFIX}
|
||||
tools/simulated_hybrid_file_system.cc
|
||||
tools/db_bench.cc
|
||||
tools/tool_hooks.cc
|
||||
tools/db_bench_tool.cc)
|
||||
target_link_libraries(db_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${THIRDPARTY_LIBS})
|
||||
|
||||
+83
@@ -1,6 +1,89 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 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`.
|
||||
* Add a new field `num_l0_files` in `CompactionJobInfo` about the number of L0 files in the CF right before and after the compaction
|
||||
* Added per-key-placement feature in Remote Compaction
|
||||
* Implemented API DB::GetPropertiesOfTablesByLevel that retrieves table properties for files in each LSM tree level
|
||||
|
||||
### Public API Changes
|
||||
* `GetAllKeyVersions()` now interprets empty slices literally, as valid keys, and uses new `OptSlice` type default value for extreme upper and lower range limits.
|
||||
* `DeleteFilesInRanges()` now takes `RangeOpt` which is based on `OptSlice`. The overload taking `RangePtr` is deprecated.
|
||||
* Add an unordered map of name/value pairs, ReadOptions::property_bag, to pass opaque options through to an external table when creating an Iterator.
|
||||
* Introduced CompactionServiceJobStatus::kAborted to allow handling aborted scenario in Schedule(), Wait() or OnInstallation() APIs in Remote Compactions.
|
||||
* format\_version < 2 in BlockBasedTableOptions is no longer supported for writing new files. Support for reading such files is deprecated and might be removed in the future. `CompressedSecondaryCacheOptions::compress_format_version == 1` is also deprecated.
|
||||
|
||||
### Behavior Changes
|
||||
* `ldb` now returns an error if the specified `--compression_type` is not supported in the build.
|
||||
* MultiGet with snapshot and ReadOptions::read_tier = kPersistedTier will now read a consistent view across CFs (instead of potentially reading some CF before and some CF after a flush).
|
||||
* CreateColumnFamily() is no longer allowed on a read-only DB (OpenForReadOnly())
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed stats for Tiered Storage with preclude_last_level feature
|
||||
|
||||
## 10.0.0 (02/21/2025)
|
||||
### New Features
|
||||
* Introduced new `auto_refresh_iterator_with_snapshot` opt-in knob that (when enabled) will periodically release obsolete memory and storage resources for as long as the iterator is making progress and its supplied `read_options.snapshot` was initialized with non-nullptr value.
|
||||
* Added the ability to plug-in a custom table reader implementation. See include/rocksdb/external_table_reader.h for more details.
|
||||
* Experimental feature: RocksDB now supports FAISS inverted file based indices via the secondary indexing framework. Applications can use FAISS secondary indices to automatically quantize embeddings and perform K-nearest-neighbors similarity searches. See `FaissIVFIndex` and `SecondaryIndex` for more details. Note: the FAISS integration currently requires using the BUCK build.
|
||||
* Add new DB property `num_running_compaction_sorted_runs` that tracks the number of sorted runs being processed by currently running compactions
|
||||
* Experimental feature: added support for simple secondary indices that index the specified column as-is. See `SimpleSecondaryIndex` and `SecondaryIndex` for more details.
|
||||
* Added new `TransactionDBOptions::txn_commit_bypass_memtable_threshold`, which enables optimized transaction commit (see `TransactionOptions::commit_bypass_memtable`) when the transaction size exceeds a configured threshold.
|
||||
|
||||
### Public API Changes
|
||||
* Updated the query API of the experimental secondary indexing feature by removing the earlier `SecondaryIndex::NewIterator` virtual and adding a `SecondaryIndexIterator` class that can be utilized by applications to find the primary keys for a given search target.
|
||||
* Added back the ability to leverage the primary key when building secondary index entries. This involved changes to the signatures of `SecondaryIndex::GetSecondary{KeyPrefix,Value}` as well as the addition of a new method `SecondaryIndex::FinalizeSecondaryKeyPrefix`. See the API comments for more details.
|
||||
* Minimum supported version of ZSTD is now 1.4.0, for code simplification. Obsolete `CompressionType` `kZSTDNotFinalCompression` is also removed.
|
||||
|
||||
### Behavior Changes
|
||||
* `VerifyBackup` in `verify_with_checksum`=`true` mode will now evaluate checksums in parallel. As a result, unlike in case of original implementation, the API won't bail out on a very first corruption / mismatch and instead will iterate over all the backup files logging success / _degree_of_failure_ for each.
|
||||
* Reversed the order of updates to the same key in WriteBatchWithIndex. This means if there are multiple updates to the same key, the most recent update is ordered first. This affects the output of WBWIIterator. When WriteBatchWithIndex is created with `overwrite_key=true`, this affects the output only if Merge is used (#13387).
|
||||
* Added support for Merge operations in transactions using option `TransactionOptions::commit_bypass_memtable`.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed GetMergeOperands() API in ReadOnlyDB and SecondaryDB
|
||||
* Fix a bug in `GetMergeOperands()` that can return incorrect status (MergeInProgress) and incorrect number of merge operands. This can happen when `GetMergeOperandsOptions::continue_cb` is set, both active and immutable memtables have merge operands and the callback stops the look up at the immutable memtable.
|
||||
|
||||
## 9.11.0 (01/17/2025)
|
||||
### New Features
|
||||
* Introduce CancelAwaitingJobs() in CompactionService interface which will allow users to implement cancellation of running remote compactions from the primary instance
|
||||
* Experimental feature: RocksDB now supports defining secondary indices, which are automatically maintained by the storage engine. Secondary indices provide a new customization point: applications can provide their own by implementing the new `SecondaryIndex` interface. See the `SecondaryIndex` API comments for more details. Note: this feature is currently only available in conjunction with write-committed pessimistic transactions, and `Merge` is not yet supported.
|
||||
* Provide a new option `track_and_verify_wals` to track and verify various information about WAL during WAL recovery. This is intended to be a better replacement to `track_and_verify_wals_in_manifest`.
|
||||
|
||||
### Public API Changes
|
||||
* Add `io_buffer_size` to BackupEngineOptions to enable optimal configuration of IO size
|
||||
* Clean up all the references to `random_access_max_buffer_size`, related rules and all the clients wrappers. This option has been officially deprecated in 5.4.0.
|
||||
* Add `file_ingestion_nanos` and `file_ingestion_blocking_live_writes_nanos` in PerfContext to observe file ingestions
|
||||
* Offer new DB::Open and variants that use `std::unique_ptr<DB>*` output parameters and deprecate the old versions that use `DB**` output parameters.
|
||||
* The DB::DeleteFile API is officially deprecated.
|
||||
|
||||
### Behavior Changes
|
||||
* For leveled compaction, manual compaction (CompactRange()) will be more strict about keeping compaction size under `max_compaction_bytes`. This prevents overly large compactions in some cases (#13306).
|
||||
* Experimental tiering options `preclude_last_level_data_seconds` and `preserve_internal_time_seconds` are now mutable with `SetOptions()`. Some changes to handling of these features along with long-lived snapshots and range deletes made this possible.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a longstanding major bug with SetOptions() in which setting changes can be quietly reverted.
|
||||
|
||||
## 9.10.0 (12/12/2024)
|
||||
### New Features
|
||||
* Introduce `TransactionOptions::commit_bypass_memtable` to enable transaction commit to bypass memtable insertions. This can be beneficial for transactions with many operations, as it reduces commit time that is mostly spent on memtable insertion.
|
||||
|
||||
### Public API Changes
|
||||
* Deprecated Remote Compaction APIs (StartV2, WaitForCompleteV2) are completely removed from the codebase
|
||||
|
||||
### Behavior Changes
|
||||
* DB::KeyMayExist() now follows its function comment, which means `value` parameter can be null, and it will be set only if `value_found` is passed in.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix the issue where compaction incorrectly drops a key when there is a snapshot with a sequence number of zero.
|
||||
* Honor ConfigOptions.ignore_unknown_options in ParseStruct()
|
||||
|
||||
### Performance Improvements
|
||||
* Enable reuse of file system allocated buffer for synchronous prefetching.
|
||||
* In buffered IO mode, try to align writes on power of 2 if checksum handoff is not enabled for the file type being written.
|
||||
|
||||
## 9.9.0 (11/18/2024)
|
||||
### New Features
|
||||
* Multi-Column-Family-Iterator (CoalescingIterator/AttributeGroupIterator) is no longer marked as experimental
|
||||
|
||||
@@ -2,7 +2,8 @@ This is the list of all known third-party language bindings for RocksDB. If some
|
||||
|
||||
* Java - https://github.com/facebook/rocksdb/tree/main/java
|
||||
* Python
|
||||
* http://python-rocksdb.readthedocs.io/en/latest/
|
||||
* https://github.com/rocksdict/RocksDict
|
||||
* http://python-rocksdb.readthedocs.io/en/latest/ (unmaintained)
|
||||
* http://pyrocksdb.readthedocs.org/en/latest/ (unmaintained)
|
||||
* Perl - https://metacpan.org/pod/RocksDB
|
||||
* Node.js - https://npmjs.org/package/rocksdb
|
||||
|
||||
@@ -2489,7 +2489,7 @@ 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 33f5b67fcaeb8705b04fd1b850873a180dc89aaa
|
||||
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
|
||||
|
||||
@@ -360,9 +360,13 @@ EOF
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZSTD; then
|
||||
# Test whether zstd library is installed
|
||||
# Test whether zstd library is installed with minimum version
|
||||
# (Keep in sync with compression.h)
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <zstd.h>
|
||||
#if ZSTD_VERSION_NUMBER < 10400
|
||||
#error "ZSTD support requires version >= 1.4.0 (libzstd-devel)"
|
||||
#endif // ZSTD_VERSION_NUMBER
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
|
||||
@@ -59,7 +59,7 @@ fi
|
||||
if ! test $ROCKSDB_DISABLE_ZSTD; then
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
CFLAGS+=" -DZSTD -DZSTD_STATIC_LINKING_ONLY"
|
||||
fi
|
||||
|
||||
# location of gflags headers and libraries
|
||||
|
||||
Vendored
-1
@@ -118,7 +118,6 @@ Status SecondaryCache::CreateFromString(
|
||||
sec_cache = NewCompressedSecondaryCache(sec_cache_opts);
|
||||
}
|
||||
|
||||
|
||||
if (status.ok()) {
|
||||
result->swap(sec_cache);
|
||||
}
|
||||
|
||||
Vendored
+1
-2
@@ -191,8 +191,7 @@ namespace {
|
||||
class SharedState {
|
||||
public:
|
||||
explicit SharedState(CacheBench* cache_bench)
|
||||
: cv_(&mu_),
|
||||
cache_bench_(cache_bench) {}
|
||||
: cv_(&mu_), cache_bench_(cache_bench) {}
|
||||
|
||||
~SharedState() = default;
|
||||
|
||||
|
||||
-1
@@ -845,7 +845,6 @@ TEST_P(CompressedSecondaryCacheTestWithCompressionParam,
|
||||
BasicTestHelper(sec_cache, sec_cache_is_compressed_);
|
||||
}
|
||||
|
||||
|
||||
TEST_P(CompressedSecondaryCacheTestWithCompressionParam, FailsTest) {
|
||||
FailsTest(sec_cache_is_compressed_);
|
||||
}
|
||||
|
||||
Vendored
+2
-3
@@ -93,9 +93,8 @@ void LRUHandleTable::Resize() {
|
||||
|
||||
uint32_t old_length = uint32_t{1} << length_bits_;
|
||||
int new_length_bits = length_bits_ + 1;
|
||||
std::unique_ptr<LRUHandle* []> new_list {
|
||||
new LRUHandle* [size_t{1} << new_length_bits] {}
|
||||
};
|
||||
std::unique_ptr<LRUHandle*[]> new_list{
|
||||
new LRUHandle* [size_t{1} << new_length_bits] {}};
|
||||
[[maybe_unused]] uint32_t count = 0;
|
||||
for (uint32_t i = 0; i < old_length; i++) {
|
||||
LRUHandle* h = list_[i];
|
||||
|
||||
Vendored
+2
-2
@@ -546,7 +546,7 @@ TYPED_TEST(ClockCacheTest, Limits) {
|
||||
// verify usage tracking on detached entries.)
|
||||
{
|
||||
size_t n = kCapacity * 5 + 1;
|
||||
std::unique_ptr<HandleImpl* []> ha { new HandleImpl* [n] {} };
|
||||
std::unique_ptr<HandleImpl*[]> ha{new HandleImpl* [n] {}};
|
||||
Status s;
|
||||
for (size_t i = 0; i < n && s.ok(); ++i) {
|
||||
hkey[1] = i;
|
||||
@@ -713,7 +713,7 @@ TYPED_TEST(ClockCacheTest, ClockEvictionEffortCapTest) {
|
||||
// evictable entries are present beyond the cache capacity, despite
|
||||
// being evictable.
|
||||
constexpr size_t kCount = kCapacity - 1;
|
||||
std::unique_ptr<HandleImpl* []> ha { new HandleImpl* [kCount] {} };
|
||||
std::unique_ptr<HandleImpl*[]> ha{new HandleImpl* [kCount] {}};
|
||||
for (size_t i = 0; i < 2 * kCount; ++i) {
|
||||
UniqueId64x2 hkey = this->CheapHash(i);
|
||||
ASSERT_OK(shard.Insert(
|
||||
|
||||
Vendored
+1
-3
@@ -7,6 +7,4 @@
|
||||
|
||||
#include "cache/cache_entry_roles.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
namespace ROCKSDB_NAMESPACE {} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+13
-5
@@ -951,12 +951,20 @@ TEST_F(DBTieredSecondaryCacheTest, FSBufferTest) {
|
||||
IODebugContext* dbg) override {
|
||||
for (size_t i = 0; i < num_reqs; ++i) {
|
||||
FSReadRequest& req = reqs[i];
|
||||
FSAllocationPtr buffer(new char[req.len], [](void* ptr) {
|
||||
delete[] static_cast<char*>(ptr);
|
||||
});
|
||||
req.fs_scratch = std::move(buffer);
|
||||
// See https://github.com/facebook/rocksdb/pull/13195 for why we
|
||||
// want to set up our test implementation for FSAllocationPtr this
|
||||
// way.
|
||||
char* internalData = new char[req.len];
|
||||
req.status = Read(req.offset, req.len, options, &req.result,
|
||||
static_cast<char*>(req.fs_scratch.get()), dbg);
|
||||
internalData, dbg);
|
||||
|
||||
Slice* internalSlice = new Slice(internalData, req.len);
|
||||
FSAllocationPtr internalPtr(internalSlice, [](void* ptr) {
|
||||
delete[] static_cast<const char*>(
|
||||
static_cast<Slice*>(ptr)->data_);
|
||||
delete static_cast<Slice*>(ptr);
|
||||
});
|
||||
req.fs_scratch = std::move(internalPtr);
|
||||
}
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
+112
-25
@@ -64,8 +64,118 @@ void ArenaWrappedDBIter::Init(
|
||||
memtable_range_tombstone_iter_ = nullptr;
|
||||
}
|
||||
|
||||
void ArenaWrappedDBIter::MaybeAutoRefresh(bool is_seek,
|
||||
DBIter::Direction direction) {
|
||||
if (cfh_ != nullptr && read_options_.snapshot != nullptr && allow_refresh_ &&
|
||||
read_options_.auto_refresh_iterator_with_snapshot) {
|
||||
// The intent here is to capture the superversion number change
|
||||
// reasonably soon from the time it actually happened. As such,
|
||||
// we're fine with weaker synchronization / ordering guarantees
|
||||
// provided by relaxed atomic (in favor of less CPU / mem overhead).
|
||||
uint64_t cur_sv_number = cfh_->cfd()->GetSuperVersionNumberRelaxed();
|
||||
if ((sv_number_ != cur_sv_number) && status().ok()) {
|
||||
// Changing iterators' direction is pretty heavy-weight operation and
|
||||
// could have unintended consequences when it comes to prefix seek.
|
||||
// Therefore, we need an efficient implementation that does not duplicate
|
||||
// the effort by doing things like double seek(forprev).
|
||||
//
|
||||
// Auto refresh can be triggered on the following groups of operations:
|
||||
//
|
||||
// 1. [Seek]: Seek(), SeekForPrev()
|
||||
// 2. [Non-Seek]: Next(), Prev()
|
||||
//
|
||||
// In case of 'Seek' group, procedure is fairly straightforward as we'll
|
||||
// simply call refresh and then invoke the operation on intended target.
|
||||
//
|
||||
// In case of 'Non-Seek' group, we'll first advance the cursor by invoking
|
||||
// intended user operation (Next() or Prev()), capture the target key T,
|
||||
// refresh the iterator and then reconcile the refreshed iterator by
|
||||
// explicitly calling [Seek(T) or SeekForPrev(T)]. Below is an example
|
||||
// flow for Next(), but same principle applies to Prev():
|
||||
//
|
||||
//
|
||||
// T0: Before the operation T1: Execute Next()
|
||||
// | |
|
||||
// | -------------
|
||||
// | | * capture the key (T)
|
||||
// DBIter(SV#A) | |
|
||||
// --------------\ /------\ /---------
|
||||
// SV #A | ... -> [ X ] -> [ T ] -> ... |
|
||||
// -----------------------------------
|
||||
// / |
|
||||
// / |
|
||||
// / T2: Refresh iterator
|
||||
// /
|
||||
// DBIter(SV#A') /
|
||||
// ----------------------------------
|
||||
// SV #A' | ... -> [ T ] -> ... |
|
||||
// ----------------/ \---------------
|
||||
// |
|
||||
// ---- T3: Seek(T)
|
||||
//
|
||||
bool valid = false;
|
||||
std::string key;
|
||||
if (!is_seek && db_iter_->Valid()) {
|
||||
// The key() Slice is valid until the iterator state changes.
|
||||
// Given that refresh is heavy-weight operation it itself,
|
||||
// we should copy the target key upfront to avoid reading bad value.
|
||||
valid = true;
|
||||
key = db_iter_->key().ToString();
|
||||
}
|
||||
|
||||
// It's perfectly fine to unref the corresponding superversion
|
||||
// as we rely on pinning behavior of snapshot for consistency.
|
||||
DoRefresh(read_options_.snapshot, cur_sv_number);
|
||||
|
||||
if (!is_seek && valid) { // Reconcile new iterator after Next() / Prev()
|
||||
if (direction == DBIter::kForward) {
|
||||
db_iter_->Seek(key);
|
||||
} else {
|
||||
assert(direction == DBIter::kReverse);
|
||||
db_iter_->SeekForPrev(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status ArenaWrappedDBIter::Refresh() { return Refresh(nullptr); }
|
||||
|
||||
void ArenaWrappedDBIter::DoRefresh(const Snapshot* snapshot,
|
||||
[[maybe_unused]] uint64_t sv_number) {
|
||||
Env* env = db_iter_->env();
|
||||
|
||||
// NOTE:
|
||||
//
|
||||
// Errors like file deletion (as a part of SV cleanup in ~DBIter) will be
|
||||
// present in the error log, but won't be reflected in the iterator status.
|
||||
// This is by design as we expect compaction to clean up those obsolete files
|
||||
// eventually.
|
||||
db_iter_->~DBIter();
|
||||
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
|
||||
auto cfd = cfh_->cfd();
|
||||
auto db_impl = cfh_->db();
|
||||
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(db_impl);
|
||||
assert(sv->version_number >= sv_number);
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(read_seq);
|
||||
}
|
||||
Init(env, read_options_, cfd->ioptions(), sv->mutable_cf_options, sv->current,
|
||||
read_seq, sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback_, cfh_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl->NewInternalIterator(
|
||||
read_options_, cfd, sv, &arena_, read_seq,
|
||||
/* allow_unprepared_value */ true, /* db_iter */ this);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
}
|
||||
|
||||
Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
if (cfh_ == nullptr || !allow_refresh_) {
|
||||
return Status::NotSupported("Creating renew iterator is not allowed.");
|
||||
@@ -85,32 +195,9 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:1");
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:2");
|
||||
|
||||
auto reinit_internal_iter = [&]() {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(db_impl);
|
||||
assert(sv->version_number >= cur_sv_number);
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(read_seq);
|
||||
}
|
||||
Init(env, read_options_, *(cfd->ioptions()), sv->mutable_cf_options,
|
||||
sv->current, read_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback_, cfh_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl->NewInternalIterator(
|
||||
read_options_, cfd, sv, &arena_, read_seq,
|
||||
/* allow_unprepared_value */ true, /* db_iter */ this);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
};
|
||||
while (true) {
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
reinit_internal_iter();
|
||||
DoRefresh(snapshot, cur_sv_number);
|
||||
break;
|
||||
} else {
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
|
||||
@@ -138,7 +225,7 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
// The memtable under DBIter did not have range tombstone before
|
||||
// refresh.
|
||||
reinit_internal_iter();
|
||||
DoRefresh(snapshot, cur_sv_number);
|
||||
break;
|
||||
} else {
|
||||
*memtable_range_tombstone_iter_ =
|
||||
|
||||
@@ -65,12 +65,26 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
void SeekToLast() override { db_iter_->SeekToLast(); }
|
||||
// 'target' does not contain timestamp, even if user timestamp feature is
|
||||
// enabled.
|
||||
void Seek(const Slice& target) override { db_iter_->Seek(target); }
|
||||
void Seek(const Slice& target) override {
|
||||
MaybeAutoRefresh(true /* is_seek */, DBIter::kForward);
|
||||
db_iter_->Seek(target);
|
||||
}
|
||||
|
||||
void SeekForPrev(const Slice& target) override {
|
||||
MaybeAutoRefresh(true /* is_seek */, DBIter::kReverse);
|
||||
db_iter_->SeekForPrev(target);
|
||||
}
|
||||
void Next() override { db_iter_->Next(); }
|
||||
void Prev() override { db_iter_->Prev(); }
|
||||
|
||||
void Next() override {
|
||||
db_iter_->Next();
|
||||
MaybeAutoRefresh(false /* is_seek */, DBIter::kForward);
|
||||
}
|
||||
|
||||
void Prev() override {
|
||||
db_iter_->Prev();
|
||||
MaybeAutoRefresh(false /* is_seek */, DBIter::kReverse);
|
||||
}
|
||||
|
||||
Slice key() const override { return db_iter_->key(); }
|
||||
Slice value() const override { return db_iter_->value(); }
|
||||
const WideColumns& columns() const override { return db_iter_->columns(); }
|
||||
@@ -103,6 +117,9 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
}
|
||||
|
||||
private:
|
||||
void DoRefresh(const Snapshot* snapshot, uint64_t sv_number);
|
||||
void MaybeAutoRefresh(bool is_seek, DBIter::Direction direction);
|
||||
|
||||
DBIter* db_iter_ = nullptr;
|
||||
Arena arena_;
|
||||
uint64_t sv_number_;
|
||||
|
||||
@@ -188,10 +188,12 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
|
||||
}
|
||||
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
|
||||
FileOptions fo_copy;
|
||||
{
|
||||
assert(file_options_);
|
||||
Status s = NewWritableFile(fs_, blob_file_path, &file, *file_options_);
|
||||
fo_copy = *file_options_;
|
||||
fo_copy.write_hint = write_hint_;
|
||||
Status s = NewWritableFile(fs_, blob_file_path, &file, fo_copy);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"BlobFileBuilder::OpenBlobFileIfNeeded:NewWritableFile", &s);
|
||||
@@ -209,7 +211,9 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
|
||||
|
||||
assert(file);
|
||||
file->SetIOPriority(write_options_->rate_limiter_priority);
|
||||
file->SetWriteLifeTimeHint(write_hint_);
|
||||
// Subsequent attempts to override the hint via SetWriteLifeTimeHint
|
||||
// with the very same value will be ignored by the fs.
|
||||
file->SetWriteLifeTimeHint(fo_copy.write_hint);
|
||||
FileTypeSet tmp_set = immutable_options_->checksum_handoff_file_types;
|
||||
Statistics* const statistics = immutable_options_->stats;
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
|
||||
@@ -82,8 +82,8 @@ Status BlobSource::PutBlobIntoCache(
|
||||
assert(cached_blob->IsEmpty());
|
||||
|
||||
TypedHandle* cache_handle = nullptr;
|
||||
const Status s = InsertEntryIntoCache(cache_key, blob->get(),
|
||||
&cache_handle, Cache::Priority::BOTTOM);
|
||||
const Status s = InsertEntryIntoCache(cache_key, blob->get(), &cache_handle,
|
||||
Cache::Priority::BOTTOM);
|
||||
if (s.ok()) {
|
||||
blob->release();
|
||||
|
||||
|
||||
+6
-2
@@ -145,7 +145,9 @@ Status BuildTable(
|
||||
bool use_direct_writes = file_options.use_direct_writes;
|
||||
TEST_SYNC_POINT_CALLBACK("BuildTable:create_file", &use_direct_writes);
|
||||
#endif // !NDEBUG
|
||||
IOStatus io_s = NewWritableFile(fs, fname, &file, file_options);
|
||||
FileOptions fo_copy = file_options;
|
||||
fo_copy.write_hint = write_hint;
|
||||
IOStatus io_s = NewWritableFile(fs, fname, &file, fo_copy);
|
||||
assert(s.ok());
|
||||
s = io_s;
|
||||
if (io_status->ok()) {
|
||||
@@ -163,7 +165,9 @@ Status BuildTable(
|
||||
table_file_created = true;
|
||||
FileTypeSet tmp_set = ioptions.checksum_handoff_file_types;
|
||||
file->SetIOPriority(tboptions.write_options.rate_limiter_priority);
|
||||
file->SetWriteLifeTimeHint(write_hint);
|
||||
// Subsequent attempts to override the hint via SetWriteLifeTimeHint
|
||||
// with the very same value will be ignored by the fs.
|
||||
file->SetWriteLifeTimeHint(fo_copy.write_hint);
|
||||
file_writer.reset(new WritableFileWriter(
|
||||
std::move(file), fname, file_options, ioptions.clock, io_tracer,
|
||||
ioptions.stats, Histograms::SST_WRITE_MICROS, ioptions.listeners,
|
||||
|
||||
@@ -231,7 +231,7 @@ struct rocksdb_livefiles_t {
|
||||
};
|
||||
struct rocksdb_column_family_handle_t {
|
||||
ColumnFamilyHandle* rep;
|
||||
bool immortal; /* only true for default cf */
|
||||
bool immortal; /* only true for default cf */
|
||||
};
|
||||
struct rocksdb_column_family_metadata_t {
|
||||
ColumnFamilyMetaData rep;
|
||||
@@ -1858,10 +1858,6 @@ extern ROCKSDB_LIBRARY_API void rocksdb_approximate_sizes_cf_with_flags(
|
||||
delete[] ranges;
|
||||
}
|
||||
|
||||
void rocksdb_delete_file(rocksdb_t* db, const char* name) {
|
||||
db->rep->DeleteFile(name);
|
||||
}
|
||||
|
||||
const rocksdb_livefiles_t* rocksdb_livefiles(rocksdb_t* db) {
|
||||
rocksdb_livefiles_t* result = new rocksdb_livefiles_t;
|
||||
db->rep->GetLiveFilesMetaData(&result->rep);
|
||||
@@ -5000,6 +4996,35 @@ int rocksdb_compactoptions_get_target_level(rocksdb_compactoptions_t* opt) {
|
||||
return opt->rep.target_level;
|
||||
}
|
||||
|
||||
void rocksdb_compactoptions_set_target_path_id(rocksdb_compactoptions_t* opt,
|
||||
int n) {
|
||||
opt->rep.target_path_id = n;
|
||||
}
|
||||
|
||||
int rocksdb_compactoptions_get_target_path_id(rocksdb_compactoptions_t* opt) {
|
||||
return opt->rep.target_path_id;
|
||||
}
|
||||
|
||||
void rocksdb_compactoptions_set_allow_write_stall(rocksdb_compactoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.allow_write_stall = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_compactoptions_get_allow_write_stall(
|
||||
rocksdb_compactoptions_t* opt) {
|
||||
return opt->rep.allow_write_stall;
|
||||
}
|
||||
|
||||
void rocksdb_compactoptions_set_max_subcompactions(
|
||||
rocksdb_compactoptions_t* opt, int n) {
|
||||
opt->rep.max_subcompactions = n;
|
||||
}
|
||||
|
||||
int rocksdb_compactoptions_get_max_subcompactions(
|
||||
rocksdb_compactoptions_t* opt) {
|
||||
return opt->rep.max_subcompactions;
|
||||
}
|
||||
|
||||
void rocksdb_compactoptions_set_full_history_ts_low(
|
||||
rocksdb_compactoptions_t* opt, char* ts, size_t tslen) {
|
||||
if (ts == nullptr) {
|
||||
|
||||
@@ -2938,6 +2938,15 @@ int main(int argc, char** argv) {
|
||||
rocksdb_compactoptions_set_target_level(co, 1);
|
||||
CheckCondition(1 == rocksdb_compactoptions_get_target_level(co));
|
||||
|
||||
rocksdb_compactoptions_set_target_path_id(co, 1);
|
||||
CheckCondition(1 == rocksdb_compactoptions_get_target_path_id(co));
|
||||
|
||||
rocksdb_compactoptions_set_allow_write_stall(co, 1);
|
||||
CheckCondition(1 == rocksdb_compactoptions_get_allow_write_stall(co));
|
||||
|
||||
rocksdb_compactoptions_set_max_subcompactions(co, 1);
|
||||
CheckCondition(1 == rocksdb_compactoptions_get_max_subcompactions(co));
|
||||
|
||||
rocksdb_compactoptions_destroy(co);
|
||||
}
|
||||
|
||||
|
||||
+59
-33
@@ -53,7 +53,7 @@ ColumnFamilyHandleImpl::ColumnFamilyHandleImpl(
|
||||
|
||||
ColumnFamilyHandleImpl::~ColumnFamilyHandleImpl() {
|
||||
if (cfd_ != nullptr) {
|
||||
for (auto& listener : cfd_->ioptions()->listeners) {
|
||||
for (auto& listener : cfd_->ioptions().listeners) {
|
||||
listener->OnColumnFamilyHandleDeletionStarted(this);
|
||||
}
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
@@ -168,7 +168,8 @@ Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options) {
|
||||
}
|
||||
if (!cf_options.memtable_factory->IsInsertConcurrentlySupported()) {
|
||||
return Status::InvalidArgument(
|
||||
"Memtable doesn't concurrent writes (allow_concurrent_memtable_write)");
|
||||
"Memtable doesn't allow concurrent writes "
|
||||
"(allow_concurrent_memtable_write)");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -199,8 +200,9 @@ const uint64_t kDefaultTtl = 0xfffffffffffffffe;
|
||||
const uint64_t kDefaultPeriodicCompSecs = 0xfffffffffffffffe;
|
||||
} // anonymous namespace
|
||||
|
||||
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src) {
|
||||
ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
|
||||
bool read_only,
|
||||
const ColumnFamilyOptions& src) {
|
||||
ColumnFamilyOptions result = src;
|
||||
size_t clamp_max = std::conditional<
|
||||
sizeof(size_t) == 4, std::integral_constant<size_t, 0xffffffff>,
|
||||
@@ -239,6 +241,10 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
|
||||
result.min_write_buffer_number_to_merge = 1;
|
||||
}
|
||||
if (result.disallow_memtable_writes) {
|
||||
// A simple memtable that enforces MarkReadOnly (unlike skip list)
|
||||
result.memtable_factory = std::make_shared<VectorRepFactory>();
|
||||
}
|
||||
|
||||
if (result.num_levels < 1) {
|
||||
result.num_levels = 1;
|
||||
@@ -435,6 +441,18 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
result.periodic_compaction_seconds = 0;
|
||||
}
|
||||
|
||||
if (read_only && (result.preserve_internal_time_seconds > 0 ||
|
||||
result.preclude_last_level_data_seconds > 0)) {
|
||||
// With no writes coming in, we don't need periodic SeqnoToTime entries.
|
||||
// Existing SST files may or may not have that info associated with them.
|
||||
ROCKS_LOG_WARN(
|
||||
db_options.info_log.get(),
|
||||
"preserve_internal_time_seconds and preclude_last_level_data_seconds "
|
||||
"are ignored in read-only DB");
|
||||
result.preserve_internal_time_seconds = 0;
|
||||
result.preclude_last_level_data_seconds = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -492,6 +510,17 @@ void SuperVersion::Init(
|
||||
imm->Ref();
|
||||
current->Ref();
|
||||
refs.store(1, std::memory_order_relaxed);
|
||||
|
||||
// There should be at least one mapping entry iff time tracking is enabled.
|
||||
#ifndef NDEBUG
|
||||
MinAndMaxPreserveSeconds preserve_info{mutable_cf_options};
|
||||
if (preserve_info.IsEnabled()) {
|
||||
assert(seqno_to_time_mapping);
|
||||
assert(!seqno_to_time_mapping->Empty());
|
||||
} else {
|
||||
assert(seqno_to_time_mapping == nullptr);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -530,7 +559,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
const FileOptions* file_options, ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer, const std::string& db_id,
|
||||
const std::string& db_session_id)
|
||||
const std::string& db_session_id, bool read_only)
|
||||
: id_(id),
|
||||
name_(name),
|
||||
dummy_versions_(_dummy_versions),
|
||||
@@ -540,7 +569,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
dropped_(false),
|
||||
flush_skip_reschedule_(false),
|
||||
internal_comparator_(cf_options.comparator),
|
||||
initial_cf_options_(SanitizeOptions(db_options, cf_options)),
|
||||
initial_cf_options_(SanitizeCfOptions(db_options, read_only, cf_options)),
|
||||
ioptions_(db_options, initial_cf_options_),
|
||||
mutable_cf_options_(initial_cf_options_),
|
||||
is_delete_range_supported_(
|
||||
@@ -593,7 +622,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
block_cache_tracer, io_tracer,
|
||||
db_session_id));
|
||||
blob_file_cache_.reset(
|
||||
new BlobFileCache(_table_cache, ioptions(), soptions(), id_,
|
||||
new BlobFileCache(_table_cache, &ioptions(), soptions(), id_,
|
||||
internal_stats_->GetBlobFileReadHist(), io_tracer));
|
||||
blob_source_.reset(new BlobSource(ioptions_, mutable_cf_options_, db_id,
|
||||
db_session_id, blob_file_cache_.get()));
|
||||
@@ -968,7 +997,7 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
auto write_stall_condition_and_cause = GetWriteStallConditionAndCause(
|
||||
imm()->NumNotFlushed(), vstorage->l0_delay_trigger_count(),
|
||||
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options,
|
||||
*ioptions());
|
||||
ioptions());
|
||||
write_stall_condition = write_stall_condition_and_cause.first;
|
||||
auto write_stall_cause = write_stall_condition_and_cause.second;
|
||||
|
||||
@@ -1160,12 +1189,13 @@ MemTable* ColumnFamilyData::ConstructNewMemtable(
|
||||
write_buffer_manager_, earliest_seq, id_);
|
||||
}
|
||||
|
||||
void ColumnFamilyData::CreateNewMemtable(
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq) {
|
||||
void ColumnFamilyData::CreateNewMemtable(SequenceNumber earliest_seq) {
|
||||
if (mem_ != nullptr) {
|
||||
delete mem_->Unref();
|
||||
}
|
||||
SetMemtable(ConstructNewMemtable(mutable_cf_options, earliest_seq));
|
||||
// NOTE: db mutex must be locked for SetMemtable, so safe for
|
||||
// GetLatestMutableCFOptions
|
||||
SetMemtable(ConstructNewMemtable(GetLatestMutableCFOptions(), earliest_seq));
|
||||
mem_->Ref();
|
||||
}
|
||||
|
||||
@@ -1338,20 +1368,17 @@ bool ColumnFamilyData::ReturnThreadLocalSuperVersion(SuperVersion* sv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ColumnFamilyData::InstallSuperVersion(SuperVersionContext* sv_context,
|
||||
InstrumentedMutex* db_mutex) {
|
||||
db_mutex->AssertHeld();
|
||||
return InstallSuperVersion(sv_context, mutable_cf_options_);
|
||||
}
|
||||
|
||||
void ColumnFamilyData::InstallSuperVersion(
|
||||
SuperVersionContext* sv_context,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
SuperVersionContext* sv_context, InstrumentedMutex* db_mutex,
|
||||
std::optional<std::shared_ptr<SeqnoToTimeMapping>>
|
||||
new_seqno_to_time_mapping) {
|
||||
db_mutex->AssertHeld();
|
||||
|
||||
SuperVersion* new_superversion = sv_context->new_superversion.release();
|
||||
new_superversion->mutable_cf_options = mutable_cf_options;
|
||||
new_superversion->mutable_cf_options = GetLatestMutableCFOptions();
|
||||
new_superversion->Init(this, mem_, imm_.current(), current_,
|
||||
sv_context->new_seqno_to_time_mapping
|
||||
? std::move(sv_context->new_seqno_to_time_mapping)
|
||||
new_seqno_to_time_mapping.has_value()
|
||||
? std::move(new_seqno_to_time_mapping.value())
|
||||
: super_version_
|
||||
? super_version_->ShareSeqnoToTimeMapping()
|
||||
: nullptr);
|
||||
@@ -1364,7 +1391,7 @@ void ColumnFamilyData::InstallSuperVersion(
|
||||
// currently RecalculateWriteStallConditions() treats it as further slowing
|
||||
// down is needed.
|
||||
super_version_->write_stall_condition =
|
||||
RecalculateWriteStallConditions(mutable_cf_options);
|
||||
RecalculateWriteStallConditions(new_superversion->mutable_cf_options);
|
||||
} else {
|
||||
super_version_->write_stall_condition =
|
||||
old_superversion->write_stall_condition;
|
||||
@@ -1377,14 +1404,15 @@ void ColumnFamilyData::InstallSuperVersion(
|
||||
ResetThreadLocalSuperVersions();
|
||||
|
||||
if (old_superversion->mutable_cf_options.write_buffer_size !=
|
||||
mutable_cf_options.write_buffer_size) {
|
||||
mem_->UpdateWriteBufferSize(mutable_cf_options.write_buffer_size);
|
||||
new_superversion->mutable_cf_options.write_buffer_size) {
|
||||
mem_->UpdateWriteBufferSize(
|
||||
new_superversion->mutable_cf_options.write_buffer_size);
|
||||
}
|
||||
if (old_superversion->write_stall_condition !=
|
||||
new_superversion->write_stall_condition) {
|
||||
sv_context->PushWriteStallNotification(
|
||||
old_superversion->write_stall_condition,
|
||||
new_superversion->write_stall_condition, GetName(), ioptions());
|
||||
new_superversion->write_stall_condition, GetName(), &ioptions());
|
||||
}
|
||||
if (old_superversion->Unref()) {
|
||||
old_superversion->Cleanup();
|
||||
@@ -1679,7 +1707,8 @@ ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
dummy_cfd_(new ColumnFamilyData(
|
||||
ColumnFamilyData::kDummyColumnFamilyDataId, "", nullptr, nullptr,
|
||||
nullptr, ColumnFamilyOptions(), *db_options, &file_options_, nullptr,
|
||||
block_cache_tracer, io_tracer, db_id, db_session_id)),
|
||||
block_cache_tracer, io_tracer, db_id, db_session_id,
|
||||
/*read_only*/ true)),
|
||||
default_cfd_cache_(nullptr),
|
||||
db_name_(dbname),
|
||||
db_options_(db_options),
|
||||
@@ -1751,12 +1780,12 @@ size_t ColumnFamilySet::NumberOfColumnFamilies() const {
|
||||
// under a DB mutex AND write thread
|
||||
ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
const std::string& name, uint32_t id, Version* dummy_versions,
|
||||
const ColumnFamilyOptions& options) {
|
||||
const ColumnFamilyOptions& options, bool read_only) {
|
||||
assert(column_families_.find(name) == column_families_.end());
|
||||
ColumnFamilyData* new_cfd = new ColumnFamilyData(
|
||||
id, name, dummy_versions, table_cache_, write_buffer_manager_, options,
|
||||
*db_options_, &file_options_, this, block_cache_tracer_, io_tracer_,
|
||||
db_id_, db_session_id_);
|
||||
db_id_, db_session_id_, read_only);
|
||||
column_families_.insert({name, id});
|
||||
column_family_data_.insert({id, new_cfd});
|
||||
auto ucmp = new_cfd->user_comparator();
|
||||
@@ -1844,10 +1873,7 @@ const ImmutableOptions& GetImmutableOptions(ColumnFamilyHandle* column_family) {
|
||||
const ColumnFamilyData* const cfd = handle->cfd();
|
||||
assert(cfd);
|
||||
|
||||
const ImmutableOptions* ioptions = cfd->ioptions();
|
||||
assert(ioptions);
|
||||
|
||||
return *ioptions;
|
||||
return cfd->ioptions();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+40
-35
@@ -210,6 +210,7 @@ struct SuperVersion {
|
||||
ReadOnlyMemTable* mem;
|
||||
MemTableListVersion* imm;
|
||||
Version* current;
|
||||
// TODO: do we really need this in addition to what's in current Version?
|
||||
MutableCFOptions mutable_cf_options;
|
||||
// Version number of the current SuperVersion
|
||||
uint64_t version_number;
|
||||
@@ -221,7 +222,8 @@ struct SuperVersion {
|
||||
// enable UDT feature, this is an empty string.
|
||||
std::string full_history_ts_low;
|
||||
|
||||
// A shared copy of the DB's seqno to time mapping.
|
||||
// An immutable snapshot of the DB's seqno to time mapping, usually shared
|
||||
// between SuperVersions.
|
||||
std::shared_ptr<const SeqnoToTimeMapping> seqno_to_time_mapping{nullptr};
|
||||
|
||||
// should be called outside the mutex
|
||||
@@ -279,8 +281,9 @@ Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options);
|
||||
Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
|
||||
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src);
|
||||
ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
|
||||
bool read_only,
|
||||
const ColumnFamilyOptions& src);
|
||||
// Wrap user defined table properties collector factories `from cf_options`
|
||||
// into internal ones in internal_tbl_prop_coll_factories. Add a system internal
|
||||
// one too.
|
||||
@@ -342,17 +345,17 @@ class ColumnFamilyData {
|
||||
|
||||
// thread-safe
|
||||
const FileOptions* soptions() const;
|
||||
const ImmutableOptions* ioptions() const { return &ioptions_; }
|
||||
const ImmutableOptions& ioptions() const { return ioptions_; }
|
||||
// REQUIRES: DB mutex held
|
||||
// This returns the MutableCFOptions used by current SuperVersion
|
||||
// You should use this API to reference MutableCFOptions most of the time.
|
||||
const MutableCFOptions* GetCurrentMutableCFOptions() const {
|
||||
return &(super_version_->mutable_cf_options);
|
||||
const MutableCFOptions& GetCurrentMutableCFOptions() const {
|
||||
return super_version_->mutable_cf_options;
|
||||
}
|
||||
// REQUIRES: DB mutex held
|
||||
// This returns the latest MutableCFOptions, which may be not in effect yet.
|
||||
const MutableCFOptions* GetLatestMutableCFOptions() const {
|
||||
return &mutable_cf_options_;
|
||||
const MutableCFOptions& GetLatestMutableCFOptions() const {
|
||||
return mutable_cf_options_;
|
||||
}
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
@@ -379,17 +382,20 @@ class ColumnFamilyData {
|
||||
return mem()->GetFirstSequenceNumber() == 0 && imm()->NumNotFlushed() == 0;
|
||||
}
|
||||
|
||||
Version* current() { return current_; }
|
||||
Version* dummy_versions() { return dummy_versions_; }
|
||||
void SetCurrent(Version* _current);
|
||||
uint64_t GetNumLiveVersions() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetTotalSstFilesSize() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetLiveSstFilesSize() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetTotalBlobFileSize() const; // REQUIRE: DB mutex held
|
||||
Version* current() { return current_; } // REQUIRE: DB mutex held
|
||||
void SetCurrent(Version* _current); // REQUIRE: DB mutex held
|
||||
uint64_t GetNumLiveVersions() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetTotalSstFilesSize() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetLiveSstFilesSize() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetTotalBlobFileSize() const; // REQUIRE: DB mutex held
|
||||
// REQUIRE: DB mutex held
|
||||
void SetMemtable(MemTable* new_mem) {
|
||||
AssignMemtableID(new_mem);
|
||||
mem_ = new_mem;
|
||||
if (ioptions_.disallow_memtable_writes) {
|
||||
mem_->MarkImmutable();
|
||||
}
|
||||
}
|
||||
|
||||
void AssignMemtableID(ReadOnlyMemTable* new_imm) {
|
||||
@@ -400,10 +406,11 @@ class ColumnFamilyData {
|
||||
uint64_t OldestLogToKeep();
|
||||
|
||||
// See Memtable constructor for explanation of earliest_seq param.
|
||||
// `mutable_cf_options` might need to be a saved copy if calling this without
|
||||
// holding the DB mutex.
|
||||
MemTable* ConstructNewMemtable(const MutableCFOptions& mutable_cf_options,
|
||||
SequenceNumber earliest_seq);
|
||||
void CreateNewMemtable(const MutableCFOptions& mutable_cf_options,
|
||||
SequenceNumber earliest_seq);
|
||||
void CreateNewMemtable(SequenceNumber earliest_seq);
|
||||
|
||||
TableCache* table_cache() const { return table_cache_.get(); }
|
||||
BlobFileCache* blob_file_cache() const { return blob_file_cache_.get(); }
|
||||
@@ -481,15 +488,14 @@ class ColumnFamilyData {
|
||||
uint64_t GetSuperVersionNumber() const {
|
||||
return super_version_number_.load();
|
||||
}
|
||||
// will return a pointer to SuperVersion* if previous SuperVersion
|
||||
// if its reference count is zero and needs deletion or nullptr if not
|
||||
// As argument takes a pointer to allocated SuperVersion to enable
|
||||
// the clients to allocate SuperVersion outside of mutex.
|
||||
// IMPORTANT: Only call this from DBImpl::InstallSuperVersion()
|
||||
uint64_t GetSuperVersionNumberRelaxed() const {
|
||||
return super_version_number_.load(std::memory_order_relaxed);
|
||||
}
|
||||
// Only intended for use by DBImpl::InstallSuperVersion() and variants
|
||||
void InstallSuperVersion(SuperVersionContext* sv_context,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
void InstallSuperVersion(SuperVersionContext* sv_context,
|
||||
InstrumentedMutex* db_mutex);
|
||||
InstrumentedMutex* db_mutex,
|
||||
std::optional<std::shared_ptr<SeqnoToTimeMapping>>
|
||||
new_seqno_to_time_mapping = {});
|
||||
|
||||
void ResetThreadLocalSuperVersions();
|
||||
|
||||
@@ -584,16 +590,14 @@ class ColumnFamilyData {
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
Version* dummy_versions, Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
const ColumnFamilyOptions& options,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const FileOptions* file_options,
|
||||
ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::string& db_id, const std::string& db_session_id);
|
||||
ColumnFamilyData(
|
||||
uint32_t id, const std::string& name, Version* dummy_versions,
|
||||
Cache* table_cache, WriteBufferManager* write_buffer_manager,
|
||||
const ColumnFamilyOptions& options, const ImmutableDBOptions& db_options,
|
||||
const FileOptions* file_options, ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer, const std::string& db_id,
|
||||
const std::string& db_session_id, bool read_only);
|
||||
|
||||
std::vector<std::string> GetDbPaths() const;
|
||||
|
||||
@@ -755,7 +759,8 @@ class ColumnFamilySet {
|
||||
|
||||
ColumnFamilyData* CreateColumnFamily(const std::string& name, uint32_t id,
|
||||
Version* dummy_version,
|
||||
const ColumnFamilyOptions& options);
|
||||
const ColumnFamilyOptions& options,
|
||||
bool read_only);
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>& GetRunningColumnFamiliesTimestampSize()
|
||||
const {
|
||||
|
||||
@@ -271,7 +271,8 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
// them.
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
|
||||
ConfigOptions(), desc.options,
|
||||
SanitizeOptions(dbfull()->immutable_db_options(), current_cf_opt)));
|
||||
SanitizeCfOptions(dbfull()->immutable_db_options(),
|
||||
/*read_only*/ false, current_cf_opt)));
|
||||
cfi++;
|
||||
}
|
||||
}
|
||||
@@ -2232,7 +2233,7 @@ TEST_P(ColumnFamilyTest, CreateMissingColumnFamilies) {
|
||||
ASSERT_EQ(my_fs->options_files_created.load(), 2);
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, SanitizeOptions) {
|
||||
TEST_P(ColumnFamilyTest, SanitizeCfOptions) {
|
||||
DBOptions db_options;
|
||||
for (int s = kCompactionStyleLevel; s <= kCompactionStyleUniversal; ++s) {
|
||||
for (int l = 0; l <= 2; l++) {
|
||||
@@ -2248,8 +2249,8 @@ TEST_P(ColumnFamilyTest, SanitizeOptions) {
|
||||
original.write_buffer_size =
|
||||
l * 4 * 1024 * 1024 + i * 1024 * 1024 + j * 1024 + k;
|
||||
|
||||
ColumnFamilyOptions result =
|
||||
SanitizeOptions(ImmutableDBOptions(db_options), original);
|
||||
ColumnFamilyOptions result = SanitizeCfOptions(
|
||||
ImmutableDBOptions(db_options), /*read_only*/ false, original);
|
||||
ASSERT_TRUE(result.level0_stop_writes_trigger >=
|
||||
result.level0_slowdown_writes_trigger);
|
||||
ASSERT_TRUE(result.level0_slowdown_writes_trigger >=
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
+95
-70
@@ -332,22 +332,22 @@ Compaction::Compaction(
|
||||
: (_blob_garbage_collection_policy ==
|
||||
BlobGarbageCollectionPolicy::kDisable
|
||||
? false
|
||||
: mutable_cf_options()->enable_blob_garbage_collection)),
|
||||
: mutable_cf_options().enable_blob_garbage_collection)),
|
||||
blob_garbage_collection_age_cutoff_(
|
||||
_blob_garbage_collection_age_cutoff < 0 ||
|
||||
_blob_garbage_collection_age_cutoff > 1
|
||||
? mutable_cf_options()->blob_garbage_collection_age_cutoff
|
||||
? mutable_cf_options().blob_garbage_collection_age_cutoff
|
||||
: _blob_garbage_collection_age_cutoff),
|
||||
penultimate_level_(
|
||||
// For simplicity, we don't support the concept of "penultimate level"
|
||||
proximal_level_(
|
||||
// For simplicity, we don't support the concept of "proximal level"
|
||||
// with `CompactionReason::kExternalSstIngestion` and
|
||||
// `CompactionReason::kRefitLevel`
|
||||
_compaction_reason == CompactionReason::kExternalSstIngestion ||
|
||||
_compaction_reason == CompactionReason::kRefitLevel
|
||||
? Compaction::kInvalidLevel
|
||||
: EvaluatePenultimateLevel(vstorage, mutable_cf_options_,
|
||||
immutable_options_, start_level_,
|
||||
output_level_)) {
|
||||
: EvaluateProximalLevel(vstorage, mutable_cf_options_,
|
||||
immutable_options_, start_level_,
|
||||
output_level_)) {
|
||||
MarkFilesBeingCompacted(true);
|
||||
if (is_manual_compaction_) {
|
||||
compaction_reason_ = CompactionReason::kManualCompaction;
|
||||
@@ -405,53 +405,71 @@ Compaction::Compaction(
|
||||
}
|
||||
}
|
||||
|
||||
PopulatePenultimateLevelOutputRange();
|
||||
PopulateProximalLevelOutputRange();
|
||||
}
|
||||
|
||||
void Compaction::PopulatePenultimateLevelOutputRange() {
|
||||
void Compaction::PopulateProximalLevelOutputRange() {
|
||||
if (!SupportsPerKeyPlacement()) {
|
||||
assert(keep_in_last_level_through_seqno_ == kMaxSequenceNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
// exclude the last level, the range of all input levels is the safe range
|
||||
// of keys that can be moved up.
|
||||
int exclude_level = number_levels_ - 1;
|
||||
penultimate_output_range_type_ = PenultimateOutputRangeType::kNonLastRange;
|
||||
proximal_output_range_type_ = ProximalOutputRangeType::kNonLastRange;
|
||||
|
||||
// For universal compaction, the penultimate_output_range could be extended if
|
||||
// all penultimate level files are included in the compaction (which includes
|
||||
// the case that the penultimate level is empty).
|
||||
// For universal compaction, the proximal_output_range could be extended if
|
||||
// all proximal level files are included in the compaction (which includes
|
||||
// the case that the proximal level is empty).
|
||||
if (immutable_options_.compaction_style == kCompactionStyleUniversal) {
|
||||
exclude_level = kInvalidLevel;
|
||||
penultimate_output_range_type_ = PenultimateOutputRangeType::kFullRange;
|
||||
std::set<uint64_t> penultimate_inputs;
|
||||
proximal_output_range_type_ = ProximalOutputRangeType::kFullRange;
|
||||
std::set<uint64_t> proximal_inputs;
|
||||
for (const auto& input_lvl : inputs_) {
|
||||
if (input_lvl.level == penultimate_level_) {
|
||||
if (input_lvl.level == proximal_level_) {
|
||||
for (const auto& file : input_lvl.files) {
|
||||
penultimate_inputs.emplace(file->fd.GetNumber());
|
||||
proximal_inputs.emplace(file->fd.GetNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
auto penultimate_files = input_vstorage_->LevelFiles(penultimate_level_);
|
||||
for (const auto& file : penultimate_files) {
|
||||
if (penultimate_inputs.find(file->fd.GetNumber()) ==
|
||||
penultimate_inputs.end()) {
|
||||
auto proximal_files = input_vstorage_->LevelFiles(proximal_level_);
|
||||
for (const auto& file : proximal_files) {
|
||||
if (proximal_inputs.find(file->fd.GetNumber()) == proximal_inputs.end()) {
|
||||
exclude_level = number_levels_ - 1;
|
||||
penultimate_output_range_type_ =
|
||||
PenultimateOutputRangeType::kNonLastRange;
|
||||
proximal_output_range_type_ = ProximalOutputRangeType::kNonLastRange;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: should make use of `penultimate_output_range_type_`.
|
||||
// FIXME: should make use of `proximal_output_range_type_`.
|
||||
// FIXME: when last level's input range does not overlap with
|
||||
// penultimate level, and penultimate level input is empty,
|
||||
// this call will not set penultimate_level_smallest_ or
|
||||
// penultimate_level_largest_. No keys will be compacted up.
|
||||
GetBoundaryInternalKeys(input_vstorage_, inputs_,
|
||||
&penultimate_level_smallest_,
|
||||
&penultimate_level_largest_, exclude_level);
|
||||
// proximal level, and proximal level input is empty,
|
||||
// this call will not set proximal_level_smallest_ or
|
||||
// proximal_level_largest_. No keys will be compacted up.
|
||||
GetBoundaryInternalKeys(input_vstorage_, inputs_, &proximal_level_smallest_,
|
||||
&proximal_level_largest_, exclude_level);
|
||||
|
||||
if (proximal_output_range_type_ != ProximalOutputRangeType::kFullRange) {
|
||||
// If not full range in proximal level, must keep everything already
|
||||
// in the last level there, because moving it back up might cause
|
||||
// overlap/placement issues that are difficult to resolve properly in the
|
||||
// presence of range deletes
|
||||
SequenceNumber max_last_level_seqno = 0;
|
||||
for (const auto& input_lvl : inputs_) {
|
||||
if (input_lvl.level == output_level_) {
|
||||
for (const auto& file : input_lvl.files) {
|
||||
max_last_level_seqno =
|
||||
std::max(max_last_level_seqno, file->fd.largest_seqno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keep_in_last_level_through_seqno_ = max_last_level_seqno;
|
||||
} else {
|
||||
keep_in_last_level_through_seqno_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Compaction::~Compaction() {
|
||||
@@ -464,23 +482,23 @@ Compaction::~Compaction() {
|
||||
}
|
||||
|
||||
bool Compaction::SupportsPerKeyPlacement() const {
|
||||
return penultimate_level_ != kInvalidLevel;
|
||||
return proximal_level_ != kInvalidLevel;
|
||||
}
|
||||
|
||||
int Compaction::GetPenultimateLevel() const { return penultimate_level_; }
|
||||
int Compaction::GetProximalLevel() const { return proximal_level_; }
|
||||
|
||||
// smallest_key and largest_key include timestamps if user-defined timestamp is
|
||||
// enabled.
|
||||
bool Compaction::OverlapPenultimateLevelOutputRange(
|
||||
bool Compaction::OverlapProximalLevelOutputRange(
|
||||
const Slice& smallest_key, const Slice& largest_key) const {
|
||||
if (!SupportsPerKeyPlacement()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// See FIXME in Compaction::PopulatePenultimateLevelOutputRange().
|
||||
// See FIXME in Compaction::PopulateProximalLevelOutputRange().
|
||||
// We do not compact any key up in this case.
|
||||
if (penultimate_level_smallest_.size() == 0 ||
|
||||
penultimate_level_largest_.size() == 0) {
|
||||
if (proximal_level_smallest_.size() == 0 ||
|
||||
proximal_level_largest_.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -488,28 +506,35 @@ bool Compaction::OverlapPenultimateLevelOutputRange(
|
||||
input_vstorage_->InternalComparator()->user_comparator();
|
||||
|
||||
return ucmp->CompareWithoutTimestamp(
|
||||
smallest_key, penultimate_level_largest_.user_key()) <= 0 &&
|
||||
smallest_key, proximal_level_largest_.user_key()) <= 0 &&
|
||||
ucmp->CompareWithoutTimestamp(
|
||||
largest_key, penultimate_level_smallest_.user_key()) >= 0;
|
||||
largest_key, proximal_level_smallest_.user_key()) >= 0;
|
||||
}
|
||||
|
||||
// key includes timestamp if user-defined timestamp is enabled.
|
||||
bool Compaction::WithinPenultimateLevelOutputRange(
|
||||
const ParsedInternalKey& ikey) const {
|
||||
if (!SupportsPerKeyPlacement()) {
|
||||
return false;
|
||||
}
|
||||
void Compaction::TEST_AssertWithinProximalLevelOutputRange(
|
||||
const Slice& user_key, bool expect_failure) const {
|
||||
#ifdef NDEBUG
|
||||
(void)user_key;
|
||||
(void)expect_failure;
|
||||
#else
|
||||
assert(SupportsPerKeyPlacement());
|
||||
|
||||
if (penultimate_level_smallest_.size() == 0 ||
|
||||
penultimate_level_largest_.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
assert(proximal_level_smallest_.size() > 0);
|
||||
assert(proximal_level_largest_.size() > 0);
|
||||
|
||||
const InternalKeyComparator* icmp = input_vstorage_->InternalComparator();
|
||||
auto* cmp = input_vstorage_->user_comparator();
|
||||
|
||||
// op_type of a key can change during compaction, e.g. Merge -> Put.
|
||||
return icmp->CompareKeySeq(ikey, penultimate_level_smallest_.Encode()) >= 0 &&
|
||||
icmp->CompareKeySeq(ikey, penultimate_level_largest_.Encode()) <= 0;
|
||||
if (!(cmp->Compare(user_key, proximal_level_smallest_.user_key()) >= 0)) {
|
||||
assert(expect_failure);
|
||||
} else if (!(cmp->Compare(user_key, proximal_level_largest_.user_key()) <=
|
||||
0)) {
|
||||
assert(expect_failure);
|
||||
} else {
|
||||
assert(!expect_failure);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Compaction::InputCompressionMatchesOutput() const {
|
||||
@@ -563,7 +588,7 @@ bool Compaction::IsTrivialMove() const {
|
||||
// input files are non overlapping
|
||||
if ((mutable_cf_options_.compaction_options_universal.allow_trivial_move) &&
|
||||
(output_level_ != 0) &&
|
||||
(cfd_->ioptions()->compaction_style == kCompactionStyleUniversal)) {
|
||||
(cfd_->ioptions().compaction_style == kCompactionStyleUniversal)) {
|
||||
return is_trivial_move_;
|
||||
}
|
||||
|
||||
@@ -621,7 +646,7 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(
|
||||
if (bottommost_level_) {
|
||||
return true;
|
||||
} else if (output_level_ != 0 &&
|
||||
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
cfd_->ioptions().compaction_style == kCompactionStyleLevel) {
|
||||
// Maybe use binary search to find right entry instead of linear search?
|
||||
const Comparator* user_cmp = cfd_->user_comparator();
|
||||
for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++) {
|
||||
@@ -662,7 +687,7 @@ bool Compaction::KeyRangeNotExistsBeyondOutputLevel(
|
||||
if (bottommost_level_) {
|
||||
return true /* does not overlap */;
|
||||
} else if (output_level_ != 0 &&
|
||||
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
cfd_->ioptions().compaction_style == kCompactionStyleLevel) {
|
||||
const Comparator* user_cmp = cfd_->user_comparator();
|
||||
for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++) {
|
||||
const std::vector<FileMetaData*>& files =
|
||||
@@ -838,12 +863,12 @@ uint64_t Compaction::OutputFilePreallocationSize() const {
|
||||
}
|
||||
|
||||
std::unique_ptr<CompactionFilter> Compaction::CreateCompactionFilter() const {
|
||||
if (!cfd_->ioptions()->compaction_filter_factory) {
|
||||
if (!cfd_->ioptions().compaction_filter_factory) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!cfd_->ioptions()
|
||||
->compaction_filter_factory->ShouldFilterTableFileCreation(
|
||||
.compaction_filter_factory->ShouldFilterTableFileCreation(
|
||||
TableFileCreationReason::kCompaction)) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -862,7 +887,7 @@ std::unique_ptr<CompactionFilter> Compaction::CreateCompactionFilter() const {
|
||||
"for compaction.");
|
||||
}
|
||||
|
||||
return cfd_->ioptions()->compaction_filter_factory->CreateCompactionFilter(
|
||||
return cfd_->ioptions().compaction_filter_factory->CreateCompactionFilter(
|
||||
context);
|
||||
}
|
||||
|
||||
@@ -896,8 +921,8 @@ bool Compaction::ShouldFormSubcompactions() const {
|
||||
|
||||
// Round-Robin pri under leveled compaction allows subcompactions by default
|
||||
// and the number of subcompactions can be larger than max_subcompactions_
|
||||
if (cfd_->ioptions()->compaction_pri == kRoundRobin &&
|
||||
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
if (cfd_->ioptions().compaction_pri == kRoundRobin &&
|
||||
cfd_->ioptions().compaction_style == kCompactionStyleLevel) {
|
||||
return output_level_ > 0;
|
||||
}
|
||||
|
||||
@@ -905,9 +930,9 @@ bool Compaction::ShouldFormSubcompactions() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
if (cfd_->ioptions().compaction_style == kCompactionStyleLevel) {
|
||||
return (start_level_ == 0 || is_manual_compaction_) && output_level_ > 0;
|
||||
} else if (cfd_->ioptions()->compaction_style == kCompactionStyleUniversal) {
|
||||
} else if (cfd_->ioptions().compaction_style == kCompactionStyleUniversal) {
|
||||
return number_levels_ > 1 && output_level_ > 0;
|
||||
} else {
|
||||
return false;
|
||||
@@ -989,7 +1014,7 @@ uint64_t Compaction::MinInputFileEpochNumber() const {
|
||||
return min_epoch_number;
|
||||
}
|
||||
|
||||
int Compaction::EvaluatePenultimateLevel(
|
||||
int Compaction::EvaluateProximalLevel(
|
||||
const VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const ImmutableOptions& immutable_options, const int start_level,
|
||||
@@ -1004,21 +1029,21 @@ int Compaction::EvaluatePenultimateLevel(
|
||||
return kInvalidLevel;
|
||||
}
|
||||
|
||||
int penultimate_level = output_level - 1;
|
||||
assert(penultimate_level < immutable_options.num_levels);
|
||||
if (penultimate_level <= 0) {
|
||||
int proximal_level = output_level - 1;
|
||||
assert(proximal_level < immutable_options.num_levels);
|
||||
if (proximal_level <= 0) {
|
||||
return kInvalidLevel;
|
||||
}
|
||||
|
||||
// If the penultimate level is not within input level -> output level range
|
||||
// check if the penultimate output level is empty, if it's empty, it could
|
||||
// also be locked for the penultimate output.
|
||||
// If the proximal level is not within input level -> output level range
|
||||
// check if the proximal output level is empty, if it's empty, it could
|
||||
// also be locked for the proximal output.
|
||||
// TODO: ideally, it only needs to check if there's a file within the
|
||||
// compaction output key range. For simplicity, it just check if there's any
|
||||
// file on the penultimate level.
|
||||
// file on the proximal level.
|
||||
if (start_level == immutable_options.num_levels - 1 &&
|
||||
(immutable_options.compaction_style != kCompactionStyleUniversal ||
|
||||
!vstorage->LevelFiles(penultimate_level).empty())) {
|
||||
!vstorage->LevelFiles(proximal_level).empty())) {
|
||||
return kInvalidLevel;
|
||||
}
|
||||
|
||||
@@ -1032,7 +1057,7 @@ int Compaction::EvaluatePenultimateLevel(
|
||||
return kInvalidLevel;
|
||||
}
|
||||
|
||||
return penultimate_level;
|
||||
return proximal_level;
|
||||
}
|
||||
|
||||
void Compaction::FilterInputsForCompactionIterator() {
|
||||
|
||||
+59
-51
@@ -102,13 +102,13 @@ class Compaction {
|
||||
BlobGarbageCollectionPolicy::kUseDefault,
|
||||
double blob_garbage_collection_age_cutoff = -1);
|
||||
|
||||
// The type of the penultimate level output range
|
||||
enum class PenultimateOutputRangeType : int {
|
||||
kNotSupported, // it cannot output to the penultimate level
|
||||
kFullRange, // any data could be output to the penultimate level
|
||||
// The type of the proximal level output range
|
||||
enum class ProximalOutputRangeType : int {
|
||||
kNotSupported, // it cannot output to the proximal level
|
||||
kFullRange, // any data could be output to the proximal level
|
||||
kNonLastRange, // only the keys within non_last_level compaction inputs can
|
||||
// be outputted to the penultimate level
|
||||
kDisabled, // no data can be outputted to the penultimate level
|
||||
// be outputted to the proximal level
|
||||
kDisabled, // no data can be outputted to the proximal level
|
||||
};
|
||||
|
||||
// No copying allowed
|
||||
@@ -288,14 +288,14 @@ class Compaction {
|
||||
|
||||
// Return the ImmutableOptions that should be used throughout the compaction
|
||||
// procedure
|
||||
const ImmutableOptions* immutable_options() const {
|
||||
return &immutable_options_;
|
||||
const ImmutableOptions& immutable_options() const {
|
||||
return immutable_options_;
|
||||
}
|
||||
|
||||
// Return the MutableCFOptions that should be used throughout the compaction
|
||||
// procedure
|
||||
const MutableCFOptions* mutable_cf_options() const {
|
||||
return &mutable_cf_options_;
|
||||
const MutableCFOptions& mutable_cf_options() const {
|
||||
return mutable_cf_options_;
|
||||
}
|
||||
|
||||
// Returns the size in bytes that the output file should be preallocated to.
|
||||
@@ -370,30 +370,30 @@ class Compaction {
|
||||
|
||||
Slice GetLargestUserKey() const { return largest_user_key_; }
|
||||
|
||||
PenultimateOutputRangeType GetPenultimateOutputRangeType() const {
|
||||
return penultimate_output_range_type_;
|
||||
ProximalOutputRangeType GetProximalOutputRangeType() const {
|
||||
return proximal_output_range_type_;
|
||||
}
|
||||
|
||||
// Return true if the compaction supports per_key_placement
|
||||
bool SupportsPerKeyPlacement() const;
|
||||
|
||||
// Get per_key_placement penultimate output level, which is `last_level - 1`
|
||||
// Get per_key_placement proximal output level, which is `last_level - 1`
|
||||
// if per_key_placement feature is supported. Otherwise, return -1.
|
||||
int GetPenultimateLevel() const;
|
||||
int GetProximalLevel() const;
|
||||
|
||||
// Return true if the given range is overlap with penultimate level output
|
||||
// Return true if the given range is overlap with proximal level output
|
||||
// range.
|
||||
// Both smallest_key and largest_key include timestamps if user-defined
|
||||
// timestamp is enabled.
|
||||
bool OverlapPenultimateLevelOutputRange(const Slice& smallest_key,
|
||||
const Slice& largest_key) const;
|
||||
bool OverlapProximalLevelOutputRange(const Slice& smallest_key,
|
||||
const Slice& largest_key) const;
|
||||
|
||||
// Return true if the key is within penultimate level output range for
|
||||
// per_key_placement feature, which is safe to place the key to the
|
||||
// penultimate level. different compaction strategy has different rules.
|
||||
// If per_key_placement is not supported, always return false.
|
||||
// key includes timestamp if user-defined timestamp is enabled.
|
||||
bool WithinPenultimateLevelOutputRange(const ParsedInternalKey& ikey) const;
|
||||
// For testing purposes, check that a key is within proximal level
|
||||
// output range for per_key_placement feature, which is safe to place the key
|
||||
// to the proximal level. Different compaction strategies have different
|
||||
// rules. `user_key` includes timestamp if user-defined timestamp is enabled.
|
||||
void TEST_AssertWithinProximalLevelOutputRange(
|
||||
const Slice& user_key, bool expect_failure = false) const;
|
||||
|
||||
CompactionReason compaction_reason() const { return compaction_reason_; }
|
||||
|
||||
@@ -441,26 +441,32 @@ class Compaction {
|
||||
|
||||
static constexpr int kInvalidLevel = -1;
|
||||
|
||||
// Evaluate penultimate output level. If the compaction supports
|
||||
// per_key_placement feature, it returns the penultimate level number.
|
||||
// Evaluate proximal output level. If the compaction supports
|
||||
// per_key_placement feature, it returns the proximal level number.
|
||||
// Otherwise, it's set to kInvalidLevel (-1), which means
|
||||
// output_to_penultimate_level is not supported.
|
||||
// Note: even the penultimate level output is supported (PenultimateLevel !=
|
||||
// output_to_proximal_level is not supported.
|
||||
// Note: even the proximal level output is supported (ProximalLevel !=
|
||||
// kInvalidLevel), some key range maybe unsafe to be outputted to the
|
||||
// penultimate level. The safe key range is populated by
|
||||
// `PopulatePenultimateLevelOutputRange()`.
|
||||
// Which could potentially disable all penultimate level output.
|
||||
static int EvaluatePenultimateLevel(
|
||||
const VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const ImmutableOptions& immutable_options, const int start_level,
|
||||
const int output_level);
|
||||
// proximal level. The safe key range is populated by
|
||||
// `PopulateProximalLevelOutputRange()`.
|
||||
// Which could potentially disable all proximal level output.
|
||||
static int EvaluateProximalLevel(const VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const ImmutableOptions& immutable_options,
|
||||
const int start_level,
|
||||
const int 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.
|
||||
SequenceNumber GetKeepInLastLevelThroughSeqno() const {
|
||||
return keep_in_last_level_through_seqno_;
|
||||
}
|
||||
|
||||
// mark (or clear) all files that are being compacted
|
||||
void MarkFilesBeingCompacted(bool being_compacted) const;
|
||||
|
||||
private:
|
||||
|
||||
Status InitInputTableProperties();
|
||||
|
||||
// get the smallest and largest key present in files to be compacted
|
||||
@@ -476,10 +482,10 @@ class Compaction {
|
||||
InternalKey* smallest_key, InternalKey* largest_key,
|
||||
int exclude_level = -1);
|
||||
|
||||
// populate penultimate level output range, which will be used to determine if
|
||||
// a key is safe to output to the penultimate level (details see
|
||||
// `Compaction::WithinPenultimateLevelOutputRange()`.
|
||||
void PopulatePenultimateLevelOutputRange();
|
||||
// populate proximal level output range, which will be used to determine if
|
||||
// a key is safe to output to the proximal level (details see
|
||||
// `Compaction::WithinProximalLevelOutputRange()`.
|
||||
void PopulateProximalLevelOutputRange();
|
||||
|
||||
// If oldest snapshot is specified at Compaction construction time, we have
|
||||
// an opportunity to optimize inputs for compaction iterator for this case:
|
||||
@@ -606,22 +612,24 @@ class Compaction {
|
||||
// Blob garbage collection age cutoff.
|
||||
double blob_garbage_collection_age_cutoff_;
|
||||
|
||||
SequenceNumber keep_in_last_level_through_seqno_ = kMaxSequenceNumber;
|
||||
|
||||
// only set when per_key_placement feature is enabled, -1 (kInvalidLevel)
|
||||
// means not supported.
|
||||
const int penultimate_level_;
|
||||
const int proximal_level_;
|
||||
|
||||
// Key range for penultimate level output
|
||||
// Key range for proximal level output
|
||||
// includes timestamp if user-defined timestamp is enabled.
|
||||
// penultimate_output_range_type_ shows the range type
|
||||
InternalKey penultimate_level_smallest_;
|
||||
InternalKey penultimate_level_largest_;
|
||||
PenultimateOutputRangeType penultimate_output_range_type_ =
|
||||
PenultimateOutputRangeType::kNotSupported;
|
||||
// proximal_output_range_type_ shows the range type
|
||||
InternalKey proximal_level_smallest_;
|
||||
InternalKey proximal_level_largest_;
|
||||
ProximalOutputRangeType proximal_output_range_type_ =
|
||||
ProximalOutputRangeType::kNotSupported;
|
||||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Helper struct only for tests, which contains the data to decide if a key
|
||||
// should be output to the penultimate level.
|
||||
// should be output to the proximal level.
|
||||
// TODO: remove this when the public feature knob is available
|
||||
struct PerKeyPlacementContext {
|
||||
const int level;
|
||||
@@ -629,16 +637,16 @@ struct PerKeyPlacementContext {
|
||||
const Slice value;
|
||||
const SequenceNumber seq_num;
|
||||
|
||||
bool& output_to_penultimate_level;
|
||||
bool& output_to_proximal_level;
|
||||
|
||||
PerKeyPlacementContext(int _level, Slice _key, Slice _value,
|
||||
SequenceNumber _seq_num,
|
||||
bool& _output_to_penultimate_level)
|
||||
bool& _output_to_proximal_level)
|
||||
: level(_level),
|
||||
key(_key),
|
||||
value(_value),
|
||||
seq_num(_seq_num),
|
||||
output_to_penultimate_level(_output_to_penultimate_level) {}
|
||||
output_to_proximal_level(_output_to_proximal_level) {}
|
||||
};
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
|
||||
@@ -38,19 +38,16 @@ CompactionIterator::CompactionIterator(
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const std::shared_ptr<Logger> info_log,
|
||||
const std::string* full_history_ts_low,
|
||||
const SequenceNumber preserve_time_min_seqno,
|
||||
const SequenceNumber preclude_last_level_min_seqno)
|
||||
std::optional<SequenceNumber> preserve_seqno_min)
|
||||
: CompactionIterator(
|
||||
input, cmp, merge_helper, last_sequence, snapshots, earliest_snapshot,
|
||||
earliest_write_conflict_snapshot, job_snapshot, snapshot_checker, env,
|
||||
report_detailed_time, expect_valid_internal_key, range_del_agg,
|
||||
blob_file_builder, allow_data_in_errors, enforce_single_del_contracts,
|
||||
manual_compaction_canceled,
|
||||
std::unique_ptr<CompactionProxy>(
|
||||
compaction ? new RealCompaction(compaction) : nullptr),
|
||||
compaction ? std::make_unique<RealCompaction>(compaction) : nullptr,
|
||||
must_count_input_entries, compaction_filter, shutting_down, info_log,
|
||||
full_history_ts_low, preserve_time_min_seqno,
|
||||
preclude_last_level_min_seqno) {}
|
||||
full_history_ts_low, preserve_seqno_min) {}
|
||||
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
@@ -68,8 +65,7 @@ CompactionIterator::CompactionIterator(
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const std::shared_ptr<Logger> info_log,
|
||||
const std::string* full_history_ts_low,
|
||||
const SequenceNumber preserve_time_min_seqno,
|
||||
const SequenceNumber preclude_last_level_min_seqno)
|
||||
std::optional<SequenceNumber> preserve_seqno_min)
|
||||
: input_(input, cmp, must_count_input_entries),
|
||||
cmp_(cmp),
|
||||
merge_helper_(merge_helper),
|
||||
@@ -110,10 +106,9 @@ CompactionIterator::CompactionIterator(
|
||||
current_key_committed_(false),
|
||||
cmp_with_history_ts_low_(0),
|
||||
level_(compaction_ == nullptr ? 0 : compaction_->level()),
|
||||
preserve_time_min_seqno_(preserve_time_min_seqno),
|
||||
preclude_last_level_min_seqno_(preclude_last_level_min_seqno) {
|
||||
preserve_seqno_after_(preserve_seqno_min.value_or(earliest_snapshot)) {
|
||||
assert(snapshots_ != nullptr);
|
||||
assert(preserve_time_min_seqno_ <= preclude_last_level_min_seqno_);
|
||||
assert(preserve_seqno_after_ <= earliest_snapshot_);
|
||||
|
||||
if (compaction_ != nullptr) {
|
||||
level_ptrs_ = std::vector<size_t>(compaction_->number_levels(), 0);
|
||||
@@ -1018,7 +1013,6 @@ void CompactionIterator::NextFromInput() {
|
||||
} else {
|
||||
if (ikey_.sequence != 0) {
|
||||
iter_stats_.num_timed_put_swap_preferred_seqno++;
|
||||
saved_seq_for_penul_check_ = ikey_.sequence;
|
||||
ikey_.sequence = preferred_seqno;
|
||||
}
|
||||
ikey_.type = kTypeValue;
|
||||
@@ -1259,71 +1253,6 @@ void CompactionIterator::GarbageCollectBlobIfNeeded() {
|
||||
}
|
||||
}
|
||||
|
||||
void CompactionIterator::DecideOutputLevel() {
|
||||
assert(compaction_->SupportsPerKeyPlacement());
|
||||
output_to_penultimate_level_ = false;
|
||||
// if the key is newer than the cutoff sequence or within the earliest
|
||||
// snapshot, it should output to the penultimate level.
|
||||
if (ikey_.sequence > preclude_last_level_min_seqno_ ||
|
||||
ikey_.sequence > earliest_snapshot_) {
|
||||
output_to_penultimate_level_ = true;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Could be overridden by unittest
|
||||
PerKeyPlacementContext context(level_, ikey_.user_key, value_, ikey_.sequence,
|
||||
output_to_penultimate_level_);
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionIterator::PrepareOutput.context",
|
||||
&context);
|
||||
if (ikey_.sequence > earliest_snapshot_) {
|
||||
output_to_penultimate_level_ = true;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
// saved_seq_for_penul_check_ is populated in `NextFromInput` when the
|
||||
// entry's sequence number is non zero and validity context for output this
|
||||
// entry is kSwapPreferredSeqno for use in `DecideOutputLevel`. It should be
|
||||
// cleared out here unconditionally. Otherwise, it may end up getting consumed
|
||||
// incorrectly by a different entry.
|
||||
SequenceNumber seq_for_range_check =
|
||||
(saved_seq_for_penul_check_.has_value() &&
|
||||
saved_seq_for_penul_check_.value() != kMaxSequenceNumber)
|
||||
? saved_seq_for_penul_check_.value()
|
||||
: ikey_.sequence;
|
||||
saved_seq_for_penul_check_ = std::nullopt;
|
||||
ParsedInternalKey ikey_for_range_check = ikey_;
|
||||
if (seq_for_range_check != ikey_.sequence) {
|
||||
ikey_for_range_check.sequence = seq_for_range_check;
|
||||
}
|
||||
if (output_to_penultimate_level_) {
|
||||
// If it's decided to output to the penultimate level, but unsafe to do so,
|
||||
// still output to the last level. For example, moving the data from a lower
|
||||
// level to a higher level outside of the higher-level input key range is
|
||||
// considered unsafe, because the key may conflict with higher-level SSTs
|
||||
// not from this compaction.
|
||||
// TODO: add statistic for declined output_to_penultimate_level
|
||||
bool safe_to_penultimate_level =
|
||||
compaction_->WithinPenultimateLevelOutputRange(ikey_for_range_check);
|
||||
if (!safe_to_penultimate_level) {
|
||||
output_to_penultimate_level_ = false;
|
||||
// It could happen when disable/enable `last_level_temperature` while
|
||||
// holding a snapshot. When `last_level_temperature` is not set
|
||||
// (==kUnknown), the data newer than any snapshot is pushed to the last
|
||||
// level, but when the per_key_placement feature is enabled on the fly,
|
||||
// the data later than the snapshot has to be moved to the penultimate
|
||||
// level, which may or may not be safe. So the user needs to make sure all
|
||||
// snapshot is released before enabling `last_level_temperature` feature
|
||||
// We will migrate the feature to `last_level_temperature` and maybe make
|
||||
// it not dynamically changeable.
|
||||
if (seq_for_range_check > earliest_snapshot_) {
|
||||
status_ = Status::Corruption(
|
||||
"Unsafe to store Seq later than snapshot in the last level if "
|
||||
"per_key_placement is enabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompactionIterator::PrepareOutput() {
|
||||
if (Valid()) {
|
||||
if (LIKELY(!is_range_del_)) {
|
||||
@@ -1332,13 +1261,6 @@ void CompactionIterator::PrepareOutput() {
|
||||
} else if (ikey_.type == kTypeBlobIndex) {
|
||||
GarbageCollectBlobIfNeeded();
|
||||
}
|
||||
|
||||
// For range del sentinel, we don't use it to cut files for bottommost
|
||||
// compaction. So it should not make a difference which output level we
|
||||
// decide.
|
||||
if (compaction_ != nullptr && compaction_->SupportsPerKeyPlacement()) {
|
||||
DecideOutputLevel();
|
||||
}
|
||||
}
|
||||
|
||||
// Zeroing out the sequence number leads to better compression.
|
||||
@@ -1356,8 +1278,7 @@ void CompactionIterator::PrepareOutput() {
|
||||
!compaction_->allow_ingest_behind() && bottommost_level_ &&
|
||||
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
|
||||
ikey_.type != kTypeMerge && current_key_committed_ &&
|
||||
!output_to_penultimate_level_ &&
|
||||
ikey_.sequence < preserve_time_min_seqno_ && !is_range_del_) {
|
||||
ikey_.sequence <= preserve_seqno_after_ && !is_range_del_) {
|
||||
if (ikey_.type == kTypeDeletion ||
|
||||
(ikey_.type == kTypeSingleDeletion && timestamp_size_ == 0)) {
|
||||
ROCKS_LOG_FATAL(
|
||||
|
||||
@@ -117,10 +117,6 @@ class CompactionIterator {
|
||||
virtual const Compaction* real_compaction() const = 0;
|
||||
|
||||
virtual bool SupportsPerKeyPlacement() const = 0;
|
||||
|
||||
// `key` includes timestamp if user-defined timestamp is enabled.
|
||||
virtual bool WithinPenultimateLevelOutputRange(
|
||||
const ParsedInternalKey&) const = 0;
|
||||
};
|
||||
|
||||
class RealCompaction : public CompactionProxy {
|
||||
@@ -128,8 +124,6 @@ class CompactionIterator {
|
||||
explicit RealCompaction(const Compaction* compaction)
|
||||
: compaction_(compaction) {
|
||||
assert(compaction_);
|
||||
assert(compaction_->immutable_options());
|
||||
assert(compaction_->mutable_cf_options());
|
||||
}
|
||||
|
||||
int level() const override { return compaction_->level(); }
|
||||
@@ -151,11 +145,11 @@ class CompactionIterator {
|
||||
}
|
||||
|
||||
bool allow_ingest_behind() const override {
|
||||
return compaction_->immutable_options()->allow_ingest_behind;
|
||||
return compaction_->immutable_options().allow_ingest_behind;
|
||||
}
|
||||
|
||||
bool allow_mmap_reads() const override {
|
||||
return compaction_->immutable_options()->allow_mmap_reads;
|
||||
return compaction_->immutable_options().allow_mmap_reads;
|
||||
}
|
||||
|
||||
bool enable_blob_garbage_collection() const override {
|
||||
@@ -167,7 +161,7 @@ class CompactionIterator {
|
||||
}
|
||||
|
||||
uint64_t blob_compaction_readahead_size() const override {
|
||||
return compaction_->mutable_cf_options()->blob_compaction_readahead_size;
|
||||
return compaction_->mutable_cf_options().blob_compaction_readahead_size;
|
||||
}
|
||||
|
||||
const Version* input_version() const override {
|
||||
@@ -184,14 +178,6 @@ class CompactionIterator {
|
||||
return compaction_->SupportsPerKeyPlacement();
|
||||
}
|
||||
|
||||
// Check if key is within penultimate level output range, to see if it's
|
||||
// safe to output to the penultimate level for per_key_placement feature.
|
||||
// `key` includes timestamp if user-defined timestamp is enabled.
|
||||
bool WithinPenultimateLevelOutputRange(
|
||||
const ParsedInternalKey& ikey) const override {
|
||||
return compaction_->WithinPenultimateLevelOutputRange(ikey);
|
||||
}
|
||||
|
||||
private:
|
||||
const Compaction* compaction_;
|
||||
};
|
||||
@@ -216,29 +202,29 @@ class CompactionIterator {
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
const std::string* full_history_ts_low = nullptr,
|
||||
const SequenceNumber preserve_time_min_seqno = kMaxSequenceNumber,
|
||||
const SequenceNumber preclude_last_level_min_seqno = kMaxSequenceNumber);
|
||||
std::optional<SequenceNumber> preserve_seqno_min = {});
|
||||
|
||||
// Constructor with custom CompactionProxy, used for tests.
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_snapshot,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
|
||||
bool enforce_single_del_contracts,
|
||||
const std::atomic<bool>& manual_compaction_canceled,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
bool must_count_input_entries,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
const std::string* full_history_ts_low = nullptr,
|
||||
const SequenceNumber preserve_time_min_seqno = kMaxSequenceNumber,
|
||||
const SequenceNumber preclude_last_level_min_seqno = kMaxSequenceNumber);
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
MergeHelper* merge_helper, SequenceNumber last_sequence,
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_snapshot,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
BlobFileBuilder* blob_file_builder,
|
||||
bool allow_data_in_errors,
|
||||
bool enforce_single_del_contracts,
|
||||
const std::atomic<bool>& manual_compaction_canceled,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
bool must_count_input_entries,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
const std::string* full_history_ts_low = nullptr,
|
||||
std::optional<SequenceNumber> preserve_seqno_min = {});
|
||||
|
||||
~CompactionIterator();
|
||||
|
||||
@@ -269,11 +255,6 @@ class CompactionIterator {
|
||||
const CompactionIterationStats& iter_stats() const { return iter_stats_; }
|
||||
bool HasNumInputEntryScanned() const { return input_.HasNumItered(); }
|
||||
uint64_t NumInputEntryScanned() const { return input_.NumItered(); }
|
||||
// If the current key should be placed on penultimate level, only valid if
|
||||
// per_key_placement is supported
|
||||
bool output_to_penultimate_level() const {
|
||||
return output_to_penultimate_level_;
|
||||
}
|
||||
Status InputStatus() const { return input_.status(); }
|
||||
|
||||
bool IsDeleteRangeSentinelKey() const { return is_range_del_; }
|
||||
@@ -285,10 +266,6 @@ class CompactionIterator {
|
||||
// Do final preparations before presenting the output to the callee.
|
||||
void PrepareOutput();
|
||||
|
||||
// Decide the current key should be output to the last level or penultimate
|
||||
// level, only call for compaction supports per key placement
|
||||
void DecideOutputLevel();
|
||||
|
||||
// Passes the output value to the blob file builder (if any), and replaces it
|
||||
// with the corresponding blob reference if it has been actually written to a
|
||||
// blob file (i.e. if it passed the value size check). Returns true if the
|
||||
@@ -440,16 +417,6 @@ class CompactionIterator {
|
||||
// NextFromInput()).
|
||||
ParsedInternalKey ikey_;
|
||||
|
||||
// When a kTypeValuePreferredSeqno entry's preferred seqno is safely swapped
|
||||
// in in this compaction, this field saves its original sequence number for
|
||||
// range checking whether it's safe to be placed on the penultimate level.
|
||||
// This is to ensure when such an entry happens to be the right boundary of
|
||||
// penultimate safe range, it won't get excluded because with the preferred
|
||||
// seqno swapped in, it's now larger than the right boundary (itself before
|
||||
// the swap). This is safe to do, because preferred seqno is swapped in only
|
||||
// when no entries with the same user key exist on lower levels and this entry
|
||||
// is already visible in the earliest snapshot.
|
||||
std::optional<SequenceNumber> saved_seq_for_penul_check_ = kMaxSequenceNumber;
|
||||
// Stores whether ikey_.user_key is valid. If set to false, the user key is
|
||||
// not compared against the current key in the underlying iterator.
|
||||
bool has_current_user_key_ = false;
|
||||
@@ -507,17 +474,8 @@ class CompactionIterator {
|
||||
// just been zeroed out during bottommost compaction.
|
||||
bool last_key_seq_zeroed_{false};
|
||||
|
||||
// True if the current key should be output to the penultimate level if
|
||||
// possible, compaction logic makes the final decision on which level to
|
||||
// output to.
|
||||
bool output_to_penultimate_level_{false};
|
||||
|
||||
// min seqno for preserving the time information.
|
||||
const SequenceNumber preserve_time_min_seqno_ = kMaxSequenceNumber;
|
||||
|
||||
// min seqno to preclude the data from the last level, if the key seqno larger
|
||||
// than this, it will be output to penultimate level
|
||||
const SequenceNumber preclude_last_level_min_seqno_ = kMaxSequenceNumber;
|
||||
// Max seqno that can be zeroed out at last level (various reasons)
|
||||
const SequenceNumber preserve_seqno_after_ = kMaxSequenceNumber;
|
||||
|
||||
void AdvanceInputIter() { input_.Next(); }
|
||||
|
||||
|
||||
@@ -192,11 +192,6 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
return supports_per_key_placement;
|
||||
}
|
||||
|
||||
bool WithinPenultimateLevelOutputRange(
|
||||
const ParsedInternalKey& key) const override {
|
||||
return (!key.user_key.starts_with("unsafe_pb"));
|
||||
}
|
||||
|
||||
bool key_not_exists_beyond_output_level = false;
|
||||
|
||||
bool is_bottommost_level = false;
|
||||
@@ -844,123 +839,6 @@ TEST_P(CompactionIteratorTest, ZeroSeqOfKeyAndSnapshot) {
|
||||
INSTANTIATE_TEST_CASE_P(CompactionIteratorTestInstance, CompactionIteratorTest,
|
||||
testing::Values(true, false));
|
||||
|
||||
class PerKeyPlacementCompIteratorTest : public CompactionIteratorTest {
|
||||
public:
|
||||
bool SupportsPerKeyPlacement() const override { return true; }
|
||||
};
|
||||
|
||||
TEST_P(PerKeyPlacementCompIteratorTest, SplitLastLevelData) {
|
||||
std::atomic_uint64_t latest_cold_seq = 0;
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
|
||||
auto context = static_cast<PerKeyPlacementContext*>(arg);
|
||||
context->output_to_penultimate_level =
|
||||
context->seq_num > latest_cold_seq;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
latest_cold_seq = 5;
|
||||
|
||||
InitIterators(
|
||||
{test::KeyStr("a", 7, kTypeValue), test::KeyStr("b", 6, kTypeValue),
|
||||
test::KeyStr("c", 5, kTypeValue)},
|
||||
{"vala", "valb", "valc"}, {}, {}, kMaxSequenceNumber, kMaxSequenceNumber,
|
||||
nullptr, nullptr, true);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
|
||||
// the first 2 keys are hot, which should has
|
||||
// `output_to_penultimate_level()==true` and seq num not zeroed out
|
||||
ASSERT_EQ(test::KeyStr("a", 7, kTypeValue), c_iter_->key().ToString());
|
||||
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("b", 6, kTypeValue), c_iter_->key().ToString());
|
||||
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
// `a` is cold data, which should be output to bottommost
|
||||
ASSERT_EQ(test::KeyStr("c", 0, kTypeValue), c_iter_->key().ToString());
|
||||
ASSERT_FALSE(c_iter_->output_to_penultimate_level());
|
||||
c_iter_->Next();
|
||||
ASSERT_OK(c_iter_->status());
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(PerKeyPlacementCompIteratorTest, SnapshotData) {
|
||||
AddSnapshot(5);
|
||||
|
||||
InitIterators(
|
||||
{test::KeyStr("a", 7, kTypeValue), test::KeyStr("b", 6, kTypeDeletion),
|
||||
test::KeyStr("b", 5, kTypeValue)},
|
||||
{"vala", "", "valb"}, {}, {}, kMaxSequenceNumber, kMaxSequenceNumber,
|
||||
nullptr, nullptr, true);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
|
||||
// The first key and the tombstone are within snapshot, which should output
|
||||
// to the penultimate level (and seq num cannot be zeroed out).
|
||||
ASSERT_EQ(test::KeyStr("a", 7, kTypeValue), c_iter_->key().ToString());
|
||||
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("b", 6, kTypeDeletion), c_iter_->key().ToString());
|
||||
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
// `a` is not protected by the snapshot, the sequence number is zero out and
|
||||
// should output bottommost
|
||||
ASSERT_EQ(test::KeyStr("b", 0, kTypeValue), c_iter_->key().ToString());
|
||||
ASSERT_FALSE(c_iter_->output_to_penultimate_level());
|
||||
c_iter_->Next();
|
||||
ASSERT_OK(c_iter_->status());
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_P(PerKeyPlacementCompIteratorTest, ConflictWithSnapshot) {
|
||||
std::atomic_uint64_t latest_cold_seq = 0;
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
|
||||
auto context = static_cast<PerKeyPlacementContext*>(arg);
|
||||
context->output_to_penultimate_level =
|
||||
context->seq_num > latest_cold_seq;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
latest_cold_seq = 6;
|
||||
|
||||
AddSnapshot(5);
|
||||
|
||||
InitIterators({test::KeyStr("a", 7, kTypeValue),
|
||||
test::KeyStr("unsafe_pb", 6, kTypeValue),
|
||||
test::KeyStr("c", 5, kTypeValue)},
|
||||
{"vala", "valb", "valc"}, {}, {}, kMaxSequenceNumber,
|
||||
kMaxSequenceNumber, nullptr, nullptr, true);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
|
||||
ASSERT_EQ(test::KeyStr("a", 7, kTypeValue), c_iter_->key().ToString());
|
||||
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
|
||||
// the 2nd key is unsafe to output_to_penultimate_level, but it's within
|
||||
// snapshot so for per_key_placement feature it has to be outputted to the
|
||||
// penultimate level. which is a corruption. We should never see
|
||||
// such case as the data with seq num (within snapshot) should always come
|
||||
// from higher compaction input level, which makes it safe to
|
||||
// output_to_penultimate_level.
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->status().IsCorruption());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(PerKeyPlacementCompIteratorTest,
|
||||
PerKeyPlacementCompIteratorTest,
|
||||
testing::Values(true, false));
|
||||
|
||||
// Tests how CompactionIterator work together with SnapshotChecker.
|
||||
class CompactionIteratorWithSnapshotCheckerTest
|
||||
: public CompactionIteratorTest {
|
||||
|
||||
+418
-229
File diff suppressed because it is too large.
Load diff
@@ -67,7 +67,7 @@ class SubcompactionState;
|
||||
// if needed.
|
||||
//
|
||||
// CompactionJob has 2 main stats:
|
||||
// 1. CompactionJobStats compaction_job_stats_
|
||||
// 1. CompactionJobStats job_stats_
|
||||
// CompactionJobStats is a public data structure which is part of Compaction
|
||||
// event listener that rocksdb share the job stats with the user.
|
||||
// Internally it's an aggregation of all the compaction_job_stats from each
|
||||
@@ -81,7 +81,7 @@ class SubcompactionState;
|
||||
// +------------------------+ |
|
||||
// | CompactionJob | | +------------------------+
|
||||
// | | | | SubcompactionState |
|
||||
// | compaction_job_stats +-----+ | |
|
||||
// | job_stats +-----+ | |
|
||||
// | | +--------->| compaction_job_stats |
|
||||
// | | | | |
|
||||
// +------------------------+ | +------------------------+
|
||||
@@ -98,16 +98,13 @@ class SubcompactionState;
|
||||
// +--------->+ |
|
||||
// +------------------------+
|
||||
//
|
||||
// 2. CompactionStatsFull compaction_stats_
|
||||
// 2. CompactionStatsFull internal_stats_
|
||||
// `CompactionStatsFull` is an internal stats about the compaction, which
|
||||
// is eventually sent to `ColumnFamilyData::internal_stats_` and used for
|
||||
// logging and public metrics.
|
||||
// Internally, it's an aggregation of stats_ from each `SubcompactionState`.
|
||||
// It has 2 parts, normal stats about the main compaction information and
|
||||
// the penultimate level output stats.
|
||||
// `SubcompactionState` maintains the CompactionOutputs for normal output and
|
||||
// the penultimate level output if exists, the per_level stats is
|
||||
// stored with the outputs.
|
||||
// It has 2 parts, ordinary output level stats and the proximal level output
|
||||
// stats.
|
||||
// +---------------------------+
|
||||
// | SubcompactionState |
|
||||
// | |
|
||||
@@ -119,15 +116,15 @@ class SubcompactionState;
|
||||
// | | |
|
||||
// | | +----------------------+ |
|
||||
// +--------------------------------+ | | | CompactionOutputs | |
|
||||
// | CompactionJob | | | | (penultimate_level) | |
|
||||
// | CompactionJob | | | | (proximal_level) | |
|
||||
// | | +--------->| stats_ | |
|
||||
// | compaction_stats_ | | | | +----------------------+ |
|
||||
// | internal_stats_ | | | | +----------------------+ |
|
||||
// | +-------------------------+ | | | | |
|
||||
// | |stats (normal) |------|----+ +---------------------------+
|
||||
// | |output_level_stats |------|----+ +---------------------------+
|
||||
// | +-------------------------+ | | |
|
||||
// | | | |
|
||||
// | +-------------------------+ | | | +---------------------------+
|
||||
// | |penultimate_level_stats +------+ | | SubcompactionState |
|
||||
// | |proximal_level_stats |------+ | | SubcompactionState |
|
||||
// | +-------------------------+ | | | | |
|
||||
// | | | | | +----------------------+ |
|
||||
// | | | | | | CompactionOutputs | |
|
||||
@@ -137,7 +134,7 @@ class SubcompactionState;
|
||||
// | | |
|
||||
// | | +----------------------+ |
|
||||
// | | | CompactionOutputs | |
|
||||
// | | | (penultimate_level) | |
|
||||
// | | | (proximal_level) | |
|
||||
// +--------->| stats_ | |
|
||||
// | +----------------------+ |
|
||||
// | |
|
||||
@@ -176,7 +173,13 @@ class CompactionJob {
|
||||
|
||||
// REQUIRED: mutex held
|
||||
// Prepare for the compaction by setting up boundaries for each subcompaction
|
||||
void Prepare();
|
||||
// and organizing seqno <-> time info. `known_single_subcompact` is non-null
|
||||
// if we already have a known single subcompaction, with optional key bounds
|
||||
// (currently for executing a remote compaction).
|
||||
void Prepare(
|
||||
std::optional<std::pair<std::optional<Slice>, std::optional<Slice>>>
|
||||
known_single_subcompact);
|
||||
|
||||
// REQUIRED mutex not held
|
||||
// Launch threads for each subcompaction and wait for them to finish. After
|
||||
// that, verify table is usable and finally do bookkeeping to unify
|
||||
@@ -187,30 +190,15 @@ class CompactionJob {
|
||||
// Add compaction input/output to the current version
|
||||
// Releases compaction file through Compaction::ReleaseCompactionFiles().
|
||||
// Sets *compaction_released to true if compaction is released.
|
||||
Status Install(const MutableCFOptions& mutable_cf_options,
|
||||
bool* compaction_released);
|
||||
Status Install(bool* compaction_released);
|
||||
|
||||
// Return the IO status
|
||||
IOStatus io_status() const { return io_status_; }
|
||||
|
||||
protected:
|
||||
// Update the following stats in compaction_stats_.stats
|
||||
// - num_input_files_in_non_output_levels
|
||||
// - num_input_files_in_output_level
|
||||
// - bytes_read_non_output_levels
|
||||
// - bytes_read_output_level
|
||||
// - num_input_records
|
||||
// - bytes_read_blob
|
||||
// - num_dropped_records
|
||||
//
|
||||
// @param num_input_range_del if non-null, will be set to the number of range
|
||||
// deletion entries in this compaction input.
|
||||
//
|
||||
// Returns true iff compaction_stats_.stats.num_input_records and
|
||||
// num_input_range_del are calculated successfully.
|
||||
bool UpdateCompactionStats(uint64_t* num_input_range_del = nullptr);
|
||||
virtual void UpdateCompactionJobStats(
|
||||
const InternalStats::CompactionStats& stats) const;
|
||||
void UpdateCompactionJobOutputStats(
|
||||
const InternalStats::CompactionStatsFull& internal_stats) const;
|
||||
|
||||
void LogCompaction();
|
||||
virtual void RecordCompactionIOStats();
|
||||
void CleanupCompaction();
|
||||
@@ -219,7 +207,7 @@ class CompactionJob {
|
||||
void ProcessKeyValueCompaction(SubcompactionState* sub_compact);
|
||||
|
||||
CompactionState* compact_;
|
||||
InternalStats::CompactionStatsFull compaction_stats_;
|
||||
InternalStats::CompactionStatsFull internal_stats_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
const MutableDBOptions mutable_db_options_copy_;
|
||||
LogBuffer* log_buffer_;
|
||||
@@ -232,11 +220,37 @@ class CompactionJob {
|
||||
|
||||
IOStatus io_status_;
|
||||
|
||||
CompactionJobStats* compaction_job_stats_;
|
||||
CompactionJobStats* job_stats_;
|
||||
|
||||
private:
|
||||
friend class CompactionJobTestBase;
|
||||
|
||||
// Collect the following stats from Input Table Properties
|
||||
// - num_input_files_in_non_output_levels
|
||||
// - num_input_files_in_output_level
|
||||
// - bytes_read_non_output_levels
|
||||
// - bytes_read_output_level
|
||||
// - num_input_records
|
||||
// - bytes_read_blob
|
||||
// - num_dropped_records
|
||||
// and set them in internal_stats_.output_level_stats
|
||||
//
|
||||
// @param num_input_range_del if non-null, will be set to the number of range
|
||||
// deletion entries in this compaction input.
|
||||
//
|
||||
// Returns true iff internal_stats_.output_level_stats.num_input_records and
|
||||
// num_input_range_del are calculated successfully.
|
||||
//
|
||||
// This should be called only once for compactions (not per subcompaction)
|
||||
bool BuildStatsFromInputTableProperties(
|
||||
uint64_t* num_input_range_del = nullptr);
|
||||
|
||||
void UpdateCompactionJobInputStats(
|
||||
const InternalStats::CompactionStatsFull& internal_stats,
|
||||
uint64_t num_input_range_del) const;
|
||||
|
||||
Status VerifyInputRecordCount(uint64_t num_input_range_del) const;
|
||||
|
||||
// Generates a histogram representing potential divisions of key ranges from
|
||||
// the input. It adds the starting and/or ending keys of certain input files
|
||||
// to the working set and then finds the approximate size of data in between
|
||||
@@ -276,8 +290,7 @@ class CompactionJob {
|
||||
const Slice& next_table_min_key,
|
||||
const Slice* comp_start_user_key,
|
||||
const Slice* comp_end_user_key);
|
||||
Status InstallCompactionResults(const MutableCFOptions& mutable_cf_options,
|
||||
bool* compaction_released);
|
||||
Status InstallCompactionResults(bool* compaction_released);
|
||||
Status OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
CompactionOutputs& outputs);
|
||||
|
||||
@@ -353,15 +366,18 @@ class CompactionJob {
|
||||
// it also collects the smallest_seqno -> oldest_ancester_time from the SST.
|
||||
SeqnoToTimeMapping seqno_to_time_mapping_;
|
||||
|
||||
// Minimal sequence number for preserving the time information. The time info
|
||||
// older than this sequence number won't be preserved after the compaction and
|
||||
// if it's bottommost compaction, the seq num will be zeroed out.
|
||||
SequenceNumber preserve_time_min_seqno_ = kMaxSequenceNumber;
|
||||
// Max seqno that can be zeroed out in last level, including for preserving
|
||||
// write times.
|
||||
SequenceNumber preserve_seqno_after_ = kMaxSequenceNumber;
|
||||
|
||||
// Minimal sequence number to preclude the data from the last level. If the
|
||||
// key has bigger (newer) sequence number than this, it will be precluded from
|
||||
// the last level (output to penultimate level).
|
||||
SequenceNumber preclude_last_level_min_seqno_ = kMaxSequenceNumber;
|
||||
// the last level (output to proximal level).
|
||||
SequenceNumber proximal_after_seqno_ = kMaxSequenceNumber;
|
||||
|
||||
// Options File Number used for Remote Compaction
|
||||
// Setting this requires DBMutex.
|
||||
uint64_t options_file_number_ = 0;
|
||||
|
||||
// Get table file name in where it's outputting to, which should also be in
|
||||
// `output_directory_`.
|
||||
@@ -424,6 +440,8 @@ struct CompactionServiceOutputFile {
|
||||
bool marked_for_compaction;
|
||||
UniqueId64x2 unique_id{};
|
||||
TableProperties table_properties;
|
||||
bool is_proximal_level_output;
|
||||
Temperature file_temperature;
|
||||
|
||||
CompactionServiceOutputFile() = default;
|
||||
CompactionServiceOutputFile(
|
||||
@@ -433,7 +451,8 @@ struct CompactionServiceOutputFile {
|
||||
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)
|
||||
const TableProperties& _table_properties, bool _is_proximal_level_output,
|
||||
Temperature _file_temperature)
|
||||
: file_name(name),
|
||||
smallest_seqno(smallest),
|
||||
largest_seqno(largest),
|
||||
@@ -447,7 +466,9 @@ struct CompactionServiceOutputFile {
|
||||
paranoid_hash(_paranoid_hash),
|
||||
marked_for_compaction(_marked_for_compaction),
|
||||
unique_id(std::move(_unique_id)),
|
||||
table_properties(_table_properties) {}
|
||||
table_properties(_table_properties),
|
||||
is_proximal_level_output(_is_proximal_level_output),
|
||||
file_temperature(_file_temperature) {}
|
||||
};
|
||||
|
||||
// CompactionServiceResult contains the compaction result from a different db
|
||||
@@ -463,8 +484,21 @@ struct CompactionServiceResult {
|
||||
|
||||
uint64_t bytes_read = 0;
|
||||
uint64_t bytes_written = 0;
|
||||
|
||||
// Job-level Compaction Stats.
|
||||
//
|
||||
// NOTE: Job level stats cannot be rebuilt from scratch by simply aggregating
|
||||
// per-level stats due to some fields populated directly during compaction
|
||||
// (e.g. RecordDroppedKeys()). This is why we need both job-level stats and
|
||||
// per-level in the serialized result. If rebuilding job-level stats from
|
||||
// per-level stats become possible in the future, consider deprecating this
|
||||
// field.
|
||||
CompactionJobStats stats;
|
||||
|
||||
// Per-level Compaction Stats for both output_level_stats and
|
||||
// proximal_level_stats
|
||||
InternalStats::CompactionStatsFull internal_stats;
|
||||
|
||||
// serialization interface to read and write the object
|
||||
static Status Read(const std::string& data_str, CompactionServiceResult* obj);
|
||||
Status Write(std::string* output);
|
||||
@@ -496,6 +530,10 @@ class CompactionServiceCompactionJob : private CompactionJob {
|
||||
const CompactionServiceInput& compaction_service_input,
|
||||
CompactionServiceResult* compaction_service_result);
|
||||
|
||||
// REQUIRED: mutex held
|
||||
// Like CompactionJob::Prepare()
|
||||
void Prepare();
|
||||
|
||||
// Run the compaction in current thread and return the result
|
||||
Status Run();
|
||||
|
||||
@@ -506,9 +544,6 @@ class CompactionServiceCompactionJob : private CompactionJob {
|
||||
protected:
|
||||
void RecordCompactionIOStats() override;
|
||||
|
||||
void UpdateCompactionJobStats(
|
||||
const InternalStats::CompactionStats& stats) const override;
|
||||
|
||||
private:
|
||||
// Get table file name in output_path
|
||||
std::string GetTableFileName(uint64_t file_number) override;
|
||||
|
||||
@@ -959,7 +959,6 @@ int main(int argc, char** argv) {
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) { return 0; }
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
|
||||
#include "db/compaction/compaction_job.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -233,6 +232,11 @@ 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 {
|
||||
@@ -302,7 +306,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
std::unique_ptr<TableBuilder> table_builder(
|
||||
cf_options_.table_factory->NewTableBuilder(
|
||||
TableBuilderOptions(
|
||||
*cfd_->ioptions(), mutable_cf_options_, read_options,
|
||||
cfd_->ioptions(), mutable_cf_options_, read_options,
|
||||
write_options, cfd_->internal_comparator(),
|
||||
cfd_->internal_tbl_prop_coll_factories(),
|
||||
CompressionType::kNoCompression, CompressionOptions(),
|
||||
@@ -398,8 +402,8 @@ class CompactionJobTestBase : public testing::Test {
|
||||
|
||||
mutex_.Lock();
|
||||
EXPECT_OK(versions_->LogAndApply(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), mutable_cf_options_,
|
||||
read_options_, write_options_, &edit, &mutex_, nullptr));
|
||||
versions_->GetColumnFamilySet()->GetDefault(), read_options_,
|
||||
write_options_, &edit, &mutex_, nullptr));
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
@@ -461,7 +465,7 @@ 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(), nullptr, FileOptions(),
|
||||
cfd_->internal_comparator(),
|
||||
0 /* block_protection_bytes_per_key */),
|
||||
std::move(freader), file_size, &table_reader, false);
|
||||
@@ -646,12 +650,12 @@ class CompactionJobTestBase : public testing::Test {
|
||||
}
|
||||
|
||||
Compaction compaction(
|
||||
cfd->current()->storage_info(), *cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions(), mutable_db_options_,
|
||||
cfd->current()->storage_info(), cfd->ioptions(),
|
||||
cfd->GetLatestMutableCFOptions(), mutable_db_options_,
|
||||
compaction_input_files, output_level,
|
||||
mutable_cf_options_.target_file_size_base,
|
||||
mutable_cf_options_.max_compaction_bytes, 0, kNoCompression,
|
||||
cfd->GetLatestMutableCFOptions()->compression_opts,
|
||||
cfd->GetLatestMutableCFOptions().compression_opts,
|
||||
Temperature::kUnknown, max_subcompactions, grandparents,
|
||||
/*earliest_snapshot*/ std::nullopt, /*snapshot_checker*/ nullptr, true);
|
||||
compaction.FinalizeInputInfo(cfd->current());
|
||||
@@ -678,15 +682,14 @@ class CompactionJobTestBase : public testing::Test {
|
||||
full_history_ts_low_);
|
||||
VerifyInitializationOfCompactionJobStats(compaction_job_stats_);
|
||||
|
||||
compaction_job.Prepare();
|
||||
compaction_job.Prepare(std::nullopt /*subcompact to be computed*/);
|
||||
mutex_.Unlock();
|
||||
Status s = compaction_job.Run();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_OK(compaction_job.io_status());
|
||||
mutex_.Lock();
|
||||
bool compaction_released = false;
|
||||
ASSERT_OK(compaction_job.Install(*cfd->GetLatestMutableCFOptions(),
|
||||
&compaction_released));
|
||||
ASSERT_OK(compaction_job.Install(&compaction_released));
|
||||
ASSERT_OK(compaction_job.io_status());
|
||||
mutex_.Unlock();
|
||||
log_buffer.FlushBufferToLog();
|
||||
@@ -1476,7 +1479,7 @@ TEST_F(CompactionJobTest, OldestBlobFileNumber) {
|
||||
/* expected_oldest_blob_file_numbers */ {19});
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, VerifyPenultimateLevelOutput) {
|
||||
TEST_F(CompactionJobTest, VerifyProximalLevelOutput) {
|
||||
cf_options_.last_level_temperature = Temperature::kCold;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"Compaction::SupportsPerKeyPlacement:Enabled", [&](void* arg) {
|
||||
@@ -1489,8 +1492,7 @@ TEST_F(CompactionJobTest, VerifyPenultimateLevelOutput) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
|
||||
auto context = static_cast<PerKeyPlacementContext*>(arg);
|
||||
context->output_to_penultimate_level =
|
||||
context->seq_num > latest_cold_seq;
|
||||
context->output_to_proximal_level = context->seq_num > latest_cold_seq;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -1536,14 +1538,11 @@ TEST_F(CompactionJobTest, VerifyPenultimateLevelOutput) {
|
||||
/*verify_func=*/[&](Compaction& comp) {
|
||||
for (char c = 'a'; c <= 'z'; c++) {
|
||||
if (c == 'a') {
|
||||
ParsedInternalKey pik("a", 0U, kTypeValue);
|
||||
ASSERT_FALSE(comp.WithinPenultimateLevelOutputRange(pik));
|
||||
comp.TEST_AssertWithinProximalLevelOutputRange(
|
||||
"a", true /*expect_failure*/);
|
||||
} else {
|
||||
std::string c_str{c};
|
||||
// WithinPenultimateLevelOutputRange checks internal key range.
|
||||
// 'z' is the last key, so set seqno properly.
|
||||
ParsedInternalKey pik(c_str, c == 'z' ? 12U : 0U, kTypeValue);
|
||||
ASSERT_TRUE(comp.WithinPenultimateLevelOutputRange(pik));
|
||||
comp.TEST_AssertWithinProximalLevelOutputRange(c_str);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1687,7 +1686,8 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
file_checksum /* file_checksum */,
|
||||
file_checksum_func_name /* file_checksum_func_name */,
|
||||
rnd64.Uniform(UINT64_MAX) /* paranoid_hash */,
|
||||
rnd.OneIn(2) /* marked_for_compaction */, id /* unique_id */, tp);
|
||||
rnd.OneIn(2) /* marked_for_compaction */, id /* unique_id */, tp,
|
||||
false /* is_proximal_level_output */, Temperature::kHot);
|
||||
}
|
||||
result.output_level = rnd.Uniform(10);
|
||||
result.output_path = rnd.RandomString(rnd.Uniform(kStrMaxLen));
|
||||
@@ -1741,6 +1741,8 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
ASSERT_EQ(deserialized_tmp.output_files[0].file_checksum, file_checksum);
|
||||
ASSERT_EQ(deserialized_tmp.output_files[0].file_checksum_func_name,
|
||||
file_checksum_func_name);
|
||||
ASSERT_EQ(deserialized_tmp.output_files[0].file_temperature,
|
||||
Temperature::kHot);
|
||||
}
|
||||
|
||||
// Test unknown field
|
||||
@@ -1900,8 +1902,8 @@ TEST_F(CompactionJobTest, CutToSkipGrandparentFile) {
|
||||
const std::vector<int> input_levels = {0, 1};
|
||||
auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
auto lvl1_files = cfd_->current()->storage_info()->LevelFiles(1);
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2});
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2});
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutToAlignGrandparentBoundary) {
|
||||
@@ -1975,8 +1977,8 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundary) {
|
||||
const std::vector<int> input_levels = {0, 1};
|
||||
auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
auto lvl1_files = cfd_->current()->storage_info()->LevelFiles(1);
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2});
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2});
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
@@ -2037,8 +2039,8 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
for (int i = 80; i <= 100; i++) {
|
||||
snapshots.emplace_back(i);
|
||||
}
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2}, snapshots);
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2}, snapshots);
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutForMaxCompactionBytesSameKey) {
|
||||
|
||||
@@ -54,7 +54,7 @@ Status CompactionOutputs::Finish(
|
||||
}
|
||||
current_output().finished = true;
|
||||
stats_.bytes_written += current_bytes;
|
||||
stats_.num_output_files = outputs_.size();
|
||||
stats_.num_output_files = static_cast<int>(outputs_.size());
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -320,7 +320,7 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
|
||||
// More details, check PR #1963
|
||||
const size_t num_skippable_boundaries_crossed =
|
||||
being_grandparent_gap_ ? 2 : 3;
|
||||
if (compaction_->immutable_options()->compaction_style ==
|
||||
if (compaction_->immutable_options().compaction_style ==
|
||||
kCompactionStyleLevel &&
|
||||
num_grandparent_boundaries_crossed >=
|
||||
num_skippable_boundaries_crossed &&
|
||||
@@ -341,7 +341,7 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
|
||||
// target file size. The test shows it can generate larger files than a
|
||||
// static threshold like 75% and has a similar write amplification
|
||||
// improvement.
|
||||
if (compaction_->immutable_options()->compaction_style ==
|
||||
if (compaction_->immutable_options().compaction_style ==
|
||||
kCompactionStyleLevel &&
|
||||
current_output_file_size_ >=
|
||||
((compaction_->target_output_file_size() + 99) / 100) *
|
||||
@@ -455,9 +455,11 @@ void SetMaxSeqAndTs(InternalKey& internal_key, const Slice& user_key,
|
||||
} // namespace
|
||||
|
||||
Status CompactionOutputs::AddRangeDels(
|
||||
CompactionRangeDelAggregator& range_del_agg,
|
||||
const Slice* comp_start_user_key, const Slice* comp_end_user_key,
|
||||
CompactionIterationStats& range_del_out_stats, bool bottommost_level,
|
||||
const InternalKeyComparator& icmp, SequenceNumber earliest_snapshot,
|
||||
std::pair<SequenceNumber, SequenceNumber> keep_seqno_range,
|
||||
const Slice& next_table_min_key, const std::string& full_history_ts_low) {
|
||||
// The following example does not happen since
|
||||
// CompactionOutput::ShouldStopBefore() always return false for the first
|
||||
@@ -470,7 +472,7 @@ Status CompactionOutputs::AddRangeDels(
|
||||
// Then meta.smallest will be set to comp_start_user_key@seqno
|
||||
// and meta.largest will be set to comp_start_user_key@kMaxSequenceNumber
|
||||
// which violates the assumption that meta.smallest should be <= meta.largest.
|
||||
assert(HasRangeDel());
|
||||
assert(!range_del_agg.IsEmpty());
|
||||
FileMetaData& meta = current_output().meta;
|
||||
const Comparator* ucmp = icmp.user_comparator();
|
||||
InternalKey lower_bound_buf, upper_bound_buf;
|
||||
@@ -579,13 +581,19 @@ Status CompactionOutputs::AddRangeDels(
|
||||
assert(comp_end_user_key == nullptr || upper_bound == nullptr ||
|
||||
ucmp->CompareWithoutTimestamp(ExtractUserKey(*upper_bound),
|
||||
*comp_end_user_key) <= 0);
|
||||
auto it = range_del_agg_->NewIterator(lower_bound, upper_bound);
|
||||
auto it = range_del_agg.NewIterator(lower_bound, upper_bound);
|
||||
Slice last_tombstone_start_user_key{};
|
||||
bool reached_lower_bound = false;
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
auto tombstone = it->Tombstone();
|
||||
auto kv = tombstone.Serialize();
|
||||
// Filter out by seqno for per-key placement
|
||||
if (tombstone.seq_ < keep_seqno_range.first ||
|
||||
tombstone.seq_ >= keep_seqno_range.second) {
|
||||
continue;
|
||||
}
|
||||
|
||||
InternalKey tombstone_end = tombstone.SerializeEndKey();
|
||||
// TODO: the underlying iterator should support clamping the bounds.
|
||||
// tombstone_end.Encode is of form user_key@kMaxSeqno
|
||||
@@ -744,11 +752,10 @@ Status CompactionOutputs::AddRangeDels(
|
||||
}
|
||||
|
||||
void CompactionOutputs::FillFilesToCutForTtl() {
|
||||
if (compaction_->immutable_options()->compaction_style !=
|
||||
if (compaction_->immutable_options().compaction_style !=
|
||||
kCompactionStyleLevel ||
|
||||
compaction_->immutable_options()->compaction_pri !=
|
||||
kMinOverlappingRatio ||
|
||||
compaction_->mutable_cf_options()->ttl == 0 ||
|
||||
compaction_->immutable_options().compaction_pri != kMinOverlappingRatio ||
|
||||
compaction_->mutable_cf_options().ttl == 0 ||
|
||||
compaction_->num_input_levels() < 2 || compaction_->bottommost_level()) {
|
||||
return;
|
||||
}
|
||||
@@ -756,20 +763,19 @@ void CompactionOutputs::FillFilesToCutForTtl() {
|
||||
// We define new file with the oldest ancestor time to be younger than 1/4
|
||||
// TTL, and an old one to be older than 1/2 TTL time.
|
||||
int64_t temp_current_time;
|
||||
auto get_time_status =
|
||||
compaction_->immutable_options()->clock->GetCurrentTime(
|
||||
&temp_current_time);
|
||||
auto get_time_status = compaction_->immutable_options().clock->GetCurrentTime(
|
||||
&temp_current_time);
|
||||
if (!get_time_status.ok()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto current_time = static_cast<uint64_t>(temp_current_time);
|
||||
if (current_time < compaction_->mutable_cf_options()->ttl) {
|
||||
if (current_time < compaction_->mutable_cf_options().ttl) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t old_age_thres =
|
||||
current_time - compaction_->mutable_cf_options()->ttl / 2;
|
||||
current_time - compaction_->mutable_cf_options().ttl / 2;
|
||||
const std::vector<FileMetaData*>& olevel =
|
||||
*(compaction_->inputs(compaction_->num_input_levels() - 1));
|
||||
for (FileMetaData* file : olevel) {
|
||||
@@ -779,15 +785,15 @@ void CompactionOutputs::FillFilesToCutForTtl() {
|
||||
// of small files.
|
||||
if (oldest_ancester_time < old_age_thres &&
|
||||
file->fd.GetFileSize() >
|
||||
compaction_->mutable_cf_options()->target_file_size_base / 2) {
|
||||
compaction_->mutable_cf_options().target_file_size_base / 2) {
|
||||
files_to_cut_for_ttl_.push_back(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CompactionOutputs::CompactionOutputs(const Compaction* compaction,
|
||||
const bool is_penultimate_level)
|
||||
: compaction_(compaction), is_penultimate_level_(is_penultimate_level) {
|
||||
const bool is_proximal_level)
|
||||
: compaction_(compaction), is_proximal_level_(is_proximal_level) {
|
||||
partitioner_ = compaction->output_level() == 0
|
||||
? nullptr
|
||||
: compaction->CreateSstPartitioner();
|
||||
|
||||
@@ -30,27 +30,32 @@ class CompactionOutputs {
|
||||
// compaction output file
|
||||
struct Output {
|
||||
Output(FileMetaData&& _meta, const InternalKeyComparator& _icmp,
|
||||
bool _enable_hash, bool _finished, uint64_t precalculated_hash)
|
||||
bool _enable_hash, bool _finished, uint64_t precalculated_hash,
|
||||
bool _is_proximal_level)
|
||||
: meta(std::move(_meta)),
|
||||
validator(_icmp, _enable_hash, precalculated_hash),
|
||||
finished(_finished) {}
|
||||
finished(_finished),
|
||||
is_proximal_level(_is_proximal_level) {}
|
||||
FileMetaData meta;
|
||||
OutputValidator validator;
|
||||
bool finished;
|
||||
bool is_proximal_level;
|
||||
std::shared_ptr<const TableProperties> table_properties;
|
||||
};
|
||||
|
||||
CompactionOutputs() = delete;
|
||||
|
||||
explicit CompactionOutputs(const Compaction* compaction,
|
||||
const bool is_penultimate_level);
|
||||
const bool is_proximal_level);
|
||||
|
||||
bool IsProximalLevel() const { return is_proximal_level_; }
|
||||
|
||||
// Add generated output to the list
|
||||
void AddOutput(FileMetaData&& meta, const InternalKeyComparator& icmp,
|
||||
bool enable_hash, bool finished = false,
|
||||
uint64_t precalculated_hash = 0) {
|
||||
outputs_.emplace_back(std::move(meta), icmp, enable_hash, finished,
|
||||
precalculated_hash);
|
||||
precalculated_hash, is_proximal_level_);
|
||||
}
|
||||
|
||||
// Set new table builder for the current output
|
||||
@@ -61,34 +66,29 @@ class CompactionOutputs {
|
||||
file_writer_.reset(writer);
|
||||
}
|
||||
|
||||
// TODO: Remove it when remote compaction support tiered compaction
|
||||
void AddBytesWritten(uint64_t bytes) { stats_.bytes_written += bytes; }
|
||||
void SetNumOutputRecords(uint64_t num) { stats_.num_output_records = num; }
|
||||
void SetNumOutputFiles(uint64_t num) { stats_.num_output_files = num; }
|
||||
|
||||
// TODO: Move the BlobDB builder into CompactionOutputs
|
||||
const std::vector<BlobFileAddition>& GetBlobFileAdditions() const {
|
||||
if (is_penultimate_level_) {
|
||||
if (is_proximal_level_) {
|
||||
assert(blob_file_additions_.empty());
|
||||
}
|
||||
return blob_file_additions_;
|
||||
}
|
||||
|
||||
std::vector<BlobFileAddition>* GetBlobFileAdditionsPtr() {
|
||||
assert(!is_penultimate_level_);
|
||||
assert(!is_proximal_level_);
|
||||
return &blob_file_additions_;
|
||||
}
|
||||
|
||||
bool HasBlobFileAdditions() const { return !blob_file_additions_.empty(); }
|
||||
|
||||
BlobGarbageMeter* CreateBlobGarbageMeter() {
|
||||
assert(!is_penultimate_level_);
|
||||
assert(!is_proximal_level_);
|
||||
blob_garbage_meter_ = std::make_unique<BlobGarbageMeter>();
|
||||
return blob_garbage_meter_.get();
|
||||
}
|
||||
|
||||
BlobGarbageMeter* GetBlobGarbageMeter() const {
|
||||
if (is_penultimate_level_) {
|
||||
if (is_proximal_level_) {
|
||||
// blobdb doesn't support per_key_placement yet
|
||||
assert(blob_garbage_meter_ == nullptr);
|
||||
return nullptr;
|
||||
@@ -97,8 +97,9 @@ class CompactionOutputs {
|
||||
}
|
||||
|
||||
void UpdateBlobStats() {
|
||||
assert(!is_penultimate_level_);
|
||||
stats_.num_output_files_blob = blob_file_additions_.size();
|
||||
assert(!is_proximal_level_);
|
||||
stats_.num_output_files_blob =
|
||||
static_cast<int>(blob_file_additions_.size());
|
||||
for (const auto& blob : blob_file_additions_) {
|
||||
stats_.bytes_written_blob += blob.GetTotalBlobBytes();
|
||||
}
|
||||
@@ -182,18 +183,13 @@ class CompactionOutputs {
|
||||
// @param next_table_min_key internal key lower bound for the next compaction
|
||||
// output.
|
||||
// @param full_history_ts_low used for range tombstone garbage collection.
|
||||
Status AddRangeDels(const Slice* comp_start_user_key,
|
||||
const Slice* comp_end_user_key,
|
||||
CompactionIterationStats& range_del_out_stats,
|
||||
bool bottommost_level, const InternalKeyComparator& icmp,
|
||||
SequenceNumber earliest_snapshot,
|
||||
const Slice& next_table_min_key,
|
||||
const std::string& full_history_ts_low);
|
||||
|
||||
// if the outputs have range delete, range delete is also data
|
||||
bool HasRangeDel() const {
|
||||
return range_del_agg_ && !range_del_agg_->IsEmpty();
|
||||
}
|
||||
Status AddRangeDels(
|
||||
CompactionRangeDelAggregator& range_del_agg,
|
||||
const Slice* comp_start_user_key, const Slice* comp_end_user_key,
|
||||
CompactionIterationStats& range_del_out_stats, bool bottommost_level,
|
||||
const InternalKeyComparator& icmp, SequenceNumber earliest_snapshot,
|
||||
std::pair<SequenceNumber, SequenceNumber> keep_seqno_range,
|
||||
const Slice& next_table_min_key, const std::string& full_history_ts_low);
|
||||
|
||||
private:
|
||||
friend class SubcompactionState;
|
||||
@@ -259,11 +255,15 @@ class CompactionOutputs {
|
||||
// Close the current output. `open_file_func` is needed for creating new file
|
||||
// for range-dels only output file.
|
||||
Status CloseOutput(const Status& curr_status,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
const CompactionFileOpenFunc& open_file_func,
|
||||
const CompactionFileCloseFunc& close_file_func) {
|
||||
Status status = curr_status;
|
||||
// handle subcompaction containing only range deletions
|
||||
if (status.ok() && !HasBuilder() && !HasOutput() && HasRangeDel()) {
|
||||
// Handle subcompaction containing only range deletions. They could
|
||||
// be dropped or sent to another output level, so this is only an
|
||||
// over-approximate check for whether opening is needed.
|
||||
if (status.ok() && !HasBuilder() && !HasOutput() && range_del_agg &&
|
||||
!range_del_agg->IsEmpty()) {
|
||||
status = open_file_func(*this);
|
||||
}
|
||||
if (HasBuilder()) {
|
||||
@@ -288,16 +288,6 @@ class CompactionOutputs {
|
||||
return outputs_.back();
|
||||
}
|
||||
|
||||
// Assign the range_del_agg to the target output level. There's only one
|
||||
// range-del-aggregator per compaction outputs, for
|
||||
// output_to_penultimate_level compaction it is only assigned to the
|
||||
// penultimate level.
|
||||
void AssignRangeDelAggregator(
|
||||
std::unique_ptr<CompactionRangeDelAggregator>&& range_del_agg) {
|
||||
assert(range_del_agg_ == nullptr);
|
||||
range_del_agg_ = std::move(range_del_agg);
|
||||
}
|
||||
|
||||
const Compaction* compaction_;
|
||||
|
||||
// current output builder and writer
|
||||
@@ -313,13 +303,12 @@ class CompactionOutputs {
|
||||
std::vector<BlobFileAddition> blob_file_additions_;
|
||||
std::unique_ptr<BlobGarbageMeter> blob_garbage_meter_;
|
||||
|
||||
// Basic compaction output stats for this level's outputs
|
||||
InternalStats::CompactionOutputsStats stats_;
|
||||
// Per level's output stat
|
||||
InternalStats::CompactionStats stats_;
|
||||
|
||||
// indicate if this CompactionOutputs obj for penultimate_level, should always
|
||||
// indicate if this CompactionOutputs obj for proximal_level, should always
|
||||
// be false if per_key_placement feature is not enabled.
|
||||
const bool is_penultimate_level_;
|
||||
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg_ = nullptr;
|
||||
const bool is_proximal_level_;
|
||||
|
||||
// partitioner information
|
||||
std::string last_key_for_partitioner_;
|
||||
@@ -373,7 +362,7 @@ class CompactionOutputs {
|
||||
std::vector<size_t> level_ptrs_;
|
||||
};
|
||||
|
||||
// helper struct to concatenate the last level and penultimate level outputs
|
||||
// helper struct to concatenate the last level and proximal level outputs
|
||||
// which could be replaced by std::ranges::join_view() in c++20
|
||||
struct OutputIterator {
|
||||
public:
|
||||
|
||||
@@ -272,8 +272,8 @@ bool CompactionPicker::RangeOverlapWithCompaction(
|
||||
return true;
|
||||
}
|
||||
if (c->SupportsPerKeyPlacement()) {
|
||||
if (c->OverlapPenultimateLevelOutputRange(smallest_user_key,
|
||||
largest_user_key)) {
|
||||
if (c->OverlapProximalLevelOutputRange(smallest_user_key,
|
||||
largest_user_key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -284,7 +284,7 @@ bool CompactionPicker::RangeOverlapWithCompaction(
|
||||
|
||||
bool CompactionPicker::FilesRangeOverlapWithCompaction(
|
||||
const std::vector<CompactionInputFiles>& inputs, int level,
|
||||
int penultimate_level) const {
|
||||
int proximal_level) const {
|
||||
bool is_empty = true;
|
||||
for (auto& in : inputs) {
|
||||
if (!in.empty()) {
|
||||
@@ -301,18 +301,18 @@ bool CompactionPicker::FilesRangeOverlapWithCompaction(
|
||||
// files cannot be overlapped in the order of L0 files.
|
||||
InternalKey smallest, largest;
|
||||
GetRange(inputs, &smallest, &largest, Compaction::kInvalidLevel);
|
||||
if (penultimate_level != Compaction::kInvalidLevel) {
|
||||
if (proximal_level != Compaction::kInvalidLevel) {
|
||||
if (ioptions_.compaction_style == kCompactionStyleUniversal) {
|
||||
if (RangeOverlapWithCompaction(smallest.user_key(), largest.user_key(),
|
||||
penultimate_level)) {
|
||||
proximal_level)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
InternalKey penultimate_smallest, penultimate_largest;
|
||||
GetRange(inputs, &penultimate_smallest, &penultimate_largest, level);
|
||||
if (RangeOverlapWithCompaction(penultimate_smallest.user_key(),
|
||||
penultimate_largest.user_key(),
|
||||
penultimate_level)) {
|
||||
InternalKey proximal_smallest, proximal_largest;
|
||||
GetRange(inputs, &proximal_smallest, &proximal_largest, level);
|
||||
if (RangeOverlapWithCompaction(proximal_smallest.user_key(),
|
||||
proximal_largest.user_key(),
|
||||
proximal_level)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -353,7 +353,7 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
}
|
||||
assert(output_level == 0 || !FilesRangeOverlapWithCompaction(
|
||||
input_files, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
Compaction::EvaluateProximalLevel(
|
||||
vstorage, mutable_cf_options, ioptions_,
|
||||
start_level, output_level)));
|
||||
#endif /* !NDEBUG */
|
||||
@@ -659,9 +659,9 @@ Compaction* CompactionPicker::CompactRange(
|
||||
// overlaping outputs in the same level.
|
||||
if (FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage, mutable_cf_options,
|
||||
ioptions_, start_level,
|
||||
output_level))) {
|
||||
Compaction::EvaluateProximalLevel(vstorage, mutable_cf_options,
|
||||
ioptions_, start_level,
|
||||
output_level))) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
*manual_conflict = true;
|
||||
@@ -721,14 +721,14 @@ Compaction* CompactionPicker::CompactRange(
|
||||
// two files overlap.
|
||||
if (input_level > 0) {
|
||||
const uint64_t limit = mutable_cf_options.max_compaction_bytes;
|
||||
uint64_t input_level_total = 0;
|
||||
int hint_index = -1;
|
||||
InternalKey* smallest = nullptr;
|
||||
assert(!inputs.empty());
|
||||
// Always include first file for progress.
|
||||
uint64_t input_level_total = inputs[0]->fd.GetFileSize();
|
||||
InternalKey* smallest = &(inputs[0]->smallest);
|
||||
InternalKey* largest = nullptr;
|
||||
for (size_t i = 0; i + 1 < inputs.size(); ++i) {
|
||||
if (!smallest) {
|
||||
smallest = &inputs[i]->smallest;
|
||||
}
|
||||
for (size_t i = 1; i < inputs.size(); ++i) {
|
||||
// Consider whether to include inputs[i]
|
||||
largest = &inputs[i]->largest;
|
||||
|
||||
uint64_t input_file_size = inputs[i]->fd.GetFileSize();
|
||||
@@ -744,13 +744,11 @@ Compaction* CompactionPicker::CompactRange(
|
||||
|
||||
input_level_total += input_file_size;
|
||||
|
||||
if (input_level_total + output_level_total >= limit) {
|
||||
if (input_level_total + output_level_total > limit) {
|
||||
// To ensure compaction size is <= limit, leave out inputs from
|
||||
// index i onwards.
|
||||
covering_the_whole_range = false;
|
||||
// still include the current file, so the compaction could be larger
|
||||
// than max_compaction_bytes, which is also to make sure the compaction
|
||||
// can make progress even `max_compaction_bytes` is small (e.g. smaller
|
||||
// than an SST file).
|
||||
inputs.files.resize(i + 1);
|
||||
inputs.files.resize(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -819,6 +817,9 @@ Compaction* CompactionPicker::CompactRange(
|
||||
output_level = vstorage->base_level();
|
||||
assert(output_level > 0);
|
||||
}
|
||||
for (int i = input_level + 1; i < output_level; i++) {
|
||||
assert(vstorage->NumLevelFiles(i) == 0);
|
||||
}
|
||||
output_level_inputs.level = output_level;
|
||||
if (input_level != output_level) {
|
||||
int parent_index = -1;
|
||||
@@ -847,9 +848,9 @@ Compaction* CompactionPicker::CompactRange(
|
||||
// overlaping outputs in the same level.
|
||||
if (FilesRangeOverlapWithCompaction(
|
||||
compaction_inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage, mutable_cf_options,
|
||||
ioptions_, input_level,
|
||||
output_level))) {
|
||||
Compaction::EvaluateProximalLevel(vstorage, mutable_cf_options,
|
||||
ioptions_, input_level,
|
||||
output_level))) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
*manual_conflict = true;
|
||||
@@ -1136,7 +1137,7 @@ Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
|
||||
if (output_level != 0 &&
|
||||
FilesRangeOverlapWithCompaction(
|
||||
*converted_input_files, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
Compaction::EvaluateProximalLevel(
|
||||
version->storage_info(), version->GetMutableCFOptions(),
|
||||
ioptions_, (*converted_input_files)[0].level, output_level))) {
|
||||
return Status::Aborted(
|
||||
@@ -1153,7 +1154,7 @@ void CompactionPicker::RegisterCompaction(Compaction* c) {
|
||||
assert(ioptions_.compaction_style != kCompactionStyleLevel ||
|
||||
c->output_level() == 0 ||
|
||||
!FilesRangeOverlapWithCompaction(*c->inputs(), c->output_level(),
|
||||
c->GetPenultimateLevel()));
|
||||
c->GetProximalLevel()));
|
||||
// CompactionReason::kExternalSstIngestion's start level is just a placeholder
|
||||
// number without actual meaning as file ingestion technically does not have
|
||||
// an input level like other compactions
|
||||
|
||||
@@ -73,6 +73,8 @@ class CompactionPicker {
|
||||
// compaction_end will be set to nullptr.
|
||||
// Client is responsible for compaction_end storage -- when called,
|
||||
// *compaction_end should point to valid InternalKey!
|
||||
// REQUIRES: If not compacting all levels (input_level == kCompactAllLevels),
|
||||
// then levels between input_level and output_level should be empty.
|
||||
virtual Compaction* CompactRange(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
@@ -136,6 +138,12 @@ class CompactionPicker {
|
||||
return !level0_compactions_in_progress_.empty();
|
||||
}
|
||||
|
||||
// Is any compaction in progress
|
||||
bool IsCompactionInProgress() const {
|
||||
return !(level0_compactions_in_progress_.empty() &&
|
||||
compactions_in_progress_.empty());
|
||||
}
|
||||
|
||||
// Return true if the passed key range overlap with a compaction output
|
||||
// that is currently running.
|
||||
bool RangeOverlapWithCompaction(const Slice& smallest_user_key,
|
||||
@@ -188,7 +196,7 @@ class CompactionPicker {
|
||||
// key range of a currently running compaction.
|
||||
bool FilesRangeOverlapWithCompaction(
|
||||
const std::vector<CompactionInputFiles>& inputs, int level,
|
||||
int penultimate_level) const;
|
||||
int proximal_level) const;
|
||||
|
||||
bool SetupOtherInputs(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
|
||||
@@ -414,9 +414,9 @@ void LevelCompactionBuilder::SetupOtherFilesWithRoundRobinExpansion() {
|
||||
&tmp_start_level_inputs) ||
|
||||
compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
{tmp_start_level_inputs}, output_level_,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level_,
|
||||
output_level_))) {
|
||||
Compaction::EvaluateProximalLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level_,
|
||||
output_level_))) {
|
||||
// Constraint 1a
|
||||
tmp_start_level_inputs.clear();
|
||||
return;
|
||||
@@ -490,9 +490,9 @@ bool LevelCompactionBuilder::SetupOtherInputsIfNeeded() {
|
||||
// We need to disallow this from happening.
|
||||
if (compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
compaction_inputs_, output_level_,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level_,
|
||||
output_level_))) {
|
||||
Compaction::EvaluateProximalLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level_,
|
||||
output_level_))) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
return false;
|
||||
@@ -846,9 +846,9 @@ bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
&start_level_inputs_) ||
|
||||
compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
{start_level_inputs_}, output_level_,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level_,
|
||||
output_level_))) {
|
||||
Compaction::EvaluateProximalLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level_,
|
||||
output_level_))) {
|
||||
// A locked (pending compaction) input-level file was pulled in due to
|
||||
// user-key overlap.
|
||||
start_level_inputs_.clear();
|
||||
|
||||
@@ -2656,6 +2656,37 @@ TEST_F(CompactionPickerTest, HitCompactionLimitWhenAddFileFromInputLevel) {
|
||||
ASSERT_EQ(5U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactRangeMaxCompactionBytes) {
|
||||
mutable_cf_options_.max_compaction_bytes = 800000U;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// We will first pick file 1 and 2 and then stop before file 3.
|
||||
// Since picking file 3 will pull in file 4 and 5 from L2 and
|
||||
// exceed max_compaction_bytes.
|
||||
Add(1, 1U, "100", "110", 10000U);
|
||||
Add(1, 2U, "200", "210", 10000U, 0, 0);
|
||||
Add(1, 3U, "400", "410", 10000U, 0, 0);
|
||||
Add(2, 4U, "300", "310", 400000U);
|
||||
Add(2, 5U, "320", "330", 400000U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
bool manual_conflict = false;
|
||||
InternalKey manual_end;
|
||||
InternalKey* manual_end_ptr = &manual_end;
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.CompactRange(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
/*input_level=*/1, /*output_level=*/2,
|
||||
/*compact_range_options*/ {}, /*begin=*/nullptr, /*end=*/nullptr,
|
||||
&manual_end_ptr, &manual_conflict,
|
||||
/*max_file_num_to_ignore=*/std::numeric_limits<uint64_t>::max(),
|
||||
/*trim_ts=*/""));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_levels());
|
||||
ASSERT_EQ(2, compaction->output_level());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, IsTrivialMoveOn) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 10001u;
|
||||
@@ -3646,7 +3677,7 @@ TEST_F(CompactionPickerTest, UniversalSizeRatioTierCompactionLastLevel) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
const int kNumLevels = 7;
|
||||
const int kLastLevel = kNumLevels - 1;
|
||||
const int kPenultimateLevel = kLastLevel - 1;
|
||||
const int kProximalLevel = kLastLevel - 1;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
mutable_cf_options_.preclude_last_level_data_seconds = 1000;
|
||||
@@ -3671,14 +3702,14 @@ TEST_F(CompactionPickerTest, UniversalSizeRatioTierCompactionLastLevel) {
|
||||
// Here to make sure it's size ratio compaction instead of size amp
|
||||
ASSERT_EQ(compaction->compaction_reason(),
|
||||
CompactionReason::kUniversalSizeRatio);
|
||||
ASSERT_EQ(compaction->output_level(), kPenultimateLevel - 1);
|
||||
ASSERT_EQ(compaction->output_level(), kProximalLevel - 1);
|
||||
ASSERT_EQ(compaction->input_levels(0)->num_files, 2);
|
||||
ASSERT_EQ(compaction->input_levels(5)->num_files, 0);
|
||||
ASSERT_EQ(compaction->input_levels(6)->num_files, 0);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalSizeAmpTierCompactionNotSuport) {
|
||||
// Tiered compaction only support level_num > 2 (otherwise the penultimate
|
||||
// Tiered compaction only support level_num > 2 (otherwise the proximal
|
||||
// level is going to be level 0, which may make thing more complicated), so
|
||||
// when there's only 2 level, still treating level 1 as the last level for
|
||||
// size amp compaction
|
||||
@@ -3722,7 +3753,7 @@ TEST_F(CompactionPickerTest, UniversalSizeAmpTierCompactionLastLevel) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
const int kNumLevels = 7;
|
||||
const int kLastLevel = kNumLevels - 1;
|
||||
const int kPenultimateLevel = kLastLevel - 1;
|
||||
const int kProximalLevel = kLastLevel - 1;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
mutable_cf_options_.preclude_last_level_data_seconds = 1000;
|
||||
@@ -3744,10 +3775,10 @@ TEST_F(CompactionPickerTest, UniversalSizeAmpTierCompactionLastLevel) {
|
||||
vstorage_.get(), &log_buffer_));
|
||||
|
||||
// It's a Size Amp compaction, but doesn't include the last level file and
|
||||
// output to the penultimate level.
|
||||
// output to the proximal level.
|
||||
ASSERT_EQ(compaction->compaction_reason(),
|
||||
CompactionReason::kUniversalSizeAmplification);
|
||||
ASSERT_EQ(compaction->output_level(), kPenultimateLevel);
|
||||
ASSERT_EQ(compaction->output_level(), kProximalLevel);
|
||||
ASSERT_EQ(compaction->input_levels(0)->num_files, 2);
|
||||
ASSERT_EQ(compaction->input_levels(5)->num_files, 1);
|
||||
ASSERT_EQ(compaction->input_levels(6)->num_files, 0);
|
||||
@@ -3909,7 +3940,7 @@ TEST_P(PerKeyPlacementCompactionPickerTest, OverlapWithNormalCompaction) {
|
||||
ASSERT_EQ(enable_per_key_placement_,
|
||||
level_compaction_picker.FilesRangeOverlapWithCompaction(
|
||||
input_files, 6,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
Compaction::EvaluateProximalLevel(
|
||||
vstorage_.get(), mutable_cf_options_, ioptions_, 0, 6)));
|
||||
}
|
||||
|
||||
@@ -3997,7 +4028,7 @@ TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
ASSERT_EQ(enable_per_key_placement_,
|
||||
universal_compaction_picker.FilesRangeOverlapWithCompaction(
|
||||
input_files, 6,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
Compaction::EvaluateProximalLevel(
|
||||
vstorage_.get(), mutable_cf_options_, ioptions_, 0, 6)));
|
||||
}
|
||||
|
||||
@@ -4045,9 +4076,9 @@ TEST_P(PerKeyPlacementCompactionPickerTest, NormalCompactionOverlapUniversal) {
|
||||
input_files, 5, Compaction::kInvalidLevel));
|
||||
}
|
||||
|
||||
TEST_P(PerKeyPlacementCompactionPickerTest, PenultimateOverlapUniversal) {
|
||||
TEST_P(PerKeyPlacementCompactionPickerTest, ProximalOverlapUniversal) {
|
||||
// This test is make sure the Tiered compaction would lock whole range of
|
||||
// both output level and penultimate level
|
||||
// both output level and proximal level
|
||||
if (enable_per_key_placement_) {
|
||||
mutable_cf_options_.preclude_last_level_data_seconds = 10000;
|
||||
}
|
||||
@@ -4067,7 +4098,7 @@ TEST_P(PerKeyPlacementCompactionPickerTest, PenultimateOverlapUniversal) {
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
// the existing compaction is the 1st L4 file + L6 file
|
||||
// then compaction of the 2nd L4 file to L5 (penultimate level) is overlapped
|
||||
// then compaction of the 2nd L4 file to L5 (proximal level) is overlapped
|
||||
// when the tiered compaction feature is on.
|
||||
CompactionOptions comp_options;
|
||||
std::unordered_set<uint64_t> input_set;
|
||||
@@ -4156,9 +4187,9 @@ TEST_P(PerKeyPlacementCompactionPickerTest, LastLevelOnlyOverlapUniversal) {
|
||||
}
|
||||
|
||||
TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
LastLevelOnlyFailPenultimateUniversal) {
|
||||
LastLevelOnlyFailProximalUniversal) {
|
||||
// This is to test last_level only compaction still unable to do the
|
||||
// penultimate level compaction if there's already a file in the penultimate
|
||||
// proximal level compaction if there's already a file in the proximal
|
||||
// level.
|
||||
// This should rarely happen in universal compaction, as the non-empty L5
|
||||
// should be included in the compaction.
|
||||
@@ -4191,9 +4222,9 @@ TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
mutable_db_options_, 0));
|
||||
|
||||
ASSERT_TRUE(comp1);
|
||||
ASSERT_EQ(comp1->GetPenultimateLevel(), Compaction::kInvalidLevel);
|
||||
ASSERT_EQ(comp1->GetProximalLevel(), Compaction::kInvalidLevel);
|
||||
|
||||
// As comp1 cannot be output to the penultimate level, compacting file 40 to
|
||||
// As comp1 cannot be output to the proximal level, compacting file 40 to
|
||||
// L5 is always safe.
|
||||
input_set.clear();
|
||||
input_files.clear();
|
||||
@@ -4208,14 +4239,14 @@ TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
comp_options, input_files, 5, vstorage_.get(), mutable_cf_options_,
|
||||
mutable_db_options_, 0));
|
||||
ASSERT_TRUE(comp2);
|
||||
ASSERT_EQ(Compaction::kInvalidLevel, comp2->GetPenultimateLevel());
|
||||
ASSERT_EQ(Compaction::kInvalidLevel, comp2->GetProximalLevel());
|
||||
}
|
||||
|
||||
TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
LastLevelOnlyConflictWithOngoingUniversal) {
|
||||
// This is to test last_level only compaction still unable to do the
|
||||
// penultimate level compaction if there's already an ongoing compaction to
|
||||
// the penultimate level
|
||||
// proximal level compaction if there's already an ongoing compaction to
|
||||
// the proximal level
|
||||
if (enable_per_key_placement_) {
|
||||
mutable_cf_options_.preclude_last_level_data_seconds = 10000;
|
||||
}
|
||||
@@ -4234,7 +4265,7 @@ TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
Add(6, 60U, "101", "351", 60000000U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
// create an ongoing compaction to L5 (penultimate level)
|
||||
// create an ongoing compaction to L5 (proximal level)
|
||||
CompactionOptions comp_options;
|
||||
std::unordered_set<uint64_t> input_set;
|
||||
input_set.insert(40);
|
||||
@@ -4247,7 +4278,7 @@ TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
mutable_db_options_, 0));
|
||||
|
||||
ASSERT_TRUE(comp1);
|
||||
ASSERT_EQ(comp1->GetPenultimateLevel(), Compaction::kInvalidLevel);
|
||||
ASSERT_EQ(comp1->GetProximalLevel(), Compaction::kInvalidLevel);
|
||||
|
||||
input_set.clear();
|
||||
input_files.clear();
|
||||
@@ -4258,7 +4289,7 @@ TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
ASSERT_EQ(enable_per_key_placement_,
|
||||
universal_compaction_picker.FilesRangeOverlapWithCompaction(
|
||||
input_files, 6,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
Compaction::EvaluateProximalLevel(
|
||||
vstorage_.get(), mutable_cf_options_, ioptions_, 6, 6)));
|
||||
|
||||
if (!enable_per_key_placement_) {
|
||||
@@ -4266,7 +4297,7 @@ TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
comp_options, input_files, 6, vstorage_.get(), mutable_cf_options_,
|
||||
mutable_db_options_, 0));
|
||||
ASSERT_TRUE(comp2);
|
||||
ASSERT_EQ(Compaction::kInvalidLevel, comp2->GetPenultimateLevel());
|
||||
ASSERT_EQ(Compaction::kInvalidLevel, comp2->GetProximalLevel());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4275,7 +4306,7 @@ TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
// This is similar to `LastLevelOnlyConflictWithOngoingUniversal`, the only
|
||||
// change is the ongoing compaction to L5 has no overlap with the last level
|
||||
// compaction, so it's safe to move data from the last level to the
|
||||
// penultimate level.
|
||||
// proximal level.
|
||||
if (enable_per_key_placement_) {
|
||||
mutable_cf_options_.preclude_last_level_data_seconds = 10000;
|
||||
}
|
||||
@@ -4294,7 +4325,7 @@ TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
Add(6, 60U, "101", "351", 60000000U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
// create an ongoing compaction to L5 (penultimate level)
|
||||
// create an ongoing compaction to L5 (proximal level)
|
||||
CompactionOptions comp_options;
|
||||
std::unordered_set<uint64_t> input_set;
|
||||
input_set.insert(42);
|
||||
@@ -4307,7 +4338,7 @@ TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
mutable_db_options_, 0));
|
||||
|
||||
ASSERT_TRUE(comp1);
|
||||
ASSERT_EQ(comp1->GetPenultimateLevel(), Compaction::kInvalidLevel);
|
||||
ASSERT_EQ(comp1->GetProximalLevel(), Compaction::kInvalidLevel);
|
||||
|
||||
input_set.clear();
|
||||
input_files.clear();
|
||||
@@ -4318,8 +4349,8 @@ TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
// always safe to move data up
|
||||
ASSERT_FALSE(universal_compaction_picker.FilesRangeOverlapWithCompaction(
|
||||
input_files, 6,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage_.get(), mutable_cf_options_,
|
||||
ioptions_, 6, 6)));
|
||||
Compaction::EvaluateProximalLevel(vstorage_.get(), mutable_cf_options_,
|
||||
ioptions_, 6, 6)));
|
||||
|
||||
// 2 compactions can be run in parallel
|
||||
std::unique_ptr<Compaction> comp2(universal_compaction_picker.CompactFiles(
|
||||
@@ -4327,9 +4358,9 @@ TEST_P(PerKeyPlacementCompactionPickerTest,
|
||||
mutable_db_options_, 0));
|
||||
ASSERT_TRUE(comp2);
|
||||
if (enable_per_key_placement_) {
|
||||
ASSERT_NE(Compaction::kInvalidLevel, comp2->GetPenultimateLevel());
|
||||
ASSERT_NE(Compaction::kInvalidLevel, comp2->GetProximalLevel());
|
||||
} else {
|
||||
ASSERT_EQ(Compaction::kInvalidLevel, comp2->GetPenultimateLevel());
|
||||
ASSERT_EQ(Compaction::kInvalidLevel, comp2->GetProximalLevel());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -288,7 +288,9 @@ class UniversalCompactionBuilder {
|
||||
// and the index of the file in that level
|
||||
|
||||
struct InputFileInfo {
|
||||
InputFileInfo() : f(nullptr), level(0), index(0) {}
|
||||
InputFileInfo() : InputFileInfo(nullptr, 0, 0) {}
|
||||
InputFileInfo(FileMetaData* file_meta, size_t l, size_t i)
|
||||
: f(file_meta), level(l), index(i) {}
|
||||
|
||||
FileMetaData* f;
|
||||
size_t level;
|
||||
@@ -321,22 +323,14 @@ SmallestKeyHeap create_level_heap(Compaction* c, const Comparator* ucmp) {
|
||||
SmallestKeyHeap smallest_key_priority_q =
|
||||
SmallestKeyHeap(SmallestKeyHeapComparator(ucmp));
|
||||
|
||||
InputFileInfo input_file;
|
||||
|
||||
for (size_t l = 0; l < c->num_input_levels(); l++) {
|
||||
if (c->num_input_files(l) != 0) {
|
||||
if (l == 0 && c->start_level() == 0) {
|
||||
for (size_t i = 0; i < c->num_input_files(0); i++) {
|
||||
input_file.f = c->input(0, i);
|
||||
input_file.level = 0;
|
||||
input_file.index = i;
|
||||
smallest_key_priority_q.push(std::move(input_file));
|
||||
smallest_key_priority_q.emplace(c->input(0, i), 0, i);
|
||||
}
|
||||
} else {
|
||||
input_file.f = c->input(l, 0);
|
||||
input_file.level = l;
|
||||
input_file.index = 0;
|
||||
smallest_key_priority_q.push(std::move(input_file));
|
||||
smallest_key_priority_q.emplace(c->input(l, 0), l, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -374,7 +368,7 @@ bool UniversalCompactionBuilder::IsInputFilesNonOverlapping(Compaction* c) {
|
||||
auto comparator = icmp_->user_comparator();
|
||||
int first_iter = 1;
|
||||
|
||||
InputFileInfo prev, curr, next;
|
||||
InputFileInfo prev, curr;
|
||||
|
||||
SmallestKeyHeap smallest_key_priority_q =
|
||||
create_level_heap(c, icmp_->user_comparator());
|
||||
@@ -397,17 +391,10 @@ bool UniversalCompactionBuilder::IsInputFilesNonOverlapping(Compaction* c) {
|
||||
prev = curr;
|
||||
}
|
||||
|
||||
next.f = nullptr;
|
||||
|
||||
if (c->level(curr.level) != 0 &&
|
||||
curr.index < c->num_input_files(curr.level) - 1) {
|
||||
next.f = c->input(curr.level, curr.index + 1);
|
||||
next.level = curr.level;
|
||||
next.index = curr.index + 1;
|
||||
}
|
||||
|
||||
if (next.f) {
|
||||
smallest_key_priority_q.push(std::move(next));
|
||||
smallest_key_priority_q.emplace(c->input(curr.level, curr.index + 1),
|
||||
curr.level, curr.index + 1);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -996,7 +983,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
|
||||
if (output_level != 0 && picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
Compaction::EvaluateProximalLevel(
|
||||
vstorage_, mutable_cf_options_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
return nullptr;
|
||||
@@ -1345,7 +1332,7 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
|
||||
// intra L0 compactions outputs could have overlap
|
||||
if (output_level != 0 && picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
Compaction::EvaluateProximalLevel(
|
||||
vstorage_, mutable_cf_options_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
return nullptr;
|
||||
@@ -1486,9 +1473,9 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
}
|
||||
if (picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
vstorage_, mutable_cf_options_, ioptions_, start_level,
|
||||
output_level))) {
|
||||
Compaction::EvaluateProximalLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level,
|
||||
output_level))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1590,7 +1577,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
|
||||
// intra L0 compactions outputs could have overlap
|
||||
if (output_level != 0 && picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
Compaction::EvaluateProximalLevel(
|
||||
vstorage_, mutable_cf_options_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
return nullptr;
|
||||
|
||||
@@ -48,10 +48,7 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
compaction_input.has_end = sub_compact->end.has_value();
|
||||
compaction_input.end =
|
||||
compaction_input.has_end ? sub_compact->end->ToString() : "";
|
||||
compaction_input.options_file_number =
|
||||
sub_compact->compaction->input_version()
|
||||
->version_set()
|
||||
->options_file_number();
|
||||
compaction_input.options_file_number = options_file_number_;
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionServiceJob::ProcessKeyValueCompactionWithCompactionService",
|
||||
@@ -86,6 +83,14 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
switch (response.status) {
|
||||
case CompactionServiceJobStatus::kSuccess:
|
||||
break;
|
||||
case CompactionServiceJobStatus::kAborted:
|
||||
sub_compact->status =
|
||||
Status::Aborted("Scheduling a remote compaction job was aborted");
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction was aborted at Schedule()",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_);
|
||||
return response.status;
|
||||
case CompactionServiceJobStatus::kFailure:
|
||||
sub_compact->status = Status::Incomplete(
|
||||
"CompactionService failed to schedule a remote compaction job.");
|
||||
@@ -105,6 +110,9 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
break;
|
||||
}
|
||||
|
||||
std::string debug_str_before_wait =
|
||||
compaction->input_version()->DebugString();
|
||||
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Waiting for remote compaction...",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_);
|
||||
@@ -113,6 +121,16 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
db_options_.compaction_service->Wait(response.scheduled_job_id,
|
||||
&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());
|
||||
}
|
||||
|
||||
if (compaction_status == CompactionServiceJobStatus::kUseLocal) {
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
@@ -121,6 +139,16 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
return compaction_status;
|
||||
}
|
||||
|
||||
if (compaction_status == CompactionServiceJobStatus::kAborted) {
|
||||
sub_compact->status =
|
||||
Status::Aborted("Waiting a remote compaction job was aborted");
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction was aborted during Wait()",
|
||||
compaction->column_family_data()->GetName().c_str(),
|
||||
job_id_);
|
||||
return compaction_status;
|
||||
}
|
||||
|
||||
CompactionServiceResult compaction_result;
|
||||
s = CompactionServiceResult::Read(compaction_result_binary,
|
||||
&compaction_result);
|
||||
@@ -177,7 +205,7 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
for (const auto& file : compaction_result.output_files) {
|
||||
uint64_t file_num = versions_->NewFileNumber();
|
||||
auto src_file = compaction_result.output_path + "/" + file.file_name;
|
||||
auto tgt_file = TableFileName(compaction->immutable_options()->cf_paths,
|
||||
auto tgt_file = TableFileName(compaction->immutable_options().cf_paths,
|
||||
file_num, compaction->output_path_id());
|
||||
s = fs_->RenameFile(src_file, tgt_file, IOOptions(), nullptr);
|
||||
if (!s.ok()) {
|
||||
@@ -189,6 +217,10 @@ 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);
|
||||
if (!s.ok()) {
|
||||
sub_compact->status = s;
|
||||
@@ -207,19 +239,34 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
meta.file_checksum_func_name = file.file_checksum_func_name;
|
||||
meta.marked_for_compaction = file.marked_for_compaction;
|
||||
meta.unique_id = file.unique_id;
|
||||
meta.temperature = file.file_temperature;
|
||||
|
||||
auto cfd = compaction->column_family_data();
|
||||
sub_compact->Current().AddOutput(std::move(meta),
|
||||
cfd->internal_comparator(), false, true,
|
||||
file.paranoid_hash);
|
||||
sub_compact->Current().UpdateTableProperties(file.table_properties);
|
||||
CompactionOutputs* compaction_outputs =
|
||||
sub_compact->Outputs(file.is_proximal_level_output);
|
||||
assert(compaction_outputs);
|
||||
compaction_outputs->AddOutput(std::move(meta), cfd->internal_comparator(),
|
||||
false, true, file.paranoid_hash);
|
||||
compaction_outputs->UpdateTableProperties(file.table_properties);
|
||||
}
|
||||
|
||||
// Set per-level stats
|
||||
auto compaction_output_stats =
|
||||
sub_compact->OutputStats(false /* is_proximal_level */);
|
||||
assert(compaction_output_stats);
|
||||
compaction_output_stats->Add(
|
||||
compaction_result.internal_stats.output_level_stats);
|
||||
if (compaction->SupportsPerKeyPlacement()) {
|
||||
compaction_output_stats =
|
||||
sub_compact->OutputStats(true /* is_proximal_level */);
|
||||
assert(compaction_output_stats);
|
||||
compaction_output_stats->Add(
|
||||
compaction_result.internal_stats.proximal_level_stats);
|
||||
}
|
||||
|
||||
// Set job stats
|
||||
sub_compact->compaction_job_stats = compaction_result.stats;
|
||||
sub_compact->Current().SetNumOutputRecords(
|
||||
compaction_result.stats.num_output_records);
|
||||
sub_compact->Current().SetNumOutputFiles(
|
||||
compaction_result.stats.num_output_files);
|
||||
sub_compact->Current().AddBytesWritten(compaction_result.bytes_written);
|
||||
|
||||
RecordTick(stats_, REMOTE_COMPACT_READ_BYTES, compaction_result.bytes_read);
|
||||
RecordTick(stats_, REMOTE_COMPACT_WRITE_BYTES,
|
||||
compaction_result.bytes_written);
|
||||
@@ -239,18 +286,6 @@ void CompactionServiceCompactionJob::RecordCompactionIOStats() {
|
||||
CompactionJob::RecordCompactionIOStats();
|
||||
}
|
||||
|
||||
void CompactionServiceCompactionJob::UpdateCompactionJobStats(
|
||||
const InternalStats::CompactionStats& stats) const {
|
||||
compaction_job_stats_->elapsed_micros = stats.micros;
|
||||
|
||||
// output information only in remote compaction
|
||||
compaction_job_stats_->total_output_bytes = stats.bytes_written;
|
||||
compaction_job_stats_->total_output_bytes_blob = stats.bytes_written_blob;
|
||||
compaction_job_stats_->num_output_records = stats.num_output_records;
|
||||
compaction_job_stats_->num_output_files = stats.num_output_files;
|
||||
compaction_job_stats_->num_output_files_blob = stats.num_output_files_blob;
|
||||
}
|
||||
|
||||
CompactionServiceCompactionJob::CompactionServiceCompactionJob(
|
||||
int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
|
||||
const MutableDBOptions& mutable_db_options, const FileOptions& file_options,
|
||||
@@ -265,43 +300,38 @@ CompactionServiceCompactionJob::CompactionServiceCompactionJob(
|
||||
std::string output_path,
|
||||
const CompactionServiceInput& compaction_service_input,
|
||||
CompactionServiceResult* compaction_service_result)
|
||||
: CompactionJob(
|
||||
job_id, compaction, db_options, mutable_db_options, file_options,
|
||||
versions, shutting_down, 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,
|
||||
compaction->mutable_cf_options()->paranoid_file_checks,
|
||||
compaction->mutable_cf_options()->report_bg_io_stats, dbname,
|
||||
&(compaction_service_result->stats), Env::Priority::USER, io_tracer,
|
||||
manual_compaction_canceled, db_id, db_session_id,
|
||||
compaction->column_family_data()->GetFullHistoryTsLow()),
|
||||
: 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,
|
||||
compaction->mutable_cf_options().paranoid_file_checks,
|
||||
compaction->mutable_cf_options().report_bg_io_stats, dbname,
|
||||
&(compaction_service_result->stats), Env::Priority::USER,
|
||||
io_tracer, manual_compaction_canceled, db_id, db_session_id,
|
||||
compaction->column_family_data()->GetFullHistoryTsLow()),
|
||||
output_path_(std::move(output_path)),
|
||||
compaction_input_(compaction_service_input),
|
||||
compaction_result_(compaction_service_result) {}
|
||||
|
||||
void CompactionServiceCompactionJob::Prepare() {
|
||||
std::optional<Slice> begin;
|
||||
if (compaction_input_.has_begin) {
|
||||
begin = compaction_input_.begin;
|
||||
}
|
||||
std::optional<Slice> end;
|
||||
if (compaction_input_.has_end) {
|
||||
end = compaction_input_.end;
|
||||
}
|
||||
CompactionJob::Prepare(std::make_pair(begin, end));
|
||||
}
|
||||
|
||||
Status CompactionServiceCompactionJob::Run() {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_RUN);
|
||||
|
||||
auto* c = compact_->compaction;
|
||||
assert(c->column_family_data() != nullptr);
|
||||
const VersionStorageInfo* storage_info = c->input_version()->storage_info();
|
||||
assert(storage_info);
|
||||
assert(storage_info->NumLevelFiles(compact_->compaction->level()) > 0);
|
||||
write_hint_ = storage_info->CalculateSSTWriteHint(c->output_level());
|
||||
|
||||
bottommost_level_ = c->bottommost_level();
|
||||
|
||||
Slice begin = compaction_input_.begin;
|
||||
Slice end = compaction_input_.end;
|
||||
compact_->sub_compact_states.emplace_back(
|
||||
c,
|
||||
compaction_input_.has_begin ? std::optional<Slice>(begin)
|
||||
: std::optional<Slice>(),
|
||||
compaction_input_.has_end ? std::optional<Slice>(end)
|
||||
: std::optional<Slice>(),
|
||||
/*sub_job_id*/ 0);
|
||||
|
||||
log_buffer_->FlushBufferToLog();
|
||||
LogCompaction();
|
||||
@@ -317,15 +347,14 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
|
||||
ProcessKeyValueCompaction(sub_compact);
|
||||
|
||||
compaction_stats_.stats.micros =
|
||||
db_options_.clock->NowMicros() - start_micros;
|
||||
compaction_stats_.stats.cpu_micros =
|
||||
sub_compact->compaction_job_stats.cpu_micros;
|
||||
uint64_t elapsed_micros = db_options_.clock->NowMicros() - start_micros;
|
||||
internal_stats_.SetMicros(elapsed_micros);
|
||||
internal_stats_.AddCpuMicros(elapsed_micros);
|
||||
|
||||
RecordTimeToHistogram(stats_, COMPACTION_TIME,
|
||||
compaction_stats_.stats.micros);
|
||||
internal_stats_.output_level_stats.micros);
|
||||
RecordTimeToHistogram(stats_, COMPACTION_CPU_TIME,
|
||||
compaction_stats_.stats.cpu_micros);
|
||||
internal_stats_.output_level_stats.cpu_micros);
|
||||
|
||||
Status status = sub_compact->status;
|
||||
IOStatus io_s = sub_compact->io_status;
|
||||
@@ -355,25 +384,31 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
|
||||
// Build Compaction Job Stats
|
||||
|
||||
// 1. Aggregate CompactionOutputStats into Internal Compaction Stats
|
||||
// (compaction_stats_) and aggregate Compaction Job Stats
|
||||
// (compaction_job_stats_) from the sub compactions
|
||||
compact_->AggregateCompactionStats(compaction_stats_, *compaction_job_stats_);
|
||||
// 1. Aggregate internal stats and job stats for all subcompactions
|
||||
// internal stats: sub_compact.proximal_level_outputs_.stats and
|
||||
// sub_compact.compaction_outputs_.stats into
|
||||
// internal_stats_.output_level_stats and
|
||||
// internal_stats_.proximal_level_stats
|
||||
// job-level stats: sub_compact.compaction_job_stats into compact.job_stats_
|
||||
//
|
||||
// For remote compaction, there's only one subcompaction.
|
||||
compact_->AggregateCompactionStats(internal_stats_, *job_stats_);
|
||||
|
||||
// 2. Update the Output information in the Compaction Job Stats with
|
||||
// aggregated Internal Compaction Stats.
|
||||
UpdateCompactionJobStats(compaction_stats_.stats);
|
||||
|
||||
// 3. Set fields that are not propagated as part of aggregations above
|
||||
// 2. Update job-level output stats with the aggregated internal_stats_
|
||||
// Please note that input stats will be updated by primary host when all
|
||||
// subcompactions are finished
|
||||
UpdateCompactionJobOutputStats(internal_stats_);
|
||||
// and set fields that are not propagated as part of the update
|
||||
compaction_result_->stats.is_manual_compaction = c->is_manual_compaction();
|
||||
compaction_result_->stats.is_full_compaction = c->is_full_compaction();
|
||||
compaction_result_->stats.is_remote_compaction = true;
|
||||
|
||||
// 4. Update IO Stats that are not part of the aggregations above (bytes_read,
|
||||
// bytes_written)
|
||||
// 3. Update IO Stats that are not part of the the update above
|
||||
// (bytes_read, bytes_written)
|
||||
RecordCompactionIOStats();
|
||||
|
||||
// Build Output
|
||||
compaction_result_->internal_stats = internal_stats_;
|
||||
compaction_result_->output_level = compact_->compaction->output_level();
|
||||
compaction_result_->output_path = output_path_;
|
||||
if (status.ok()) {
|
||||
@@ -386,7 +421,8 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
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.table_properties, output_file.is_proximal_level,
|
||||
meta.temperature);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,7 +594,16 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
const auto this_one = static_cast<const TableProperties*>(addr1);
|
||||
const auto that_one = static_cast<const TableProperties*>(addr2);
|
||||
return this_one->AreEqual(opts, that_one, mismatch);
|
||||
}}}};
|
||||
}}},
|
||||
{"is_proximal_level_output",
|
||||
{offsetof(struct CompactionServiceOutputFile,
|
||||
is_proximal_level_output),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"file_temperature",
|
||||
{offsetof(struct CompactionServiceOutputFile, file_temperature),
|
||||
OptionType::kTemperature, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}}};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
compaction_job_stats_type_info = {
|
||||
@@ -683,6 +728,125 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionTypeFlags::kNone}},
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
compaction_stats_type_info = {
|
||||
{"micros",
|
||||
{offsetof(struct InternalStats::CompactionStats, micros),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"cpu_micros",
|
||||
{offsetof(struct InternalStats::CompactionStats, cpu_micros),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"bytes_read_non_output_levels",
|
||||
{offsetof(struct InternalStats::CompactionStats,
|
||||
bytes_read_non_output_levels),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"bytes_read_output_level",
|
||||
{offsetof(struct InternalStats::CompactionStats,
|
||||
bytes_read_output_level),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"bytes_skipped_non_output_levels",
|
||||
{offsetof(struct InternalStats::CompactionStats,
|
||||
bytes_skipped_non_output_levels),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"bytes_skipped_output_level",
|
||||
{offsetof(struct InternalStats::CompactionStats,
|
||||
bytes_skipped_output_level),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"bytes_read_blob",
|
||||
{offsetof(struct InternalStats::CompactionStats, bytes_read_blob),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"bytes_written",
|
||||
{offsetof(struct InternalStats::CompactionStats, bytes_written),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"bytes_written_blob",
|
||||
{offsetof(struct InternalStats::CompactionStats, bytes_written_blob),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"bytes_moved",
|
||||
{offsetof(struct InternalStats::CompactionStats, bytes_moved),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"num_input_files_in_non_output_levels",
|
||||
{offsetof(struct InternalStats::CompactionStats,
|
||||
num_input_files_in_non_output_levels),
|
||||
OptionType::kInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"num_input_files_in_output_level",
|
||||
{offsetof(struct InternalStats::CompactionStats,
|
||||
num_input_files_in_output_level),
|
||||
OptionType::kInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"num_filtered_input_files_in_non_output_levels",
|
||||
{offsetof(struct InternalStats::CompactionStats,
|
||||
num_filtered_input_files_in_non_output_levels),
|
||||
OptionType::kInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"num_filtered_input_files_in_output_level",
|
||||
{offsetof(struct InternalStats::CompactionStats,
|
||||
num_filtered_input_files_in_output_level),
|
||||
OptionType::kInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"num_output_files",
|
||||
{offsetof(struct InternalStats::CompactionStats, num_output_files),
|
||||
OptionType::kInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"num_output_files_blob",
|
||||
{offsetof(struct InternalStats::CompactionStats,
|
||||
num_output_files_blob),
|
||||
OptionType::kInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"num_input_records",
|
||||
{offsetof(struct InternalStats::CompactionStats, num_input_records),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"num_dropped_records",
|
||||
{offsetof(struct InternalStats::CompactionStats, num_dropped_records),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"num_output_records",
|
||||
{offsetof(struct InternalStats::CompactionStats, num_output_records),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"count",
|
||||
{offsetof(struct InternalStats::CompactionStats, count),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"counts", OptionTypeInfo::Array<
|
||||
int, static_cast<int>(CompactionReason::kNumOfReasons)>(
|
||||
offsetof(struct InternalStats::CompactionStats, counts),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
|
||||
{0, OptionType::kInt})},
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
compaction_internal_stats_type_info = {
|
||||
{"output_level_stats",
|
||||
OptionTypeInfo::Struct(
|
||||
"output_level_stats", &compaction_stats_type_info,
|
||||
offsetof(struct InternalStats::CompactionStatsFull,
|
||||
output_level_stats),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
|
||||
{"has_proximal_level_output",
|
||||
{offsetof(struct InternalStats::CompactionStatsFull,
|
||||
has_proximal_level_output),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"proximal_level_stats",
|
||||
OptionTypeInfo::Struct(
|
||||
"proximal_level_stats", &compaction_stats_type_info,
|
||||
offsetof(struct InternalStats::CompactionStatsFull,
|
||||
proximal_level_stats),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
|
||||
};
|
||||
|
||||
namespace {
|
||||
// this is a helper struct to serialize and deserialize class Status, because
|
||||
// Status's members are not public.
|
||||
@@ -789,6 +953,11 @@ static std::unordered_map<std::string, OptionTypeInfo> cs_result_type_info = {
|
||||
"stats", &compaction_job_stats_type_info,
|
||||
offsetof(struct CompactionServiceResult, stats),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
|
||||
{"internal_stats",
|
||||
OptionTypeInfo::Struct(
|
||||
"internal_stats", &compaction_internal_stats_type_info,
|
||||
offsetof(struct CompactionServiceResult, internal_stats),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
|
||||
};
|
||||
|
||||
Status CompactionServiceInput::Read(const std::string& data_str,
|
||||
|
||||
@@ -114,6 +114,8 @@ class MyTestCompactionService : public CompactionService {
|
||||
}
|
||||
}
|
||||
|
||||
void CancelAwaitingJobs() override { canceled_ = true; }
|
||||
|
||||
void OnInstallation(const std::string& /*scheduled_job_id*/,
|
||||
CompactionServiceJobStatus status) override {
|
||||
final_updated_status_ = status;
|
||||
@@ -146,6 +148,7 @@ class MyTestCompactionService : public CompactionService {
|
||||
}
|
||||
|
||||
void SetCanceled(bool canceled) { canceled_ = canceled; }
|
||||
bool GetCanceled() { return canceled_; }
|
||||
|
||||
void GetResult(CompactionServiceResult* deserialized) {
|
||||
CompactionServiceResult::Read(result_, deserialized).PermitUncheckedError();
|
||||
@@ -194,12 +197,15 @@ class CompactionServiceTest : public DBTestBase {
|
||||
options->statistics = primary_statistics_;
|
||||
compactor_statistics_ = CreateDBStatistics();
|
||||
|
||||
compaction_service_ = std::make_shared<MyTestCompactionService>(
|
||||
auto my_cs = std::make_shared<MyTestCompactionService>(
|
||||
dbname_, *options, compactor_statistics_, remote_listeners,
|
||||
remote_table_properties_collector_factories);
|
||||
|
||||
compaction_service_ = my_cs;
|
||||
options->compaction_service = compaction_service_;
|
||||
DestroyAndReopen(*options);
|
||||
CreateAndReopenWithCF({"cf_1", "cf_2", "cf_3"}, *options);
|
||||
my_cs->SetCanceled(false);
|
||||
}
|
||||
|
||||
Statistics* GetCompactorStatistics() { return compactor_statistics_.get(); }
|
||||
@@ -339,6 +345,8 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
assert(*id != kNullUniqueId64x2);
|
||||
verify_passed++;
|
||||
});
|
||||
Close();
|
||||
my_cs->SetCanceled(false);
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "cf_1", "cf_2", "cf_3"},
|
||||
options);
|
||||
ASSERT_GT(verify_passed, 0);
|
||||
@@ -349,11 +357,12 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
} else {
|
||||
ASSERT_OK(result.status);
|
||||
}
|
||||
ASSERT_GE(result.stats.elapsed_micros, 1);
|
||||
ASSERT_GE(result.stats.cpu_micros, 1);
|
||||
ASSERT_GE(result.internal_stats.output_level_stats.micros, 1);
|
||||
ASSERT_GE(result.internal_stats.output_level_stats.cpu_micros, 1);
|
||||
|
||||
ASSERT_EQ(20, result.stats.num_output_records);
|
||||
ASSERT_EQ(result.output_files.size(), result.stats.num_output_files);
|
||||
ASSERT_EQ(20, result.internal_stats.output_level_stats.num_output_records);
|
||||
ASSERT_EQ(result.output_files.size(),
|
||||
result.internal_stats.output_level_stats.num_output_files);
|
||||
|
||||
uint64_t total_size = 0;
|
||||
for (auto output_file : result.output_files) {
|
||||
@@ -364,7 +373,7 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
ASSERT_GT(file_size, 0);
|
||||
total_size += file_size;
|
||||
}
|
||||
ASSERT_EQ(total_size, result.stats.total_output_bytes);
|
||||
ASSERT_EQ(total_size, result.internal_stats.TotalBytesWritten());
|
||||
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
@@ -487,7 +496,7 @@ TEST_F(CompactionServiceTest, PreservedOptionsLocalCompaction) {
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
// Should be old value
|
||||
ASSERT_EQ(2, compaction->mutable_cf_options()
|
||||
->level0_file_num_compaction_trigger);
|
||||
.level0_file_num_compaction_trigger);
|
||||
ASSERT_TRUE(dbfull()->min_options_file_numbers_.empty());
|
||||
});
|
||||
|
||||
@@ -573,7 +582,7 @@ TEST_F(CompactionServiceTest, PreservedOptionsRemoteCompaction) {
|
||||
"CompactionJob::ProcessKeyValueCompaction()::Processing", [&](void* arg) {
|
||||
auto compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_EQ(2, compaction->mutable_cf_options()
|
||||
->level0_file_num_compaction_trigger);
|
||||
.level0_file_num_compaction_trigger);
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -708,6 +717,46 @@ TEST_F(CompactionServiceTest, VerifyStatsLocalFallback) {
|
||||
VerifyTestData();
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, VerifyInputRecordCount) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
std::string start_str = Key(15);
|
||||
std::string end_str = Key(45);
|
||||
Slice start(start_str);
|
||||
Slice end(end_str);
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
|
||||
// Only iterator through 10 keys and force compaction to finish.
|
||||
int num_iter = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::ProcessKeyValueCompaction()::stop", [&](void* stop_ptr) {
|
||||
num_iter++;
|
||||
if (num_iter == 10) {
|
||||
*(bool*)stop_ptr = true;
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// CompactRange() should fail
|
||||
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
const char* expected_message =
|
||||
"Compaction number of input keys does not match number of keys "
|
||||
"processed.";
|
||||
ASSERT_TRUE(std::strstr(s.getState(), expected_message));
|
||||
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CorruptedOutput) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -996,6 +1045,41 @@ TEST_F(CompactionServiceTest, CancelCompactionOnRemoteSide) {
|
||||
VerifyTestData();
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CancelCompactionOnPrimarySide) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
std::string start_str = Key(15);
|
||||
std::string end_str = Key(45);
|
||||
Slice start(start_str);
|
||||
Slice end(end_str);
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
my_cs = GetCompactionService();
|
||||
|
||||
// Primary DB calls CancelAllBackgroundWork() while the compaction is running
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():Inprogress",
|
||||
[&](void* /*arg*/) { CancelAllBackgroundWork(db_, false /*wait*/); });
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
|
||||
// Check canceled_ was set to true by CancelAwaitingJobs()
|
||||
ASSERT_TRUE(my_cs->GetCanceled());
|
||||
|
||||
// compaction number is not increased
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, FailedToStart) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -1118,6 +1202,67 @@ TEST_F(CompactionServiceTest, Snapshot) {
|
||||
db_->ReleaseSnapshot(s1);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, PrecludeLastLevel) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
options.max_subcompactions = 10;
|
||||
options.num_levels = kNumLevels;
|
||||
|
||||
ReopenWithCompactionService(&options);
|
||||
// Alternate for comparison: DestroyAndReopen(options);
|
||||
|
||||
// This is simpler than setting up mock time to make the user option work,
|
||||
// but is not as direct as testing with preclude option itself.
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"Compaction::SupportsPerKeyPlacement:Enabled",
|
||||
[&](void* arg) { *static_cast<bool*>(arg) = true; });
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::PrepareTimes():preclude_last_level_min_seqno",
|
||||
[&](void* arg) { *static_cast<SequenceNumber*>(arg) = 100; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
for (int i = 0; i < kNumTrigger; i++) {
|
||||
for (int j = 0; j < kNumKeys; j++) {
|
||||
ASSERT_OK(Put(Key(j * kNumTrigger + i), "v" + std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// Data split between proximal (kUnknown) and last (kCold) levels
|
||||
ASSERT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
|
||||
// TODO: Check FileSystem temperatures with FileTemperatureTestFS
|
||||
|
||||
for (int i = 0; i < kNumTrigger; i++) {
|
||||
for (int j = 0; j < kNumKeys; j++) {
|
||||
ASSERT_EQ(Get(Key(j * kNumTrigger + i)), "v" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Verify Output Stats
|
||||
auto my_cs = GetCompactionService();
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_GT(result.internal_stats.output_level_stats.cpu_micros, 0);
|
||||
ASSERT_GT(result.internal_stats.output_level_stats.micros, 0);
|
||||
ASSERT_EQ(result.internal_stats.output_level_stats.num_output_records +
|
||||
result.internal_stats.proximal_level_stats.num_output_records,
|
||||
kNumTrigger * kNumKeys);
|
||||
ASSERT_EQ(result.internal_stats.output_level_stats.num_output_files +
|
||||
result.internal_stats.proximal_level_stats.num_output_files,
|
||||
2);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, ConcurrentCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 100;
|
||||
@@ -1371,6 +1516,40 @@ TEST_F(CompactionServiceTest, FallbackLocalManual) {
|
||||
VerifyTestData();
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, AbortedWhileWait) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
ReopenWithCompactionService(&options);
|
||||
|
||||
GenerateTestData();
|
||||
VerifyTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
Statistics* compactor_statistics = GetCompactorStatistics();
|
||||
Statistics* primary_statistics = GetPrimaryStatistics();
|
||||
|
||||
my_cs->ResetOverride();
|
||||
std::string start_str = Key(15);
|
||||
std::string end_str = Key(45);
|
||||
Slice start(start_str);
|
||||
Slice end(end_str);
|
||||
|
||||
// Override Wait() result with kAborted
|
||||
my_cs->OverrideWaitStatus(CompactionServiceJobStatus::kAborted);
|
||||
start_str = Key(120);
|
||||
start = start_str;
|
||||
|
||||
Status s = db_->CompactRange(CompactRangeOptions(), &start, nullptr);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsAborted());
|
||||
// no remote compaction is run
|
||||
ASSERT_EQ(my_cs->GetCompactionNum(), 0);
|
||||
// make sure the compaction statistics is not recorded any side
|
||||
ASSERT_EQ(primary_statistics->getTickerCount(COMPACT_WRITE_BYTES), 0);
|
||||
ASSERT_EQ(primary_statistics->getTickerCount(REMOTE_COMPACT_WRITE_BYTES), 0);
|
||||
ASSERT_EQ(compactor_statistics->getTickerCount(COMPACT_WRITE_BYTES), 0);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, RemoteEventListener) {
|
||||
class RemoteEventListenerTest : public EventListener {
|
||||
public:
|
||||
|
||||
@@ -36,11 +36,11 @@ Slice CompactionState::LargestUserKey() {
|
||||
}
|
||||
|
||||
void CompactionState::AggregateCompactionStats(
|
||||
InternalStats::CompactionStatsFull& compaction_stats,
|
||||
CompactionJobStats& compaction_job_stats) {
|
||||
InternalStats::CompactionStatsFull& internal_stats,
|
||||
CompactionJobStats& job_stats) {
|
||||
for (const auto& sc : sub_compact_states) {
|
||||
sc.AggregateCompactionOutputStats(compaction_stats);
|
||||
compaction_job_stats.Add(sc.compaction_job_stats);
|
||||
sc.AggregateCompactionOutputStats(internal_stats);
|
||||
job_stats.Add(sc.compaction_job_stats);
|
||||
}
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -29,8 +29,8 @@ class CompactionState {
|
||||
Status status;
|
||||
|
||||
void AggregateCompactionStats(
|
||||
InternalStats::CompactionStatsFull& compaction_stats,
|
||||
CompactionJobStats& compaction_job_stats);
|
||||
InternalStats::CompactionStatsFull& internal_stats,
|
||||
CompactionJobStats& job_stats);
|
||||
|
||||
explicit CompactionState(Compaction* c) : compaction(c) {}
|
||||
|
||||
|
||||
@@ -14,22 +14,32 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
void SubcompactionState::AggregateCompactionOutputStats(
|
||||
InternalStats::CompactionStatsFull& compaction_stats) const {
|
||||
compaction_stats.stats.Add(compaction_outputs_.stats_);
|
||||
if (HasPenultimateLevelOutputs()) {
|
||||
compaction_stats.has_penultimate_level_output = true;
|
||||
compaction_stats.penultimate_level_stats.Add(
|
||||
penultimate_level_outputs_.stats_);
|
||||
InternalStats::CompactionStatsFull& internal_stats) const {
|
||||
// Outputs should be closed. By extension, any files created just for
|
||||
// range deletes have already been written also.
|
||||
assert(compaction_outputs_.HasBuilder() == false);
|
||||
assert(proximal_level_outputs_.HasBuilder() == false);
|
||||
|
||||
// FIXME: These stats currently include abandonned output files
|
||||
// assert(compaction_outputs_.stats_.num_output_files ==
|
||||
// compaction_outputs_.outputs_.size());
|
||||
// assert(proximal_level_outputs_.stats_.num_output_files ==
|
||||
// proximal_level_outputs_.outputs_.size());
|
||||
|
||||
internal_stats.output_level_stats.Add(compaction_outputs_.stats_);
|
||||
if (proximal_level_outputs_.HasOutput()) {
|
||||
internal_stats.has_proximal_level_output = true;
|
||||
internal_stats.proximal_level_stats.Add(proximal_level_outputs_.stats_);
|
||||
}
|
||||
}
|
||||
|
||||
OutputIterator SubcompactionState::GetOutputs() const {
|
||||
return OutputIterator(penultimate_level_outputs_.outputs_,
|
||||
return OutputIterator(proximal_level_outputs_.outputs_,
|
||||
compaction_outputs_.outputs_);
|
||||
}
|
||||
|
||||
void SubcompactionState::Cleanup(Cache* cache) {
|
||||
penultimate_level_outputs_.Cleanup();
|
||||
proximal_level_outputs_.Cleanup();
|
||||
compaction_outputs_.Cleanup();
|
||||
|
||||
if (!status.ok()) {
|
||||
@@ -43,7 +53,7 @@ void SubcompactionState::Cleanup(Cache* cache) {
|
||||
// be picked up by scanning the DB directory.
|
||||
TableCache::ReleaseObsolete(
|
||||
cache, out.meta.fd.GetNumber(), nullptr /*handle*/,
|
||||
compaction->mutable_cf_options()->uncache_aggressiveness);
|
||||
compaction->mutable_cf_options().uncache_aggressiveness);
|
||||
}
|
||||
}
|
||||
// TODO: sub_compact.io_status is not checked like status. Not sure if thats
|
||||
@@ -52,9 +62,9 @@ void SubcompactionState::Cleanup(Cache* cache) {
|
||||
}
|
||||
|
||||
Slice SubcompactionState::SmallestUserKey() const {
|
||||
if (has_penultimate_level_outputs_) {
|
||||
if (proximal_level_outputs_.HasOutput()) {
|
||||
Slice a = compaction_outputs_.SmallestUserKey();
|
||||
Slice b = penultimate_level_outputs_.SmallestUserKey();
|
||||
Slice b = proximal_level_outputs_.SmallestUserKey();
|
||||
if (a.empty()) {
|
||||
return b;
|
||||
}
|
||||
@@ -74,9 +84,9 @@ Slice SubcompactionState::SmallestUserKey() const {
|
||||
}
|
||||
|
||||
Slice SubcompactionState::LargestUserKey() const {
|
||||
if (has_penultimate_level_outputs_) {
|
||||
if (proximal_level_outputs_.HasOutput()) {
|
||||
Slice a = compaction_outputs_.LargestUserKey();
|
||||
Slice b = penultimate_level_outputs_.LargestUserKey();
|
||||
Slice b = proximal_level_outputs_.LargestUserKey();
|
||||
if (a.empty()) {
|
||||
return b;
|
||||
}
|
||||
@@ -96,18 +106,13 @@ Slice SubcompactionState::LargestUserKey() const {
|
||||
}
|
||||
|
||||
Status SubcompactionState::AddToOutput(
|
||||
const CompactionIterator& iter,
|
||||
const CompactionIterator& iter, bool use_proximal_output,
|
||||
const CompactionFileOpenFunc& open_file_func,
|
||||
const CompactionFileCloseFunc& close_file_func) {
|
||||
// update target output first
|
||||
is_current_penultimate_level_ = iter.output_to_penultimate_level();
|
||||
current_outputs_ = is_current_penultimate_level_ ? &penultimate_level_outputs_
|
||||
: &compaction_outputs_;
|
||||
if (is_current_penultimate_level_) {
|
||||
has_penultimate_level_outputs_ = true;
|
||||
}
|
||||
|
||||
return Current().AddToOutput(iter, open_file_func, close_file_func);
|
||||
// update target output
|
||||
current_outputs_ =
|
||||
use_proximal_output ? &proximal_level_outputs_ : &compaction_outputs_;
|
||||
return current_outputs_->AddToOutput(iter, open_file_func, close_file_func);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -26,13 +26,13 @@ namespace ROCKSDB_NAMESPACE {
|
||||
// Maintains state and outputs for each sub-compaction
|
||||
// It contains 2 `CompactionOutputs`:
|
||||
// 1. one for the normal output files
|
||||
// 2. another for the penultimate level outputs
|
||||
// 2. another for the proximal level outputs
|
||||
// a `current` pointer maintains the current output group, when calling
|
||||
// `AddToOutput()`, it checks the output of the current compaction_iterator key
|
||||
// and point `current` to the target output group. By default, it just points to
|
||||
// normal compaction_outputs, if the compaction_iterator key should be placed on
|
||||
// the penultimate level, `current` is changed to point to
|
||||
// `penultimate_level_outputs`.
|
||||
// the proximal level, `current` is changed to point to
|
||||
// `proximal_level_outputs`.
|
||||
// The later operations uses `Current()` to get the target group.
|
||||
//
|
||||
// +----------+ +-----------------------------+ +---------+
|
||||
@@ -43,7 +43,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
// | | ... |
|
||||
// |
|
||||
// | +-----------------------------+ +---------+
|
||||
// +-------------> | penultimate_level_outputs |----->| output |
|
||||
// +-------------> | proximal_level_outputs |----->| output |
|
||||
// +-----------------------------+ +---------+
|
||||
// | ... |
|
||||
|
||||
@@ -78,30 +78,21 @@ class SubcompactionState {
|
||||
Slice LargestUserKey() const;
|
||||
|
||||
// Get all outputs from the subcompaction. For per_key_placement compaction,
|
||||
// it returns both the last level outputs and penultimate level outputs.
|
||||
// it returns both the last level outputs and proximal level outputs.
|
||||
OutputIterator GetOutputs() const;
|
||||
|
||||
// Assign range dels aggregator, for each range_del, it can only be assigned
|
||||
// to one output level, for per_key_placement, it's going to be the
|
||||
// penultimate level.
|
||||
// TODO: This does not work for per_key_placement + user-defined timestamp +
|
||||
// DeleteRange() combo. If user-defined timestamp is enabled,
|
||||
// it is possible for a range tombstone to belong to bottommost level (
|
||||
// seqno < earliest snapshot) without being dropped (garbage collection
|
||||
// for user-defined timestamp).
|
||||
// Assign range dels aggregator. The various tombstones will potentially
|
||||
// be filtered to different outputs.
|
||||
void AssignRangeDelAggregator(
|
||||
std::unique_ptr<CompactionRangeDelAggregator>&& range_del_agg) {
|
||||
if (compaction->SupportsPerKeyPlacement()) {
|
||||
penultimate_level_outputs_.AssignRangeDelAggregator(
|
||||
std::move(range_del_agg));
|
||||
} else {
|
||||
compaction_outputs_.AssignRangeDelAggregator(std::move(range_del_agg));
|
||||
}
|
||||
assert(range_del_agg_ == nullptr);
|
||||
assert(range_del_agg);
|
||||
range_del_agg_ = std::move(range_del_agg);
|
||||
}
|
||||
|
||||
void RemoveLastEmptyOutput() {
|
||||
compaction_outputs_.RemoveLastEmptyOutput();
|
||||
penultimate_level_outputs_.RemoveLastEmptyOutput();
|
||||
proximal_level_outputs_.RemoveLastEmptyOutput();
|
||||
}
|
||||
|
||||
void BuildSubcompactionJobInfo(
|
||||
@@ -128,14 +119,14 @@ class SubcompactionState {
|
||||
start(_start),
|
||||
end(_end),
|
||||
sub_job_id(_sub_job_id),
|
||||
compaction_outputs_(c, /*is_penultimate_level=*/false),
|
||||
penultimate_level_outputs_(c, /*is_penultimate_level=*/true) {
|
||||
compaction_outputs_(c, /*is_proximal_level=*/false),
|
||||
proximal_level_outputs_(c, /*is_proximal_level=*/true) {
|
||||
assert(compaction != nullptr);
|
||||
// Set output split key (used for RoundRobin feature) only for normal
|
||||
// compaction_outputs, output to penultimate_level feature doesn't support
|
||||
// compaction_outputs, output to proximal_level feature doesn't support
|
||||
// RoundRobin feature (and may never going to be supported, because for
|
||||
// RoundRobin, the data time is mostly naturally sorted, no need to have
|
||||
// per-key placement with output_to_penultimate_level).
|
||||
// per-key placement with output_to_proximal_level).
|
||||
compaction_outputs_.SetOutputSlitKey(start, end);
|
||||
}
|
||||
|
||||
@@ -150,27 +141,17 @@ class SubcompactionState {
|
||||
compaction_job_stats(std::move(state.compaction_job_stats)),
|
||||
sub_job_id(state.sub_job_id),
|
||||
compaction_outputs_(std::move(state.compaction_outputs_)),
|
||||
penultimate_level_outputs_(std::move(state.penultimate_level_outputs_)),
|
||||
is_current_penultimate_level_(state.is_current_penultimate_level_),
|
||||
has_penultimate_level_outputs_(state.has_penultimate_level_outputs_) {
|
||||
current_outputs_ = is_current_penultimate_level_
|
||||
? &penultimate_level_outputs_
|
||||
proximal_level_outputs_(std::move(state.proximal_level_outputs_)),
|
||||
range_del_agg_(std::move(state.range_del_agg_)) {
|
||||
current_outputs_ = state.current_outputs_ == &state.proximal_level_outputs_
|
||||
? &proximal_level_outputs_
|
||||
: &compaction_outputs_;
|
||||
}
|
||||
|
||||
bool HasPenultimateLevelOutputs() const {
|
||||
return has_penultimate_level_outputs_ ||
|
||||
penultimate_level_outputs_.HasRangeDel();
|
||||
}
|
||||
|
||||
bool IsCurrentPenultimateLevel() const {
|
||||
return is_current_penultimate_level_;
|
||||
}
|
||||
|
||||
// Add all the new files from this compaction to version_edit
|
||||
void AddOutputsEdit(VersionEdit* out_edit) const {
|
||||
for (const auto& file : penultimate_level_outputs_.outputs_) {
|
||||
out_edit->AddFile(compaction->GetPenultimateLevel(), file.meta);
|
||||
for (const auto& file : proximal_level_outputs_.outputs_) {
|
||||
out_edit->AddFile(compaction->GetProximalLevel(), file.meta);
|
||||
}
|
||||
for (const auto& file : compaction_outputs_.outputs_) {
|
||||
out_edit->AddFile(compaction->output_level(), file.meta);
|
||||
@@ -180,41 +161,74 @@ class SubcompactionState {
|
||||
void Cleanup(Cache* cache);
|
||||
|
||||
void AggregateCompactionOutputStats(
|
||||
InternalStats::CompactionStatsFull& compaction_stats) const;
|
||||
InternalStats::CompactionStatsFull& internal_stats) const;
|
||||
|
||||
CompactionOutputs& Current() const {
|
||||
assert(current_outputs_);
|
||||
return *current_outputs_;
|
||||
}
|
||||
|
||||
CompactionOutputs* Outputs(bool is_proximal_level) {
|
||||
assert(compaction);
|
||||
if (is_proximal_level) {
|
||||
assert(compaction->SupportsPerKeyPlacement());
|
||||
return &proximal_level_outputs_;
|
||||
}
|
||||
return &compaction_outputs_;
|
||||
}
|
||||
|
||||
// Per-level stats for the output
|
||||
InternalStats::CompactionStats* OutputStats(bool is_proximal_level) {
|
||||
assert(compaction);
|
||||
if (is_proximal_level) {
|
||||
assert(compaction->SupportsPerKeyPlacement());
|
||||
return &proximal_level_outputs_.stats_;
|
||||
}
|
||||
return &compaction_outputs_.stats_;
|
||||
}
|
||||
|
||||
CompactionRangeDelAggregator* RangeDelAgg() const {
|
||||
return range_del_agg_.get();
|
||||
}
|
||||
|
||||
// if the outputs have range delete, range delete is also data
|
||||
bool HasRangeDel() const {
|
||||
return range_del_agg_ && !range_del_agg_->IsEmpty();
|
||||
}
|
||||
|
||||
// Add compaction_iterator key/value to the `Current` output group.
|
||||
Status AddToOutput(const CompactionIterator& iter,
|
||||
Status AddToOutput(const CompactionIterator& iter, bool use_proximal_output,
|
||||
const CompactionFileOpenFunc& open_file_func,
|
||||
const CompactionFileCloseFunc& close_file_func);
|
||||
|
||||
// Close all compaction output files, both output_to_penultimate_level outputs
|
||||
// Close all compaction output files, both output_to_proximal_level outputs
|
||||
// and normal outputs.
|
||||
Status CloseCompactionFiles(const Status& curr_status,
|
||||
const CompactionFileOpenFunc& open_file_func,
|
||||
const CompactionFileCloseFunc& close_file_func) {
|
||||
auto per_key = compaction->SupportsPerKeyPlacement();
|
||||
// Call FinishCompactionOutputFile() even if status is not ok: it needs to
|
||||
// close the output file.
|
||||
// CloseOutput() may open new compaction output files.
|
||||
is_current_penultimate_level_ = true;
|
||||
Status s = penultimate_level_outputs_.CloseOutput(
|
||||
curr_status, open_file_func, close_file_func);
|
||||
is_current_penultimate_level_ = false;
|
||||
s = compaction_outputs_.CloseOutput(s, open_file_func, close_file_func);
|
||||
Status s = curr_status;
|
||||
if (per_key) {
|
||||
s = proximal_level_outputs_.CloseOutput(s, range_del_agg_.get(),
|
||||
open_file_func, close_file_func);
|
||||
} else {
|
||||
assert(proximal_level_outputs_.HasBuilder() == false);
|
||||
assert(proximal_level_outputs_.HasOutput() == false);
|
||||
}
|
||||
s = compaction_outputs_.CloseOutput(s, range_del_agg_.get(), open_file_func,
|
||||
close_file_func);
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
// State kept for output being generated
|
||||
CompactionOutputs compaction_outputs_;
|
||||
CompactionOutputs penultimate_level_outputs_;
|
||||
CompactionOutputs proximal_level_outputs_;
|
||||
CompactionOutputs* current_outputs_ = &compaction_outputs_;
|
||||
bool is_current_penultimate_level_ = false;
|
||||
bool has_penultimate_level_outputs_ = false;
|
||||
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
File diff suppressed because it is too large.
Load diff
+11
-1
@@ -4,7 +4,6 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
|
||||
|
||||
#include "rocksdb/convenience.h"
|
||||
|
||||
#include "db/convenience_impl.h"
|
||||
@@ -27,6 +26,17 @@ Status DeleteFilesInRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
|
||||
Status DeleteFilesInRanges(DB* db, ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n, bool include_end) {
|
||||
std::vector<RangeOpt> range_opts(n);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
range_opts[i] = {OptSlice::CopyFromPtr(ranges[i].start),
|
||||
OptSlice::CopyFromPtr(ranges[i].limit)};
|
||||
}
|
||||
return DeleteFilesInRanges(db, column_family, range_opts.data(), n,
|
||||
include_end);
|
||||
}
|
||||
|
||||
Status DeleteFilesInRanges(DB* db, ColumnFamilyHandle* column_family,
|
||||
const RangeOpt* ranges, size_t n, bool include_end) {
|
||||
return (static_cast_with_check<DBImpl>(db->GetRootDB()))
|
||||
->DeleteFilesInRanges(column_family, ranges, n, include_end);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
// 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 "rocksdb/options.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
@@ -25,6 +23,7 @@
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/utilities/transaction_db.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
@@ -852,6 +851,9 @@ 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_;
|
||||
@@ -864,7 +866,6 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db_));
|
||||
assert(db_ != nullptr); // suppress false clang-analyze report
|
||||
Build(100, 2);
|
||||
// ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
|
||||
ASSERT_OK(dbi->TEST_FlushMemTable());
|
||||
mock->SetCorruptionMode(mode);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -349,4 +348,3 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
+190
-17
@@ -161,6 +161,7 @@ TEST_F(DBBasicTest, UniqueSession) {
|
||||
|
||||
ASSERT_EQ(sid2, sid3);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"goku"}, options);
|
||||
ASSERT_OK(db_->GetDbSessionId(sid1));
|
||||
ASSERT_OK(Put("bar", "e1"));
|
||||
@@ -179,6 +180,7 @@ TEST_F(DBBasicTest, UniqueSession) {
|
||||
TEST_F(DBBasicTest, ReadOnlyDB) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
ASSERT_OK(Put("bar", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo", "v3"));
|
||||
Close();
|
||||
|
||||
@@ -208,10 +210,11 @@ TEST_F(DBBasicTest, ReadOnlyDB) {
|
||||
|
||||
auto options = CurrentOptions();
|
||||
assert(options.env == env_);
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ASSERT_OK(EnforcedReadOnlyReopen(options));
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
ASSERT_EQ("v2", Get("bar"));
|
||||
verify_all_iters();
|
||||
ASSERT_EQ(Flush().code(), Status::Code::kNotSupported);
|
||||
Close();
|
||||
|
||||
// Reopen and flush memtable.
|
||||
@@ -219,26 +222,38 @@ TEST_F(DBBasicTest, ReadOnlyDB) {
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
// Now check keys in read only mode.
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ASSERT_OK(EnforcedReadOnlyReopen(options));
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
ASSERT_EQ("v2", Get("bar"));
|
||||
verify_all_iters();
|
||||
ASSERT_TRUE(db_->SyncWAL().IsNotSupported());
|
||||
ASSERT_EQ(db_->SyncWAL().code(), Status::Code::kNotSupported);
|
||||
|
||||
// More ops that should fail
|
||||
std::vector<ColumnFamilyHandle*> cfhs{{}};
|
||||
ASSERT_EQ(db_->CreateColumnFamily(options, "blah", &cfhs[0]).code(),
|
||||
Status::Code::kNotSupported);
|
||||
|
||||
ASSERT_EQ(db_->CreateColumnFamilies(options, {"blah"}, &cfhs).code(),
|
||||
Status::Code::kNotSupported);
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> cfds;
|
||||
cfds.push_back({"blah", options});
|
||||
ASSERT_EQ(db_->CreateColumnFamilies(cfds, &cfhs).code(),
|
||||
Status::Code::kNotSupported);
|
||||
}
|
||||
|
||||
// TODO akanksha: Update the test to check that combination
|
||||
// does not actually write to FS (use open read-only with
|
||||
// CompositeEnvWrapper+ReadOnlyFileSystem).
|
||||
TEST_F(DBBasicTest, DISABLED_ReadOnlyDBWithWriteDBIdToManifestSet) {
|
||||
TEST_F(DBBasicTest, ReadOnlyDBWithWriteDBIdToManifestSet) {
|
||||
auto options = CurrentOptions();
|
||||
options.write_dbid_to_manifest = false;
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
ASSERT_OK(Put("bar", "v2"));
|
||||
ASSERT_OK(Put("foo", "v3"));
|
||||
Close();
|
||||
|
||||
auto options = CurrentOptions();
|
||||
options.write_dbid_to_manifest = true;
|
||||
assert(options.env == env_);
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ASSERT_OK(EnforcedReadOnlyReopen(options));
|
||||
std::string db_id1;
|
||||
ASSERT_OK(db_->GetDbIdentity(db_id1));
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
@@ -258,7 +273,7 @@ TEST_F(DBBasicTest, DISABLED_ReadOnlyDBWithWriteDBIdToManifestSet) {
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
// Now check keys in read only mode.
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ASSERT_OK(EnforcedReadOnlyReopen(options));
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
ASSERT_EQ("v2", Get("bar"));
|
||||
ASSERT_TRUE(db_->SyncWAL().IsNotSupported());
|
||||
@@ -878,7 +893,6 @@ TEST_F(DBBasicTest, Snapshot) {
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
|
||||
class DBBasicMultiConfigs : public DBBasicTest,
|
||||
public ::testing::WithParamInterface<int> {
|
||||
public:
|
||||
@@ -2604,8 +2618,7 @@ class DBMultiGetAsyncIOTest : public DBBasicTest,
|
||||
// Warm up the block cache so we don't need to use the IO uring
|
||||
Iterator* iter = dbfull()->NewIterator(ReadOptions());
|
||||
for (iter->SeekToFirst(); iter->Valid() && iter->status().ok();
|
||||
iter->Next())
|
||||
;
|
||||
iter->Next());
|
||||
EXPECT_OK(iter->status());
|
||||
delete iter;
|
||||
#endif // ROCKSDB_IOURING_PRESENT
|
||||
@@ -3275,8 +3288,7 @@ TEST_F(DBBasicTest, GetAllKeyVersions) {
|
||||
ASSERT_OK(Delete(std::to_string(i)));
|
||||
}
|
||||
std::vector<KeyVersion> key_versions;
|
||||
ASSERT_OK(GetAllKeyVersions(db_, Slice(), Slice(),
|
||||
std::numeric_limits<size_t>::max(),
|
||||
ASSERT_OK(GetAllKeyVersions(db_, {}, {}, std::numeric_limits<size_t>::max(),
|
||||
&key_versions));
|
||||
ASSERT_EQ(kNumInserts + kNumDeletes + kNumUpdates, key_versions.size());
|
||||
for (size_t i = 0; i < kNumInserts + kNumDeletes + kNumUpdates; i++) {
|
||||
@@ -3286,7 +3298,7 @@ TEST_F(DBBasicTest, GetAllKeyVersions) {
|
||||
ASSERT_EQ(key_versions[i].GetTypeName(), "TypeValue");
|
||||
}
|
||||
}
|
||||
ASSERT_OK(GetAllKeyVersions(db_, handles_[0], Slice(), Slice(),
|
||||
ASSERT_OK(GetAllKeyVersions(db_, handles_[0], {}, {},
|
||||
std::numeric_limits<size_t>::max(),
|
||||
&key_versions));
|
||||
ASSERT_EQ(kNumInserts + kNumDeletes + kNumUpdates, key_versions.size());
|
||||
@@ -3301,10 +3313,17 @@ TEST_F(DBBasicTest, GetAllKeyVersions) {
|
||||
for (size_t i = 0; i + 1 != kNumDeletes; ++i) {
|
||||
ASSERT_OK(Delete(1, std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(GetAllKeyVersions(db_, handles_[1], Slice(), Slice(),
|
||||
ASSERT_OK(GetAllKeyVersions(db_, handles_[1], {}, {},
|
||||
std::numeric_limits<size_t>::max(),
|
||||
&key_versions));
|
||||
ASSERT_EQ(kNumInserts + kNumDeletes + kNumUpdates - 3, key_versions.size());
|
||||
|
||||
// Change from historical behavior: empty key is now interpreted literally as
|
||||
// a legal key (rather than as a "not present" key)
|
||||
ASSERT_OK(GetAllKeyVersions(db_, handles_[1], Slice(), Slice(),
|
||||
std::numeric_limits<size_t>::max(),
|
||||
&key_versions));
|
||||
ASSERT_EQ(key_versions.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, ValueTypeString) {
|
||||
@@ -3356,6 +3375,69 @@ TEST_F(DBBasicTest, MultiGetIOBufferOverrun) {
|
||||
keys.data(), values.data(), statuses.data(), true);
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetWithSnapshotsAndPersistedTier) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = true;
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"cf1", "cf2"}, options);
|
||||
|
||||
// Insert initial data
|
||||
ASSERT_OK(Put(0, "key1", "value1_cf0"));
|
||||
ASSERT_OK(Put(1, "key1", "value1_cf1"));
|
||||
ASSERT_OK(Put(2, "key1", "value1_cf2"));
|
||||
ASSERT_OK(Flush({0, 1, 2}));
|
||||
for (auto cf : {0, 1, 2}) {
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0, cf));
|
||||
}
|
||||
|
||||
ASSERT_OK(Put(0, "key1", "value2_cf0"));
|
||||
ASSERT_OK(Put(1, "key1", "value2_cf1"));
|
||||
ASSERT_OK(Put(2, "key1", "value2_cf2"));
|
||||
|
||||
// Prepare for concurrent atomic flush
|
||||
std::atomic<bool> flush_done(false);
|
||||
std::thread flush_thread([&]() {
|
||||
ASSERT_OK(Flush({0, 1, 2}));
|
||||
flush_done.store(true);
|
||||
});
|
||||
|
||||
// Perform MultiGet with snapshot and read_tier = kPersistentTier
|
||||
ReadOptions ro;
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ro.snapshot = snapshot;
|
||||
ro.read_tier = kPersistedTier;
|
||||
|
||||
std::string k = "key1";
|
||||
std::vector<Slice> keys(3, Slice(k));
|
||||
std::vector<Status> statuses(keys.size());
|
||||
std::vector<ColumnFamilyHandle*> cfs(keys.size());
|
||||
std::vector<Slice> new_keys(keys.size());
|
||||
std::vector<PinnableSlice> pin_values(keys.size());
|
||||
for (size_t i = 0; i < keys.size(); ++i) {
|
||||
cfs[i] = handles_[i];
|
||||
}
|
||||
db_->MultiGet(ro, cfs.size(), cfs.data(), keys.data(), pin_values.data(),
|
||||
statuses.data());
|
||||
for (const auto& s : statuses) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
if (pin_values[0] == "value1_cf0") {
|
||||
// Check if the first value matches expected value
|
||||
ASSERT_EQ(pin_values[1], "value1_cf1");
|
||||
ASSERT_EQ(pin_values[2], "value1_cf2");
|
||||
} else {
|
||||
// If first value doesn't match, check if we got the updated values
|
||||
ASSERT_EQ(pin_values[0], "value2_cf0");
|
||||
ASSERT_EQ(pin_values[1], "value2_cf1");
|
||||
ASSERT_EQ(pin_values[2], "value2_cf2");
|
||||
}
|
||||
|
||||
flush_thread.join();
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, IncrementalRecoveryNoCorrupt) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
@@ -4996,6 +5078,97 @@ TEST_F(DBBasicTest, VerifyFileChecksumsReadahead) {
|
||||
(sst_size + alignment - 1) / (alignment));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, DisallowMemtableWrite) {
|
||||
// This test is mostly about what you can't do with memtable writes
|
||||
// disallowed. For what you can do, see
|
||||
// ExternalSSTFileBasicTest.FailIfNotBottommostLevelAndDisallowMemtable
|
||||
Options options_allow = GetDefaultOptions();
|
||||
options_allow.create_if_missing = true;
|
||||
Options options_disallow = options_allow;
|
||||
options_disallow.disallow_memtable_writes = true;
|
||||
|
||||
DestroyAndReopen(options_allow);
|
||||
// CFs allowing and disallowing memtable write
|
||||
CreateColumnFamilies({"cf1", "cf2"}, options_allow);
|
||||
CreateColumnFamilies({"cf3"}, options_disallow);
|
||||
// XXX: needed to get consistent handles_ mappings
|
||||
ReopenWithColumnFamilies(
|
||||
{"default", "cf1", "cf2", "cf3"},
|
||||
{options_allow, options_allow, options_allow, options_disallow});
|
||||
|
||||
EXPECT_EQ(Put(0, "a0", "1").code(), Status::Code::kOk);
|
||||
EXPECT_EQ(Put(1, "a1", "1").code(), Status::Code::kOk);
|
||||
EXPECT_EQ(Put(2, "a2", "1").code(), Status::Code::kOk);
|
||||
EXPECT_EQ(Put(3, "a3", "1").code(), Status::Code::kInvalidArgument);
|
||||
|
||||
EXPECT_EQ(Get(0, "a0"), "1");
|
||||
EXPECT_EQ(Get(1, "a1"), "1");
|
||||
EXPECT_EQ(Get(2, "a2"), "1");
|
||||
EXPECT_EQ(Get(3, "a3"), "NOT_FOUND");
|
||||
|
||||
EXPECT_EQ(Delete(0, "z0").code(), Status::Code::kOk);
|
||||
EXPECT_EQ(Delete(1, "z1").code(), Status::Code::kOk);
|
||||
EXPECT_EQ(Delete(2, "z2").code(), Status::Code::kOk);
|
||||
EXPECT_EQ(Delete(3, "z3").code(), Status::Code::kInvalidArgument);
|
||||
|
||||
WriteBatch wb;
|
||||
EXPECT_EQ(wb.Put(handles_[0], "b0", "2").code(), Status::Code::kOk);
|
||||
EXPECT_EQ(wb.Put(handles_[1], "b1", "2").code(), Status::Code::kOk);
|
||||
EXPECT_EQ(wb.Put(handles_[2], "b2", "2").code(), Status::Code::kOk);
|
||||
EXPECT_EQ(wb.Put(handles_[3], "b3", "2").code(),
|
||||
Status::Code::kInvalidArgument);
|
||||
ASSERT_OK(db_->Write({}, &wb));
|
||||
wb.Clear();
|
||||
|
||||
EXPECT_EQ(Get(0, "b0"), "2");
|
||||
EXPECT_EQ(Get(1, "b1"), "2");
|
||||
EXPECT_EQ(Get(2, "b2"), "2");
|
||||
EXPECT_EQ(Get(3, "b3"), "NOT_FOUND");
|
||||
|
||||
// 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(
|
||||
{"default", "cf1", "cf2", "cf3"},
|
||||
{options_allow, options_allow, options_disallow, options_allow})
|
||||
.code(),
|
||||
Status::Code::kInvalidArgument);
|
||||
|
||||
// Successfully opening with allow creates L0 files from the WAL
|
||||
ReopenWithColumnFamilies({"default", "cf1", "cf2", "cf3"}, options_allow);
|
||||
|
||||
EXPECT_EQ(Get(0, "a0"), "1");
|
||||
EXPECT_EQ(Get(1, "a1"), "1");
|
||||
EXPECT_EQ(Get(2, "a2"), "1");
|
||||
EXPECT_EQ(Get(3, "a3"), "NOT_FOUND");
|
||||
|
||||
// Now able to disallow on CF2 because no relevant WAL entries
|
||||
ReopenWithColumnFamilies(
|
||||
{"default", "cf1", "cf2", "cf3"},
|
||||
{options_allow, options_allow, options_disallow, options_allow});
|
||||
|
||||
EXPECT_EQ(Get(0, "a0"), "1");
|
||||
EXPECT_EQ(Get(1, "a1"), "1");
|
||||
EXPECT_EQ(Get(2, "a2"), "1");
|
||||
EXPECT_EQ(Get(3, "a3"), "NOT_FOUND");
|
||||
|
||||
// Now able to write to CF 3 but not CF 2
|
||||
EXPECT_EQ(Put(0, "c0", "3").code(), Status::Code::kOk);
|
||||
EXPECT_EQ(Put(1, "c1", "3").code(), Status::Code::kOk);
|
||||
EXPECT_EQ(Put(2, "c2", "3").code(), Status::Code::kInvalidArgument);
|
||||
EXPECT_EQ(Put(3, "c3", "3").code(), Status::Code::kOk);
|
||||
|
||||
EXPECT_EQ(Get(0, "c0"), "3");
|
||||
EXPECT_EQ(Get(1, "c1"), "3");
|
||||
EXPECT_EQ(Get(2, "c2"), "NOT_FOUND");
|
||||
EXPECT_EQ(Get(3, "c3"), "3");
|
||||
|
||||
// disallow_memtable_writes not supported on default column family.
|
||||
// (Would be complicated to make a WriteBatch aware of the setting in order
|
||||
// to reject the write before entering the write path.)
|
||||
Destroy(options_allow);
|
||||
EXPECT_EQ(TryReopen(options_disallow).code(), Status::Code::kInvalidArgument);
|
||||
}
|
||||
|
||||
// TODO: re-enable after we provide finer-grained control for WAL tracking to
|
||||
// meet the needs of different use cases, durability levels and recovery modes.
|
||||
TEST_F(DBBasicTest, DISABLED_ManualWalSync) {
|
||||
|
||||
@@ -305,7 +305,6 @@ class ReadOnlyCacheWrapper : public CacheWrapper {
|
||||
} // anonymous namespace
|
||||
#endif // SNAPPY
|
||||
|
||||
|
||||
// Make sure that when options.block_cache is set, after a new table is
|
||||
// created its index/filter blocks are added to block cache.
|
||||
TEST_F(DBBlockCacheTest, IndexAndFilterBlocksOfNewTableAddedToCache) {
|
||||
@@ -836,8 +835,6 @@ TEST_F(DBBlockCacheTest, CacheCompressionDict) {
|
||||
}
|
||||
if (ZSTD_Supported()) {
|
||||
compression_types.push_back(kZSTD);
|
||||
} else if (ZSTDNotFinal_Supported()) {
|
||||
compression_types.push_back(kZSTDNotFinalCompression);
|
||||
}
|
||||
Random rnd(301);
|
||||
for (auto compression_type : compression_types) {
|
||||
@@ -1758,8 +1755,8 @@ class CacheKeyTest : public testing::Test {
|
||||
tp_.db_id = std::to_string(db_id_);
|
||||
tp_.orig_file_number = file_number;
|
||||
bool is_stable;
|
||||
std::string cur_session_id; // ignored
|
||||
uint64_t cur_file_number = 42; // ignored
|
||||
std::string cur_session_id; // ignored
|
||||
uint64_t cur_file_number = 42; // ignored
|
||||
OffsetableCacheKey rv;
|
||||
BlockBasedTable::SetupBaseCacheKey(&tp_, cur_session_id, cur_file_number,
|
||||
&rv, &is_stable);
|
||||
|
||||
@@ -2244,6 +2244,54 @@ TEST_F(DBBloomFilterTest, MemtableWholeKeyBloomFilterMultiGet) {
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, TestMemtableBloomAndWBM) {
|
||||
Options options = CurrentOptions();
|
||||
options.arena_block_size = 4096;
|
||||
options.write_buffer_size = 4000000;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(LRUCacheOptions(
|
||||
options.write_buffer_size * 3 /* capacity */, 1 /* num_shard_bits */,
|
||||
false /* strict_capacity_limit */, 0.0 /* high_pri_pool_ratio */,
|
||||
nullptr /* memory_allocator */, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata));
|
||||
|
||||
options.write_buffer_manager.reset(
|
||||
new WriteBufferManager(options.write_buffer_size, cache));
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
|
||||
const auto kDummyEntrySize =
|
||||
CacheReservationManagerImpl<CacheEntryRole::kMisc>::GetDummyEntrySize();
|
||||
|
||||
// Just the start of a memtable, no Bloom
|
||||
ASSERT_GE(cache->GetUsage(), options.arena_block_size);
|
||||
ASSERT_LE(cache->GetUsage(), kDummyEntrySize);
|
||||
|
||||
// Now enable memtable bloom filter
|
||||
const double kRatio = 0.25;
|
||||
options.memtable_prefix_bloom_size_ratio = kRatio;
|
||||
options.memtable_whole_key_filtering = true;
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("foo2", "bar2"));
|
||||
|
||||
// Expecting a memtable Bloom of ratio times write_buffer_size, memory tracked
|
||||
auto bloom_size = static_cast<size_t>(kRatio * options.write_buffer_size);
|
||||
ASSERT_GE(cache->GetUsage(), bloom_size);
|
||||
ASSERT_LE(cache->GetUsage(), bloom_size + 2U * kDummyEntrySize);
|
||||
|
||||
// Now get another memtable and test
|
||||
// Pin this memtable with an iterator
|
||||
std::unique_ptr<Iterator> iter{db_->NewIterator({})};
|
||||
iter->Seek("foo2");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo2", "bar2"));
|
||||
|
||||
// Expecting twice as much
|
||||
ASSERT_GE(cache->GetUsage(), 2U * bloom_size);
|
||||
ASSERT_LE(cache->GetUsage(), 2U * bloom_size + 2U * kDummyEntrySize);
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::pair<uint64_t, uint64_t> GetBloomStat(const Options& options, bool sst) {
|
||||
if (sst) {
|
||||
|
||||
+248
-25
@@ -127,6 +127,19 @@ class DBCompactionTestWithParam
|
||||
exclusive_manual_compaction_ = std::get<1>(GetParam());
|
||||
}
|
||||
|
||||
class TrivialMoveEventListener : public EventListener {
|
||||
public:
|
||||
explicit TrivialMoveEventListener(size_t expected_trivially_moved_files)
|
||||
: expected_trivially_moved_files_(expected_trivially_moved_files) {}
|
||||
void OnCompactionBegin(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(ci.stats.num_input_files_trivially_moved,
|
||||
expected_trivially_moved_files_);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t expected_trivially_moved_files_ = 0;
|
||||
};
|
||||
|
||||
// Required if inheriting from testing::WithParamInterface<>
|
||||
static void SetUpTestCase() {}
|
||||
static void TearDownTestCase() {}
|
||||
@@ -1301,6 +1314,9 @@ TEST_P(DBCompactionTestWithParam, TrivialMoveOneFile) {
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100000000;
|
||||
TrivialMoveEventListener* trivial_move_listener =
|
||||
new TrivialMoveEventListener(1 /*expected_trivially_moved_files*/);
|
||||
options.listeners.emplace_back(trivial_move_listener);
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -1361,6 +1377,10 @@ TEST_P(DBCompactionTestWithParam, TrivialMoveNonOverlappingFiles) {
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
// 8 is number of `ranges` that each is a non overlapping file.
|
||||
TrivialMoveEventListener* trivial_move_listener =
|
||||
new TrivialMoveEventListener(8 /*expected_trivially_moved_files*/);
|
||||
options.listeners.emplace_back(trivial_move_listener);
|
||||
options.write_buffer_size = 10 * 1024 * 1024;
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
|
||||
@@ -1408,6 +1428,11 @@ TEST_P(DBCompactionTestWithParam, TrivialMoveNonOverlappingFiles) {
|
||||
trivial_move = 0;
|
||||
non_trivial_move = 0;
|
||||
values.clear();
|
||||
options.listeners.clear();
|
||||
// Same ranges of files, but now overlapping, trivial move not applicable.
|
||||
TrivialMoveEventListener* trivial_move_listener2 =
|
||||
new TrivialMoveEventListener(0 /*expected_trivially_moved_files*/);
|
||||
options.listeners.emplace_back(trivial_move_listener2);
|
||||
DestroyAndReopen(options);
|
||||
// Same ranges as above but overlapping
|
||||
ranges = {
|
||||
@@ -1455,6 +1480,11 @@ TEST_P(DBCompactionTestWithParam, TrivialMoveTargetLevel) {
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
// Two non overlapping files in L0 trivialy moved:
|
||||
// file 1 [0 => 300], file 2 [600 => 700]
|
||||
TrivialMoveEventListener* trivial_move_listener1 =
|
||||
new TrivialMoveEventListener(2 /*expected_trivially_moved_files*/);
|
||||
options.listeners.emplace_back(trivial_move_listener1);
|
||||
options.write_buffer_size = 10 * 1024 * 1024;
|
||||
options.num_levels = 7;
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
@@ -2087,13 +2117,10 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
auto begin_str1 = Key(0), end_str1 = Key(100);
|
||||
auto begin_str2 = Key(100), end_str2 = Key(200);
|
||||
auto begin_str3 = Key(200), end_str3 = Key(299);
|
||||
Slice begin1(begin_str1), end1(end_str1);
|
||||
Slice begin2(begin_str2), end2(end_str2);
|
||||
Slice begin3(begin_str3), end3(end_str3);
|
||||
std::vector<RangePtr> ranges;
|
||||
ranges.emplace_back(&begin1, &end1);
|
||||
ranges.emplace_back(&begin2, &end2);
|
||||
ranges.emplace_back(&begin3, &end3);
|
||||
std::vector<RangeOpt> ranges;
|
||||
ranges.emplace_back(begin_str1, end_str1);
|
||||
ranges.emplace_back(begin_str2, end_str2);
|
||||
ranges.emplace_back(begin_str3, end_str3);
|
||||
ASSERT_OK(DeleteFilesInRanges(db_, db_->DefaultColumnFamily(),
|
||||
ranges.data(), ranges.size()));
|
||||
ASSERT_EQ("0,3,7", FilesPerLevel(0));
|
||||
@@ -2141,7 +2168,7 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
|
||||
// Delete all files.
|
||||
{
|
||||
RangePtr range;
|
||||
RangeOpt range;
|
||||
ASSERT_OK(DeleteFilesInRanges(db_, db_->DefaultColumnFamily(), &range, 1));
|
||||
ASSERT_EQ("", FilesPerLevel(0));
|
||||
|
||||
@@ -5856,6 +5883,26 @@ TEST_F(DBCompactionTest, CompactionStatsTest) {
|
||||
options.listeners.emplace_back(collector);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Verify that the internal statistics for num_running_compactions and
|
||||
// num_running_compaction_sorted_runs start and end at valid states
|
||||
uint64_t num_running_compactions = 0;
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumRunningCompactions,
|
||||
&num_running_compactions));
|
||||
ASSERT_EQ(num_running_compactions, 0);
|
||||
uint64_t num_running_compaction_sorted_runs = 0;
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty(DB::Properties::kNumRunningCompactionSortedRuns,
|
||||
&num_running_compaction_sorted_runs));
|
||||
ASSERT_EQ(num_running_compaction_sorted_runs, 0);
|
||||
// Check that the stat actually gets changed some time between the start and
|
||||
// end of compaction
|
||||
std::atomic<bool> sorted_runs_count_incremented = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionMergingIterator::UpdateInternalStats",
|
||||
[&](void*) { sorted_runs_count_incremented = true; });
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
for (int j = 0; j < 5000; j++) {
|
||||
ASSERT_OK(Put(std::to_string(j), std::string(1, 'A')));
|
||||
@@ -5869,6 +5916,19 @@ TEST_F(DBCompactionTest, CompactionStatsTest) {
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
|
||||
VerifyCompactionStats(*cfd, *collector);
|
||||
// There should be no more running compactions, and thus no more sorted runs
|
||||
// to process
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumRunningCompactions,
|
||||
&num_running_compactions));
|
||||
ASSERT_EQ(num_running_compactions, 0);
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty(DB::Properties::kNumRunningCompactionSortedRuns,
|
||||
&num_running_compaction_sorted_runs));
|
||||
ASSERT_EQ(num_running_compaction_sorted_runs, 0);
|
||||
ASSERT_TRUE(sorted_runs_count_incremented);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, SubcompactionEvent) {
|
||||
@@ -5879,6 +5939,9 @@ TEST_F(DBCompactionTest, SubcompactionEvent) {
|
||||
ASSERT_EQ(running_compactions_.find(ci.job_id),
|
||||
running_compactions_.end());
|
||||
running_compactions_.emplace(ci.job_id, std::unordered_set<int>());
|
||||
if (expected_num_l0_files_pre_compaction_ != -1) {
|
||||
ASSERT_EQ(expected_num_l0_files_pre_compaction_, ci.num_l0_files);
|
||||
}
|
||||
}
|
||||
|
||||
void OnCompactionCompleted(DB* /*db*/,
|
||||
@@ -5888,6 +5951,9 @@ TEST_F(DBCompactionTest, SubcompactionEvent) {
|
||||
ASSERT_NE(it, running_compactions_.end());
|
||||
ASSERT_EQ(it->second.size(), 0);
|
||||
running_compactions_.erase(it);
|
||||
if (expected_num_l0_files_post_compaction_ != -1) {
|
||||
ASSERT_EQ(expected_num_l0_files_post_compaction_, ci.num_l0_files);
|
||||
}
|
||||
}
|
||||
|
||||
void OnSubcompactionBegin(const SubcompactionJobInfo& si) override {
|
||||
@@ -5917,10 +5983,25 @@ TEST_F(DBCompactionTest, SubcompactionEvent) {
|
||||
return total_subcompaction_cnt_;
|
||||
}
|
||||
|
||||
void SetExpectedNumL0FilesPreCompaction(int num) {
|
||||
expected_num_l0_files_pre_compaction_ = num;
|
||||
}
|
||||
|
||||
void SetExpectedNumL0FilesPostCompaction(int num) {
|
||||
expected_num_l0_files_post_compaction_ = num;
|
||||
}
|
||||
|
||||
void ResetExpectedNumL0Files() {
|
||||
SetExpectedNumL0FilesPreCompaction(-1);
|
||||
SetExpectedNumL0FilesPostCompaction(-1);
|
||||
}
|
||||
|
||||
private:
|
||||
InstrumentedMutex mutex_;
|
||||
std::unordered_map<int, std::unordered_set<int>> running_compactions_;
|
||||
size_t total_subcompaction_cnt_ = 0;
|
||||
int expected_num_l0_files_pre_compaction_ = -1;
|
||||
int expected_num_l0_files_post_compaction_ = -1;
|
||||
};
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -5940,6 +6021,7 @@ TEST_F(DBCompactionTest, SubcompactionEvent) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
MoveFilesToLevel(2);
|
||||
ASSERT_EQ(FilesPerLevel(), "0,0,4");
|
||||
|
||||
// generate 2 files @ L1 which overlaps with L2 files
|
||||
for (int i = 0; i < 2; i++) {
|
||||
@@ -5949,11 +6031,18 @@ TEST_F(DBCompactionTest, SubcompactionEvent) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
listener->SetExpectedNumL0FilesPreCompaction(2 /* num */);
|
||||
listener->SetExpectedNumL0FilesPostCompaction(0 /* num */);
|
||||
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ(FilesPerLevel(), "0,2,4");
|
||||
|
||||
listener->ResetExpectedNumL0Files();
|
||||
|
||||
CompactRangeOptions comp_opts;
|
||||
comp_opts.max_subcompactions = 4;
|
||||
|
||||
listener->SetExpectedNumL0FilesPreCompaction(0 /* num */);
|
||||
Status s = dbfull()->CompactRange(comp_opts, nullptr, nullptr);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -5961,6 +6050,8 @@ TEST_F(DBCompactionTest, SubcompactionEvent) {
|
||||
ASSERT_EQ(listener->GetRunningCompactionCount(), 0);
|
||||
// and sub compaction is triggered
|
||||
ASSERT_GT(listener->GetTotalSubcompactionCount(), 0);
|
||||
|
||||
listener->ResetExpectedNumL0Files();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactFilesOutputRangeConflict) {
|
||||
@@ -6407,20 +6498,40 @@ TEST_F(DBCompactionTest, PersistRoundRobinCompactCursor) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(RoundRobinSubcompactionsAgainstPressureToken, PressureTokenTest) {
|
||||
// FIXME: the test is flaky and failing the assertion
|
||||
// ASSERT_EQ(num_planned_subcompactions, kNumSubcompactions);
|
||||
// It's likely a test set up issue, fix if we are to use RoubdRobin compaction.
|
||||
TEST_P(RoundRobinSubcompactionsAgainstPressureToken,
|
||||
DISABLED_PressureTokenTest) {
|
||||
const int kKeysPerBuffer = 100;
|
||||
const int kNumSubcompactions = 2;
|
||||
const int kFilesPerLevel = 50;
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
// SetBackgroundThreads() only starts the bg threads. Background
|
||||
// threads may not be immediately available after SetBackgroundThreads
|
||||
// returns. There are some initialization before they start waiting for
|
||||
// new jobs, see ThreadPoolImpl::Impl::BGThread(). Here is a hacky way
|
||||
// to wait until there are at least two background threads (thread id
|
||||
// starts from 0) start waiting to accept jobs.
|
||||
{"ThreadPoolImpl::BGThread::Start:th1", "WaitForThreadAvailable"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
env_->SetBackgroundThreads(kNumSubcompactions, Env::LOW);
|
||||
TEST_SYNC_POINT("WaitForThreadAvailable");
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 3;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
options.target_file_size_base = kKeysPerBuffer * 1024;
|
||||
options.compaction_pri = CompactionPri::kRoundRobin;
|
||||
// Target size is chosen so that filling the level with `kFilesPerLevel` files
|
||||
// will make it oversized by `kNumSubcompactions` files.
|
||||
// Pick a small target level size so that round-robin compaction will pick
|
||||
// more files to compact and trigger subcompactions. Actual number of
|
||||
// subcompactions will be limited by number of bg threads available.
|
||||
// Cannot be too small to cause compaction pressure. See
|
||||
// GetPendingCompactionBytesForCompactionSpeedup().
|
||||
options.max_bytes_for_level_base =
|
||||
(kFilesPerLevel - kNumSubcompactions) * kKeysPerBuffer * 1024;
|
||||
(kFilesPerLevel - 10) * kKeysPerBuffer * 1024;
|
||||
options.disable_auto_compactions = true;
|
||||
// Setup `kNumSubcompactions` threads but limited subcompactions so
|
||||
// that RoundRobin requires extra compactions from reserved threads
|
||||
@@ -6428,7 +6539,6 @@ TEST_P(RoundRobinSubcompactionsAgainstPressureToken, PressureTokenTest) {
|
||||
options.max_background_compactions = kNumSubcompactions;
|
||||
options.max_compaction_bytes = 100000000;
|
||||
DestroyAndReopen(options);
|
||||
env_->SetBackgroundThreads(kNumSubcompactions, Env::LOW);
|
||||
|
||||
Random rnd(301);
|
||||
for (int lvl = 2; lvl > 0; lvl--) {
|
||||
@@ -6446,21 +6556,34 @@ TEST_P(RoundRobinSubcompactionsAgainstPressureToken, PressureTokenTest) {
|
||||
ASSERT_EQ(kFilesPerLevel, NumTableFilesAtLevel(lvl, 0));
|
||||
}
|
||||
|
||||
bool compaction_num_input_file_verified = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
if (!compaction_num_input_file_verified) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
// In Round-Robin, # of subcompactions needed is the # of input files
|
||||
ASSERT_GT(compaction->num_input_files(0), 1);
|
||||
compaction_num_input_file_verified = true;
|
||||
}
|
||||
});
|
||||
|
||||
// This is a variable for making sure the following callback is called
|
||||
// and the assertions in it are indeed excuted.
|
||||
bool num_planned_subcompactions_verified = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::GenSubcompactionBoundaries:0", [&](void* arg) {
|
||||
uint64_t num_planned_subcompactions = *(static_cast<uint64_t*>(arg));
|
||||
if (grab_pressure_token_) {
|
||||
// `kNumSubcompactions` files are selected for round-robin under auto
|
||||
// compaction. The number of planned subcompaction is restricted by
|
||||
// the limited number of max_background_compactions
|
||||
ASSERT_EQ(num_planned_subcompactions, kNumSubcompactions);
|
||||
} else {
|
||||
ASSERT_EQ(num_planned_subcompactions, 1);
|
||||
if (!num_planned_subcompactions_verified) {
|
||||
uint64_t num_planned_subcompactions = *(static_cast<uint64_t*>(arg));
|
||||
if (grab_pressure_token_) {
|
||||
// `kNumSubcompactions` files are selected for round-robin under
|
||||
// auto compaction. The number of planned subcompaction is
|
||||
// restricted by the limited number of max_background_compactions
|
||||
ASSERT_EQ(num_planned_subcompactions, kNumSubcompactions);
|
||||
} else {
|
||||
ASSERT_EQ(num_planned_subcompactions, 1);
|
||||
}
|
||||
num_planned_subcompactions_verified = true;
|
||||
}
|
||||
num_planned_subcompactions_verified = true;
|
||||
});
|
||||
|
||||
// The following 3 dependencies have to be added to ensure the auto
|
||||
@@ -6911,7 +7034,8 @@ TEST_F(DBCompactionTest, ManualCompactionMax) {
|
||||
DestroyAndReopen(opts);
|
||||
generate_sst_func();
|
||||
uint64_t total_size = (l1_avg_size * 10) + (l2_avg_size * 100);
|
||||
opts.max_compaction_bytes = total_size / num_split;
|
||||
// Slightly inflate max_compaction_bytes since it's usually a strict bound.
|
||||
opts.max_compaction_bytes = total_size / num_split + l2_avg_size * 10;
|
||||
opts.target_file_size_base = total_size / num_split;
|
||||
Reopen(opts);
|
||||
num_compactions.store(0);
|
||||
@@ -6933,8 +7057,10 @@ TEST_F(DBCompactionTest, ManualCompactionMax) {
|
||||
DestroyAndReopen(opts);
|
||||
generate_sst_func();
|
||||
total_size = (l1_avg_size * 10) + (l2_avg_size * 100);
|
||||
// Slightly inflate max_compaction_bytes since it's usually a strict bound.
|
||||
Status s = db_->SetOptions(
|
||||
{{"max_compaction_bytes", std::to_string(total_size / num_split)},
|
||||
{{"max_compaction_bytes",
|
||||
std::to_string(total_size / num_split + 10 * l2_avg_size)},
|
||||
{"target_file_size_base", std::to_string(total_size / num_split)}});
|
||||
ASSERT_OK(s);
|
||||
|
||||
@@ -10404,7 +10530,7 @@ TEST_F(DBCompactionTest, NumberOfSubcompactions) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, VerifyRecordCount) {
|
||||
TEST_F(DBCompactionTest, VerifyInputRecordCount) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
@@ -10442,6 +10568,103 @@ TEST_F(DBCompactionTest, VerifyRecordCount) {
|
||||
ASSERT_TRUE(std::strstr(s.getState(), expect));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, VerifyOutputRecordCountBlockBasedTable) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
options.compaction_verify_record_count = true;
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
|
||||
// Create 2 overlapping L0 files
|
||||
for (int i = 1; i < 20; i += 2) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), Key(10), Key(15)));
|
||||
|
||||
for (int i = 0; i < 20; i += 2) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Skip adding every 7th key in the output table
|
||||
int num_iter = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::Add::skip", [&](void* skip) {
|
||||
num_iter++;
|
||||
if (num_iter % 7 == 0) {
|
||||
*(bool*)skip = true;
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Status s = db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
const char* expect =
|
||||
"Number of keys in compaction output SST files does not match number of "
|
||||
"keys added.";
|
||||
ASSERT_TRUE(std::strstr(s.getState(), expect));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, VerifyOutputRecordCountPlainTable) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
options.compaction_verify_record_count = true;
|
||||
|
||||
PlainTableOptions plain_table_options;
|
||||
plain_table_options.user_key_len = 0;
|
||||
plain_table_options.bloom_bits_per_key = 2;
|
||||
plain_table_options.hash_table_ratio = 0.8;
|
||||
plain_table_options.index_sparseness = 3;
|
||||
plain_table_options.huge_page_tlb_size = 0;
|
||||
plain_table_options.encoding_type = kPrefix;
|
||||
plain_table_options.full_scan_mode = false;
|
||||
plain_table_options.store_index_in_file = false;
|
||||
|
||||
options.table_factory.reset(NewPlainTableFactory(plain_table_options));
|
||||
options.memtable_factory.reset(NewHashLinkListRepFactory(4, 0, 3, true));
|
||||
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(8));
|
||||
options.allow_mmap_reads = false;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
options.unordered_write = false;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
|
||||
// Create 2 overlapping L0 files
|
||||
for (int i = 1; i < 20; i += 2) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
for (int i = 0; i < 20; i += 2) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Skip adding every 7th key in the output table
|
||||
int num_iter = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"PlainTableBuilder::Add::skip", [&](void* skip) {
|
||||
num_iter++;
|
||||
if (num_iter % 7 == 0) {
|
||||
*(bool*)skip = true;
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Status s = db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
const char* expect =
|
||||
"Number of keys in compaction output SST files does not match number of "
|
||||
"keys added.";
|
||||
ASSERT_TRUE(std::strstr(s.getState(), expect));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ErrorWhenReadFileHead) {
|
||||
// This is to test a bug that is fixed in
|
||||
// https://github.com/facebook/rocksdb/pull/11782.
|
||||
|
||||
@@ -491,7 +491,6 @@ TEST_F(DBTestDynamicLevel, DISABLED_MigrateToDynamicLevelMaxBytesBase) {
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -25,7 +26,6 @@ class DBEncryptionTest : public DBTestBase {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TEST_F(DBEncryptionTest, CheckEncrypted) {
|
||||
ASSERT_OK(Put("foo567", "v1.fetdq"));
|
||||
ASSERT_OK(Put("bar123", "v2.dfgkjdfghsd"));
|
||||
@@ -116,7 +116,6 @@ TEST_F(DBEncryptionTest, ReadEmptyFile) {
|
||||
ASSERT_TRUE(data.empty());
|
||||
}
|
||||
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -266,7 +265,7 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
continue;
|
||||
}
|
||||
VersionStorageInfo& vsi = *cfd->current()->storage_info();
|
||||
auto& cf_paths = cfd->ioptions()->cf_paths;
|
||||
auto& cf_paths = cfd->ioptions().cf_paths;
|
||||
|
||||
auto GetDir = [&](size_t path_id) {
|
||||
// Matching TableFileName() behavior
|
||||
|
||||
@@ -248,7 +248,7 @@ Status CompactedDBImpl::Init(const Options& options) {
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Open(const Options& options, const std::string& dbname,
|
||||
DB** dbptr) {
|
||||
std::unique_ptr<DB>* dbptr) {
|
||||
*dbptr = nullptr;
|
||||
|
||||
if (options.max_open_files != -1) {
|
||||
@@ -267,7 +267,7 @@ Status CompactedDBImpl::Open(const Options& options, const std::string& dbname,
|
||||
ROCKS_LOG_INFO(db->immutable_db_options_.info_log,
|
||||
"Opened the db as fully compacted mode");
|
||||
LogFlush(db->immutable_db_options_.info_log);
|
||||
*dbptr = db.release();
|
||||
*dbptr = std::move(db);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class CompactedDBImpl : public DBImpl {
|
||||
~CompactedDBImpl() override;
|
||||
|
||||
static Status Open(const Options& options, const std::string& dbname,
|
||||
DB** dbptr);
|
||||
std::unique_ptr<DB>* dbptr);
|
||||
|
||||
// Implementations of the DB interface
|
||||
using DB::Get;
|
||||
|
||||
+371
-382
File diff suppressed because it is too large.
Load diff
+149
-44
@@ -162,6 +162,23 @@ class Directories {
|
||||
std::unique_ptr<FSDirectory> wal_dir_;
|
||||
};
|
||||
|
||||
struct DBOpenLogRecordReadReporter : public log::Reader::Reporter {
|
||||
Env* env;
|
||||
Logger* info_log;
|
||||
const char* fname;
|
||||
Status* status; // nullptr if immutable_db_options_.paranoid_checks==false
|
||||
bool* old_log_record;
|
||||
void Corruption(size_t bytes, const Status& s,
|
||||
uint64_t log_number = kMaxSequenceNumber) override;
|
||||
|
||||
void OldLogRecord(size_t bytes) override;
|
||||
|
||||
uint64_t GetCorruptedLogNumber() const { return corrupted_log_number_; }
|
||||
|
||||
private:
|
||||
uint64_t corrupted_log_number_ = kMaxSequenceNumber;
|
||||
};
|
||||
|
||||
// While DB is the public interface of RocksDB, and DBImpl is the actual
|
||||
// class implementing it. It's the entrance of the core RocksdB engine.
|
||||
// All other DB implementations, e.g. TransactionDB, BlobDB, etc, wrap a
|
||||
@@ -524,9 +541,8 @@ class DBImpl : public DB {
|
||||
SequenceNumber seq_number, std::unique_ptr<TransactionLogIterator>* iter,
|
||||
const TransactionLogIterator::ReadOptions& read_options =
|
||||
TransactionLogIterator::ReadOptions()) override;
|
||||
Status DeleteFile(std::string name) override;
|
||||
Status DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
const RangeOpt* ranges, size_t n,
|
||||
bool include_end = true);
|
||||
|
||||
void GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata) override;
|
||||
@@ -635,6 +651,11 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* column_family, const Range* range, std::size_t n,
|
||||
TablePropertiesCollection* props) override;
|
||||
|
||||
Status GetPropertiesOfTablesByLevel(
|
||||
ColumnFamilyHandle* column_family,
|
||||
std::vector<std::unique_ptr<TablePropertiesCollection>>* props_by_level)
|
||||
override;
|
||||
|
||||
// ---- End of implementations of the DB interface ----
|
||||
SystemClock* GetSystemClock() const;
|
||||
|
||||
@@ -939,13 +960,6 @@ class DBImpl : public DB {
|
||||
return num_running_flushes_;
|
||||
}
|
||||
|
||||
// Returns the number of currently running compactions.
|
||||
// REQUIREMENT: mutex_ must be held when calling this function.
|
||||
int num_running_compactions() {
|
||||
mutex_.AssertHeld();
|
||||
return num_running_compactions_;
|
||||
}
|
||||
|
||||
const WriteController& write_controller() { return write_controller_; }
|
||||
|
||||
// hollow transactions shell used for recovery.
|
||||
@@ -1081,9 +1095,10 @@ class DBImpl : public DB {
|
||||
// This is to be used only by internal rocksdb classes.
|
||||
static Status Open(const DBOptions& db_options, const std::string& name,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
const bool seq_per_batch, const bool batch_per_txn,
|
||||
const bool is_retry, bool* can_retry);
|
||||
std::vector<ColumnFamilyHandle*>* handles,
|
||||
std::unique_ptr<DB>* dbptr, const bool seq_per_batch,
|
||||
const bool batch_per_txn, const bool is_retry,
|
||||
bool* can_retry);
|
||||
|
||||
static IOStatus CreateAndNewDirectory(
|
||||
FileSystem* fs, const std::string& dirname,
|
||||
@@ -1257,27 +1272,18 @@ class DBImpl : public DB {
|
||||
// flush LOG out of application buffer
|
||||
void FlushInfoLog();
|
||||
|
||||
// record current sequence number to time mapping. If
|
||||
// populate_historical_seconds > 0 then pre-populate all the
|
||||
// sequence numbers from [1, last] to map to [now minus
|
||||
// populate_historical_seconds, now].
|
||||
void RecordSeqnoToTimeMapping(uint64_t populate_historical_seconds);
|
||||
// For the background timer job
|
||||
void RecordSeqnoToTimeMapping();
|
||||
|
||||
// Everytime DB's seqno to time mapping changed (which already hold the db
|
||||
// mutex), we install a new SuperVersion in each column family with a shared
|
||||
// copy of the new mapping while holding the db mutex.
|
||||
// This is done for all column families even though the column family does not
|
||||
// explicitly enabled the
|
||||
// `preclude_last_level_data_seconds` or `preserve_internal_time_seconds`
|
||||
// features.
|
||||
// This mapping supports iterators to fulfill the
|
||||
// "rocksdb.iterator.write-time" iterator property for entries in memtables.
|
||||
//
|
||||
// Since this new SuperVersion doesn't involve an LSM tree shape change, we
|
||||
// don't schedule work after installing this SuperVersion. It returns the used
|
||||
// `SuperVersionContext` for clean up after release mutex.
|
||||
void InstallSeqnoToTimeMappingInSV(
|
||||
std::vector<SuperVersionContext>* sv_contexts);
|
||||
// REQUIRES: DB mutex held
|
||||
std::pair<SequenceNumber, uint64_t> GetSeqnoToTimeSample() const;
|
||||
|
||||
// REQUIRES: DB mutex held or during open
|
||||
void EnsureSeqnoToTimeMapping(const MinAndMaxPreserveSeconds& preserve_secs);
|
||||
|
||||
// Only called during open
|
||||
void PrepopulateSeqnoToTimeMapping(
|
||||
const MinAndMaxPreserveSeconds& preserve_secs);
|
||||
|
||||
// Interface to block and signal the DB in case of stalling writes by
|
||||
// WriteBufferManager. Each DBImpl object contains ptr to WBMStallInterface.
|
||||
@@ -1440,7 +1446,6 @@ class DBImpl : public DB {
|
||||
uint32_t size = static_cast<uint32_t>(map_.size());
|
||||
map_.emplace(cfd->GetID(), size);
|
||||
cfds_.emplace_back(cfd);
|
||||
mutable_cf_opts_.emplace_back(cfd->GetLatestMutableCFOptions());
|
||||
edit_lists_.emplace_back(autovector<VersionEdit*>());
|
||||
}
|
||||
uint32_t i = map_[cfd->GetID()];
|
||||
@@ -1449,7 +1454,6 @@ class DBImpl : public DB {
|
||||
|
||||
std::unordered_map<uint32_t, uint32_t> map_; // cf_id to index;
|
||||
autovector<ColumnFamilyData*> cfds_;
|
||||
autovector<const MutableCFOptions*> mutable_cf_opts_;
|
||||
autovector<autovector<VersionEdit*>> edit_lists_;
|
||||
// All existing data files (SST files and Blob files) found during DB::Open.
|
||||
std::vector<std::string> existing_data_files_;
|
||||
@@ -1910,8 +1914,8 @@ class DBImpl : public DB {
|
||||
const InternalKey* begin = nullptr; // nullptr means beginning of key range
|
||||
const InternalKey* end = nullptr; // nullptr means end of key range
|
||||
InternalKey* manual_end = nullptr; // how far we are compacting
|
||||
InternalKey tmp_storage; // Used to keep track of compaction progress
|
||||
InternalKey tmp_storage1; // Used to keep track of compaction progress
|
||||
InternalKey tmp_storage; // Used to keep track of compaction progress
|
||||
InternalKey tmp_storage1; // Used to keep track of compaction progress
|
||||
|
||||
// When the user provides a canceled pointer in CompactRangeOptions, the
|
||||
// above varaibe is the reference of the user-provided
|
||||
@@ -1971,7 +1975,7 @@ class DBImpl : public DB {
|
||||
|
||||
// Follow-up work to user creating a column family or (families)
|
||||
Status WrapUpCreateColumnFamilies(
|
||||
const ReadOptions& read_options, const WriteOptions& write_options,
|
||||
const WriteOptions& write_options,
|
||||
const std::vector<const ColumnFamilyOptions*>& cf_options);
|
||||
|
||||
Status DropColumnFamilyImpl(ColumnFamilyHandle* column_family);
|
||||
@@ -2043,6 +2047,99 @@ class DBImpl : public DB {
|
||||
bool is_retry, bool* corrupted_log_found,
|
||||
RecoveryContext* recovery_ctx);
|
||||
|
||||
void SetupLogFilesRecovery(
|
||||
const std::vector<uint64_t>& wal_numbers,
|
||||
std::unordered_map<int, VersionEdit>* version_edits, int* job_id,
|
||||
uint64_t* min_wal_number);
|
||||
|
||||
Status ProcessLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
bool read_only, bool is_retry, uint64_t min_wal_number,
|
||||
int job_id, SequenceNumber* next_sequence,
|
||||
std::unordered_map<int, VersionEdit>* version_edits,
|
||||
bool* corrupted_wal_found,
|
||||
RecoveryContext* recovery_ctx);
|
||||
|
||||
Status ProcessLogFile(
|
||||
uint64_t wal_number, uint64_t min_wal_number, bool is_retry,
|
||||
bool read_only, int job_id, SequenceNumber* next_sequence,
|
||||
bool* stop_replay_for_corruption, bool* stop_replay_by_wal_filter,
|
||||
uint64_t* corrupted_wal_number, bool* corrupted_wal_found,
|
||||
std::unordered_map<int, VersionEdit>* version_edits, bool* flushed,
|
||||
PredecessorWALInfo& predecessor_wal_info);
|
||||
|
||||
void SetupLogFileProcessing(uint64_t wal_number);
|
||||
|
||||
Status InitializeLogReader(uint64_t wal_number, bool is_retry,
|
||||
std::string& fname,
|
||||
|
||||
bool stop_replay_for_corruption,
|
||||
uint64_t min_wal_number,
|
||||
const PredecessorWALInfo& predecessor_wal_info,
|
||||
bool* const old_log_record,
|
||||
Status* const reporter_status,
|
||||
DBOpenLogRecordReadReporter* reporter,
|
||||
std::unique_ptr<log::Reader>& reader);
|
||||
Status ProcessLogRecord(
|
||||
Slice record, const std::unique_ptr<log::Reader>& reader,
|
||||
const UnorderedMap<uint32_t, size_t>& running_ts_sz, uint64_t wal_number,
|
||||
const std::string& fname, bool read_only, int job_id,
|
||||
const std::function<void()>& logFileDropped,
|
||||
DBOpenLogRecordReadReporter* reporter, uint64_t* record_checksum,
|
||||
SequenceNumber* last_seqno_observed, SequenceNumber* next_sequence,
|
||||
bool* stop_replay_for_corruption, Status* status,
|
||||
bool* stop_replay_by_wal_filter,
|
||||
std::unordered_map<int, VersionEdit>* version_edits, bool* flushed);
|
||||
|
||||
Status InitializeWriteBatchForLogRecord(
|
||||
Slice record, const std::unique_ptr<log::Reader>& reader,
|
||||
const UnorderedMap<uint32_t, size_t>& running_ts_sz, WriteBatch* batch,
|
||||
std::unique_ptr<WriteBatch>& new_batch, WriteBatch*& batch_to_use,
|
||||
uint64_t* record_checksum);
|
||||
|
||||
void MaybeReviseStopReplayForCorruption(
|
||||
SequenceNumber sequence, SequenceNumber const* const next_sequence,
|
||||
bool* stop_replay_for_corruption);
|
||||
|
||||
Status InsertLogRecordToMemtable(WriteBatch* batch_to_use,
|
||||
uint64_t wal_number,
|
||||
SequenceNumber* next_sequence,
|
||||
bool* has_valid_writes);
|
||||
|
||||
Status MaybeWriteLevel0TableForRecovery(
|
||||
bool has_valid_writes, bool read_only, uint64_t wal_number, int job_id,
|
||||
SequenceNumber const* const next_sequence,
|
||||
std::unordered_map<int, VersionEdit>* version_edits, bool* flushed);
|
||||
|
||||
Status HandleNonOkStatusOrOldLogRecord(
|
||||
uint64_t wal_number, SequenceNumber const* const next_sequence,
|
||||
Status status, const DBOpenLogRecordReadReporter& reporter,
|
||||
bool* old_log_record, bool* stop_replay_for_corruption,
|
||||
uint64_t* corrupted_wal_number, bool* corrupted_wal_found);
|
||||
|
||||
Status UpdatePredecessorWALInfo(uint64_t wal_number,
|
||||
const SequenceNumber last_seqno_observed,
|
||||
const std::string& fname,
|
||||
PredecessorWALInfo& predecessor_wal_info);
|
||||
|
||||
void FinishLogFileProcessing(const Status& status,
|
||||
const SequenceNumber* next_sequence);
|
||||
|
||||
// Return `Status::Corruption()` when `stop_replay_for_corruption == true` and
|
||||
// exits inconsistency between SST and WAL data
|
||||
Status MaybeHandleStopReplayForCorruptionForInconsistency(
|
||||
bool stop_replay_for_corruption, uint64_t corrupted_wal_number);
|
||||
|
||||
Status MaybeFlushFinalMemtableOrRestoreActiveLogFiles(
|
||||
const std::vector<uint64_t>& wal_numbers, bool read_only, int job_id,
|
||||
bool flushed, std::unordered_map<int, VersionEdit>* version_edits,
|
||||
RecoveryContext* recovery_ctx);
|
||||
|
||||
// Check that DB sequence number is not set back during recovery between
|
||||
// replaying of WAL files and between replaying of WriteBatches.
|
||||
Status CheckSeqnoNotSetBackDuringRecovery(SequenceNumber prev_next_seqno,
|
||||
SequenceNumber current_next_seqno);
|
||||
|
||||
void FinishLogFilesRecovery(int job_id, const Status& status);
|
||||
// The following two methods are used to flush a memtable to
|
||||
// storage. The first one is used at database RecoveryTime (when the
|
||||
// database is opened) and is heavyweight because it holds the mutex
|
||||
@@ -2222,7 +2319,8 @@ class DBImpl : public DB {
|
||||
const WriteOptions& write_options,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
uint64_t* log_size,
|
||||
LogFileNumberSize& log_file_number_size);
|
||||
LogFileNumberSize& log_file_number_size,
|
||||
SequenceNumber sequence);
|
||||
|
||||
IOStatus WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
@@ -2353,9 +2451,7 @@ class DBImpl : public DB {
|
||||
// Cancel scheduled periodic tasks
|
||||
Status CancelPeriodicTaskScheduler();
|
||||
|
||||
Status RegisterRecordSeqnoTimeWorker(const ReadOptions& read_options,
|
||||
const WriteOptions& write_options,
|
||||
bool is_new_db);
|
||||
Status RegisterRecordSeqnoTimeWorker();
|
||||
|
||||
void PrintStatistics();
|
||||
|
||||
@@ -2421,14 +2517,22 @@ class DBImpl : public DB {
|
||||
|
||||
// Background threads call this function, which is just a wrapper around
|
||||
// the InstallSuperVersion() function. Background threads carry
|
||||
// sv_context which can have new_superversion already
|
||||
// allocated.
|
||||
// sv_context to allow allocation of SuperVersion object outside of holding
|
||||
// the DB mutex.
|
||||
// All ColumnFamily state changes go through this function. Here we analyze
|
||||
// the new state and we schedule background work if we detect that the new
|
||||
// state needs flush or compaction.
|
||||
// See also InstallSuperVersionForConfigChange().
|
||||
void InstallSuperVersionAndScheduleWork(
|
||||
ColumnFamilyData* cfd, SuperVersionContext* sv_context,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
std::optional<std::shared_ptr<SeqnoToTimeMapping>>
|
||||
new_seqno_to_time_mapping = {});
|
||||
|
||||
// A variant of InstallSuperVersionAndScheduleWork() that must be used for
|
||||
// new CFs or for changes to mutable_cf_options. This is so that it can
|
||||
// update seqno_to_time_mapping cached for the new SuperVersion as relevant.
|
||||
void InstallSuperVersionForConfigChange(ColumnFamilyData* cfd,
|
||||
SuperVersionContext* sv_context);
|
||||
|
||||
bool GetIntPropertyInternal(ColumnFamilyData* cfd,
|
||||
const DBPropertyInfo& property_info,
|
||||
@@ -2467,6 +2571,7 @@ class DBImpl : public DB {
|
||||
|
||||
IOStatus CreateWAL(const WriteOptions& write_options, uint64_t log_file_num,
|
||||
uint64_t recycle_log_number, size_t preallocate_block_size,
|
||||
const PredecessorWALInfo& predecessor_wal_info,
|
||||
log::Writer** new_log);
|
||||
|
||||
// Validate self-consistency of DB options
|
||||
|
||||
@@ -66,7 +66,7 @@ bool DBImpl::RequestCompactionToken(ColumnFamilyData* cfd, bool force,
|
||||
LogBuffer* log_buffer) {
|
||||
assert(*token == nullptr);
|
||||
auto limiter = static_cast<ConcurrentTaskLimiterImpl*>(
|
||||
cfd->ioptions()->compaction_thread_limiter.get());
|
||||
cfd->ioptions().compaction_thread_limiter.get());
|
||||
if (limiter == nullptr) {
|
||||
return true;
|
||||
}
|
||||
@@ -99,7 +99,7 @@ bool DBImpl::ShouldRescheduleFlushRequestToRetainUDT(
|
||||
// Check if holding on the flush will cause entering write stall mode.
|
||||
// Write stall entered because of the accumulation of write buffers can be
|
||||
// alleviated if we continue with the flush instead of postponing it.
|
||||
const auto& mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
const auto& mutable_cf_options = cfd->GetLatestMutableCFOptions();
|
||||
|
||||
// Use the same criteria as WaitUntilFlushWouldNotStallWrites does w.r.t
|
||||
// defining what a write stall is about to happen means. If this uses a
|
||||
@@ -116,7 +116,7 @@ bool DBImpl::ShouldRescheduleFlushRequestToRetainUDT(
|
||||
cfd->GetUnflushedMemTableCountForWriteStallCheck(),
|
||||
/*num_l0_files=*/0,
|
||||
/*num_compaction_needed_bytes=*/0, mutable_cf_options,
|
||||
*cfd->ioptions())
|
||||
cfd->ioptions())
|
||||
.first;
|
||||
if (write_stall != WriteStallCondition::kNormal) {
|
||||
return false;
|
||||
@@ -213,7 +213,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
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_,
|
||||
GetCompressionFlush(cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
true /* sync_output_directory */, true /* write_manifest */, thread_pri,
|
||||
io_tracer_, cfd->GetSuperVersion()->ShareSeqnoToTimeMapping(), db_id_,
|
||||
@@ -299,8 +299,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, superversion_context,
|
||||
mutable_cf_options);
|
||||
InstallSuperVersionAndScheduleWork(cfd, superversion_context);
|
||||
if (made_progress) {
|
||||
*made_progress = true;
|
||||
}
|
||||
@@ -369,7 +368,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
if (sfm) {
|
||||
// Notify sst_file_manager that a new file was added
|
||||
std::string file_path = MakeTableFileName(
|
||||
cfd->ioptions()->cf_paths[0].path, file_meta.fd.GetNumber());
|
||||
cfd->ioptions().cf_paths[0].path, file_meta.fd.GetNumber());
|
||||
// TODO (PR7798). We should only add the file to the FileManager if it
|
||||
// exists. Otherwise, some tests may fail. Ignore the error in the
|
||||
// interim.
|
||||
@@ -404,7 +403,8 @@ Status DBImpl::FlushMemTablesToOutputFiles(
|
||||
const auto& bg_flush_arg = bg_flush_args[0];
|
||||
ColumnFamilyData* cfd = bg_flush_arg.cfd_;
|
||||
// intentional infrequent copy for each flush
|
||||
MutableCFOptions mutable_cf_options_copy = *cfd->GetLatestMutableCFOptions();
|
||||
const MutableCFOptions mutable_cf_options_copy =
|
||||
cfd->GetLatestMutableCFOptions();
|
||||
SuperVersionContext* superversion_context =
|
||||
bg_flush_arg.superversion_context_;
|
||||
FlushReason flush_reason = bg_flush_arg.flush_reason_;
|
||||
@@ -461,7 +461,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
for (int i = 0; i < num_cfs; ++i) {
|
||||
auto cfd = cfds[i];
|
||||
FSDirectory* data_dir = GetDataDir(cfd, 0U);
|
||||
const std::string& curr_path = cfd->ioptions()->cf_paths[0].path;
|
||||
const std::string& curr_path = cfd->ioptions().cf_paths[0].path;
|
||||
|
||||
// Add to distinct output directories if eligible. Use linear search. Since
|
||||
// the number of elements in the vector is not large, performance should be
|
||||
@@ -478,7 +478,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
distinct_output_dirs.emplace_back(data_dir);
|
||||
}
|
||||
|
||||
all_mutable_cf_options.emplace_back(*cfd->GetLatestMutableCFOptions());
|
||||
all_mutable_cf_options.emplace_back(cfd->GetLatestMutableCFOptions());
|
||||
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.back();
|
||||
uint64_t max_memtable_id = bg_flush_args[i].max_memtable_id_;
|
||||
FlushReason flush_reason = bg_flush_args[i].flush_reason_;
|
||||
@@ -488,7 +488,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
&shutting_down_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, job_context, flush_reason, log_buffer,
|
||||
directories_.GetDbDir(), data_dir,
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
GetCompressionFlush(cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
false /* sync_output_directory */, false /* write_manifest */,
|
||||
thread_pri, io_tracer_,
|
||||
@@ -754,7 +754,6 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
if (s.ok()) {
|
||||
autovector<ColumnFamilyData*> tmp_cfds;
|
||||
autovector<const autovector<ReadOnlyMemTable*>*> mems_list;
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
autovector<FileMetaData*> tmp_file_meta;
|
||||
autovector<std::list<std::unique_ptr<FlushJobInfo>>*>
|
||||
committed_flush_jobs_info;
|
||||
@@ -763,7 +762,6 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
if (!cfds[i]->IsDropped() && !mems.empty()) {
|
||||
tmp_cfds.emplace_back(cfds[i]);
|
||||
mems_list.emplace_back(&mems);
|
||||
mutable_cf_options_list.emplace_back(&all_mutable_cf_options[i]);
|
||||
tmp_file_meta.emplace_back(&file_meta[i]);
|
||||
committed_flush_jobs_info.emplace_back(
|
||||
jobs[i]->GetCommittedFlushJobsInfo());
|
||||
@@ -771,8 +769,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
}
|
||||
|
||||
s = InstallMemtableAtomicFlushResults(
|
||||
nullptr /* imm_lists */, tmp_cfds, mutable_cf_options_list, mems_list,
|
||||
versions_.get(), &logs_with_prep_tracker_, &mutex_, tmp_file_meta,
|
||||
nullptr /* imm_lists */, tmp_cfds, mems_list, versions_.get(),
|
||||
&logs_with_prep_tracker_, &mutex_, tmp_file_meta,
|
||||
committed_flush_jobs_info, &job_context->memtables_to_free,
|
||||
directories_.GetDbDir(), log_buffer);
|
||||
}
|
||||
@@ -786,9 +784,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
if (cfds[i]->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
InstallSuperVersionAndScheduleWork(cfds[i],
|
||||
&job_context->superversion_contexts[i],
|
||||
all_mutable_cf_options[i]);
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfds[i], &job_context->superversion_contexts[i]);
|
||||
|
||||
const std::string& column_family_name = cfds[i]->GetName();
|
||||
|
||||
@@ -834,7 +831,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
jobs[i]->GetCommittedFlushJobsInfo());
|
||||
if (sfm) {
|
||||
std::string file_path = MakeTableFileName(
|
||||
cfds[i]->ioptions()->cf_paths[0].path, file_meta[i].fd.GetNumber());
|
||||
cfds[i]->ioptions().cf_paths[0].path, file_meta[i].fd.GetNumber());
|
||||
// TODO (PR7798). We should only add the file to the FileManager if it
|
||||
// exists. Otherwise, some tests may fail. Ignore the error in the
|
||||
// interim.
|
||||
@@ -908,7 +905,7 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
// go to L0 in the future.
|
||||
const uint64_t file_number = file_meta->fd.GetNumber();
|
||||
info.file_path =
|
||||
MakeTableFileName(cfd->ioptions()->cf_paths[0].path, file_number);
|
||||
MakeTableFileName(cfd->ioptions().cf_paths[0].path, file_number);
|
||||
info.file_number = file_number;
|
||||
info.thread_id = env_->GetThreadID();
|
||||
info.job_id = job_id;
|
||||
@@ -984,7 +981,8 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
|
||||
std::string begin_str, end_str;
|
||||
auto [begin, end] =
|
||||
MaybeAddTimestampsToRange(begin_without_ts, end_without_ts, ts_sz,
|
||||
MaybeAddTimestampsToRange(OptSlice::CopyFromPtr(begin_without_ts),
|
||||
OptSlice::CopyFromPtr(end_without_ts), ts_sz,
|
||||
&begin_str, &end_str, false /*exclusive_end*/);
|
||||
|
||||
return CompactRangeInternal(
|
||||
@@ -1040,9 +1038,8 @@ Status DBImpl::IncreaseFullHistoryTsLowImpl(ColumnFamilyData* cfd,
|
||||
return Status::InvalidArgument(oss.str());
|
||||
}
|
||||
|
||||
Status s = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
read_options, write_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
Status s = versions_->LogAndApply(cfd, read_options, write_options, &edit,
|
||||
&mutex_, directories_.GetDbDir());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -1066,11 +1063,11 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
|
||||
if (options.target_path_id >= cfd->ioptions()->cf_paths.size()) {
|
||||
if (options.target_path_id >= cfd->ioptions().cf_paths.size()) {
|
||||
return Status::InvalidArgument("Invalid target path ID");
|
||||
}
|
||||
if (options.change_level &&
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
cfd->ioptions().compaction_style == kCompactionStyleFIFO) {
|
||||
return Status::NotSupported(
|
||||
"FIFO compaction does not support change_level.");
|
||||
}
|
||||
@@ -1122,7 +1119,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
constexpr int kInvalidLevel = -1;
|
||||
int final_output_level = kInvalidLevel;
|
||||
bool exclusive = options.exclusive_manual_compaction;
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal &&
|
||||
if (cfd->ioptions().compaction_style == kCompactionStyleUniversal &&
|
||||
cfd->NumberLevels() > 1) {
|
||||
// Always compact all files together.
|
||||
final_output_level = cfd->NumberLevels() - 1;
|
||||
@@ -1134,7 +1131,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
final_output_level, options, begin, end, exclusive,
|
||||
false /* disable_trivial_move */,
|
||||
std::numeric_limits<uint64_t>::max(), trim_ts);
|
||||
} else if (cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
} else if (cfd->ioptions().compaction_style == kCompactionStyleFIFO) {
|
||||
// FIFOCompactionPicker::CompactRange() will ignore the input key range
|
||||
// [begin, end] and just try to pick compaction based on the configured
|
||||
// option `compaction_options_fifo`. So we skip checking if [begin, end]
|
||||
@@ -1152,7 +1149,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
|
||||
// Might need to query the partitioner
|
||||
SstPartitionerFactory* partitioner_factory =
|
||||
current_version->cfd()->ioptions()->sst_partitioner_factory.get();
|
||||
current_version->cfd()->ioptions().sst_partitioner_factory.get();
|
||||
std::unique_ptr<SstPartitioner> partitioner;
|
||||
if (partitioner_factory && begin != nullptr && end != nullptr) {
|
||||
SstPartitioner::Context context;
|
||||
@@ -1228,7 +1225,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
if (s.ok() && first_overlapped_level != kInvalidLevel) {
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal) {
|
||||
if (cfd->ioptions().compaction_style == kCompactionStyleUniversal) {
|
||||
assert(first_overlapped_level == 0);
|
||||
s = RunManualCompaction(
|
||||
cfd, first_overlapped_level, first_overlapped_level, options, begin,
|
||||
@@ -1237,7 +1234,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
trim_ts);
|
||||
final_output_level = first_overlapped_level;
|
||||
} else {
|
||||
assert(cfd->ioptions()->compaction_style == kCompactionStyleLevel);
|
||||
assert(cfd->ioptions().compaction_style == kCompactionStyleLevel);
|
||||
uint64_t next_file_number = versions_->current_next_file_number();
|
||||
// Start compaction from `first_overlapped_level`, one level down at a
|
||||
// time, until output level >= max_overlapped_level.
|
||||
@@ -1250,9 +1247,9 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
for (;;) {
|
||||
// Always allow L0 -> L1 compaction
|
||||
if (level > 0) {
|
||||
if (cfd->ioptions()->level_compaction_dynamic_level_bytes) {
|
||||
assert(final_output_level < cfd->ioptions()->num_levels);
|
||||
if (final_output_level + 1 == cfd->ioptions()->num_levels) {
|
||||
if (cfd->ioptions().level_compaction_dynamic_level_bytes) {
|
||||
assert(final_output_level < cfd->ioptions().num_levels);
|
||||
if (final_output_level + 1 == cfd->ioptions().num_levels) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -1271,7 +1268,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
}
|
||||
}
|
||||
output_level = level + 1;
|
||||
if (cfd->ioptions()->level_compaction_dynamic_level_bytes &&
|
||||
if (cfd->ioptions().level_compaction_dynamic_level_bytes &&
|
||||
level == 0) {
|
||||
output_level = ColumnFamilyData::kCompactToBaseLevel;
|
||||
}
|
||||
@@ -1303,8 +1300,8 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
// bottommost level intra-level compaction
|
||||
if ((options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kIfHaveCompactionFilter &&
|
||||
(cfd->ioptions()->compaction_filter != nullptr ||
|
||||
cfd->ioptions()->compaction_filter_factory != nullptr)) ||
|
||||
(cfd->ioptions().compaction_filter != nullptr ||
|
||||
cfd->ioptions().compaction_filter_factory != nullptr)) ||
|
||||
options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kForceOptimized ||
|
||||
options.bottommost_level_compaction ==
|
||||
@@ -1458,7 +1455,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
}
|
||||
|
||||
if (output_path_id < 0) {
|
||||
if (cfd->ioptions()->cf_paths.size() == 1U) {
|
||||
if (cfd->ioptions().cf_paths.size() == 1U) {
|
||||
output_path_id = 0;
|
||||
} else {
|
||||
return Status::NotSupported(
|
||||
@@ -1467,12 +1464,12 @@ Status DBImpl::CompactFilesImpl(
|
||||
}
|
||||
}
|
||||
|
||||
if (cfd->ioptions()->allow_ingest_behind &&
|
||||
output_level >= cfd->ioptions()->num_levels - 1) {
|
||||
if (cfd->ioptions().allow_ingest_behind &&
|
||||
output_level >= cfd->ioptions().num_levels - 1) {
|
||||
return Status::InvalidArgument(
|
||||
"Exceed the maximum output level defined by "
|
||||
"the current compaction algorithm with ingest_behind --- " +
|
||||
std::to_string(cfd->ioptions()->num_levels - 1));
|
||||
std::to_string(cfd->ioptions().num_levels - 1));
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> input_files;
|
||||
@@ -1509,7 +1506,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
assert(cfd->compaction_picker());
|
||||
c.reset(cfd->compaction_picker()->CompactFiles(
|
||||
compact_options, input_files, output_level, version->storage_info(),
|
||||
*cfd->GetLatestMutableCFOptions(), mutable_db_options_, output_path_id));
|
||||
cfd->GetLatestMutableCFOptions(), mutable_db_options_, output_path_id));
|
||||
// we already sanitized the set of input files and checked for conflicts
|
||||
// without releasing the lock, so we're guaranteed a compaction can be formed.
|
||||
assert(c != nullptr);
|
||||
@@ -1539,8 +1536,8 @@ Status DBImpl::CompactFilesImpl(
|
||||
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_,
|
||||
c->mutable_cf_options().paranoid_file_checks,
|
||||
c->mutable_cf_options().report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, Env::Priority::USER, io_tracer_,
|
||||
kManualCompactionCanceledFalse_, db_id_, db_session_id_,
|
||||
c->column_family_data()->GetFullHistoryTsLow(), c->trim_ts(),
|
||||
@@ -1551,10 +1548,10 @@ Status DBImpl::CompactFilesImpl(
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
// being compacted). Since we just changed compaction score, we recalculate it
|
||||
// here.
|
||||
version->storage_info()->ComputeCompactionScore(*cfd->ioptions(),
|
||||
*c->mutable_cf_options());
|
||||
version->storage_info()->ComputeCompactionScore(cfd->ioptions(),
|
||||
c->mutable_cf_options());
|
||||
|
||||
compaction_job.Prepare();
|
||||
compaction_job.Prepare(std::nullopt /*subcompact to be computed*/);
|
||||
|
||||
std::unique_ptr<std::list<uint64_t>::iterator> min_options_file_number_elem;
|
||||
if (immutable_db_options().compaction_service != nullptr) {
|
||||
@@ -1576,16 +1573,14 @@ Status DBImpl::CompactFilesImpl(
|
||||
}
|
||||
|
||||
bool compaction_released = false;
|
||||
Status status =
|
||||
compaction_job.Install(*c->mutable_cf_options(), &compaction_released);
|
||||
Status status = compaction_job.Install(&compaction_released);
|
||||
if (!compaction_released) {
|
||||
c->ReleaseCompactionFiles(s);
|
||||
}
|
||||
if (status.ok()) {
|
||||
assert(compaction_job.io_status().ok());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data(),
|
||||
*c->mutable_cf_options());
|
||||
c->column_family_data(), job_context->superversion_contexts.data());
|
||||
}
|
||||
// status above captures any error during compaction_job.Install, so its ok
|
||||
// not check compaction_job.io_status() explicitly if we're not calling
|
||||
@@ -1633,13 +1628,13 @@ Status DBImpl::CompactFilesImpl(
|
||||
if (output_file_names != nullptr) {
|
||||
for (const auto& newf : c->edit()->GetNewFiles()) {
|
||||
output_file_names->push_back(TableFileName(
|
||||
c->immutable_options()->cf_paths, newf.second.fd.GetNumber(),
|
||||
c->immutable_options().cf_paths, newf.second.fd.GetNumber(),
|
||||
newf.second.fd.GetPathId()));
|
||||
}
|
||||
|
||||
for (const auto& blob_file : c->edit()->GetBlobFileAdditions()) {
|
||||
output_file_names->push_back(
|
||||
BlobFileName(c->immutable_options()->cf_paths.front().path,
|
||||
BlobFileName(c->immutable_options().cf_paths.front().path,
|
||||
blob_file.GetBlobFileNumber()));
|
||||
}
|
||||
}
|
||||
@@ -1701,11 +1696,13 @@ void DBImpl::NotifyOnCompactionBegin(ColumnFamilyData* cfd, Compaction* c,
|
||||
}
|
||||
|
||||
c->SetNotifyOnCompactionCompleted();
|
||||
int num_l0_files = c->input_version()->storage_info()->NumLevelFiles(0);
|
||||
// release lock while notifying events
|
||||
mutex_.Unlock();
|
||||
TEST_SYNC_POINT("DBImpl::NotifyOnCompactionBegin::UnlockMutex");
|
||||
{
|
||||
CompactionJobInfo info{};
|
||||
info.num_l0_files = num_l0_files;
|
||||
BuildCompactionJobInfo(cfd, c, st, job_stats, job_id, &info);
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
listener->OnCompactionBegin(this, info);
|
||||
@@ -1730,11 +1727,13 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
return;
|
||||
}
|
||||
|
||||
int num_l0_files = cfd->current()->storage_info()->NumLevelFiles(0);
|
||||
// release lock while notifying events
|
||||
mutex_.Unlock();
|
||||
TEST_SYNC_POINT("DBImpl::NotifyOnCompactionCompleted::UnlockMutex");
|
||||
{
|
||||
CompactionJobInfo info{};
|
||||
info.num_l0_files = num_l0_files;
|
||||
BuildCompactionJobInfo(cfd, c, st, compaction_job_stats, job_id, &info);
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
listener->OnCompactionCompleted(this, info);
|
||||
@@ -1774,7 +1773,7 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
}
|
||||
refitting_level_ = true;
|
||||
|
||||
const MutableCFOptions mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
const auto& mutable_cf_options = cfd->GetLatestMutableCFOptions();
|
||||
// move to a smaller level
|
||||
int to_level = target_level;
|
||||
if (target_level < 0) {
|
||||
@@ -1845,12 +1844,12 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
cfd->current()->DebugString().data());
|
||||
|
||||
std::unique_ptr<Compaction> c(new Compaction(
|
||||
vstorage, *cfd->ioptions(), mutable_cf_options, mutable_db_options_,
|
||||
vstorage, cfd->ioptions(), mutable_cf_options, mutable_db_options_,
|
||||
{input}, to_level,
|
||||
MaxFileSizeForLevel(
|
||||
mutable_cf_options, to_level,
|
||||
cfd->ioptions()
|
||||
->compaction_style) /* output file size limit, not applicable */
|
||||
.compaction_style) /* output file size limit, not applicable */
|
||||
,
|
||||
LLONG_MAX /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID, not applicable */, mutable_cf_options.compression,
|
||||
@@ -1883,14 +1882,14 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
"[%s] Apply version edit:\n%s", cfd->GetName().c_str(),
|
||||
edit.DebugString().data());
|
||||
|
||||
Status status = versions_->LogAndApply(cfd, mutable_cf_options,
|
||||
read_options, write_options, &edit,
|
||||
&mutex_, directories_.GetDbDir());
|
||||
Status status =
|
||||
versions_->LogAndApply(cfd, read_options, write_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
c->MarkFilesBeingCompacted(false);
|
||||
cfd->compaction_picker()->UnregisterCompaction(c.get());
|
||||
c.reset();
|
||||
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context, mutable_cf_options);
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context);
|
||||
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "[%s] LogAndApply: %s\n",
|
||||
cfd->GetName().c_str(), status.ToString().data());
|
||||
@@ -2043,16 +2042,16 @@ Status DBImpl::RunManualCompaction(
|
||||
// For universal compaction, we enforce every manual compaction to compact
|
||||
// all files.
|
||||
if (begin == nullptr ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
cfd->ioptions().compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions().compaction_style == kCompactionStyleFIFO) {
|
||||
manual.begin = nullptr;
|
||||
} else {
|
||||
begin_storage.SetMinPossibleForUserKey(*begin);
|
||||
manual.begin = &begin_storage;
|
||||
}
|
||||
if (end == nullptr ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
cfd->ioptions().compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions().compaction_style == kCompactionStyleFIFO) {
|
||||
manual.end = nullptr;
|
||||
} else {
|
||||
end_storage.SetMaxPossibleForUserKey(*end);
|
||||
@@ -2133,7 +2132,7 @@ Status DBImpl::RunManualCompaction(
|
||||
scheduled ||
|
||||
(((manual.manual_end = &manual.tmp_storage1) != nullptr) &&
|
||||
((compaction = manual.cfd->CompactRange(
|
||||
*manual.cfd->GetLatestMutableCFOptions(), mutable_db_options_,
|
||||
manual.cfd->GetLatestMutableCFOptions(), mutable_db_options_,
|
||||
manual.input_level, manual.output_level, compact_range_options,
|
||||
manual.begin, manual.end, &manual.manual_end, &manual_conflict,
|
||||
max_file_num_to_ignore, trim_ts)) == nullptr &&
|
||||
@@ -2676,7 +2675,7 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
const auto& mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
const auto& mutable_cf_options = cfd->GetLatestMutableCFOptions();
|
||||
const auto* vstorage = cfd->current()->storage_info();
|
||||
|
||||
// Skip stalling check if we're below auto-flush and auto-compaction
|
||||
@@ -2684,7 +2683,7 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
// triggers are so low that stalling is needed for any background work. In
|
||||
// that case we shouldn't wait since background work won't be scheduled.
|
||||
if (cfd->imm()->NumNotFlushed() <
|
||||
cfd->ioptions()->min_write_buffer_number_to_merge &&
|
||||
cfd->ioptions().min_write_buffer_number_to_merge &&
|
||||
vstorage->l0_delay_trigger_count() <
|
||||
mutable_cf_options.level0_file_num_compaction_trigger) {
|
||||
break;
|
||||
@@ -2700,7 +2699,7 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
cfd->GetUnflushedMemTableCountForWriteStallCheck(),
|
||||
vstorage->l0_delay_trigger_count() + 1,
|
||||
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options,
|
||||
*cfd->ioptions())
|
||||
cfd->ioptions())
|
||||
.first;
|
||||
} while (write_stall_condition != WriteStallCondition::kNormal);
|
||||
}
|
||||
@@ -3528,6 +3527,7 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
}
|
||||
}
|
||||
|
||||
// Precondition: mutex_ must be held when calling this function.
|
||||
Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
JobContext* job_context,
|
||||
LogBuffer* log_buffer,
|
||||
@@ -3679,8 +3679,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
// Compaction makes a copy of the latest MutableCFOptions. It should be used
|
||||
// throughout the compaction procedure to make sure consistency. It will
|
||||
// eventually be installed into SuperVersion
|
||||
auto* mutable_cf_options = cfd->GetLatestMutableCFOptions();
|
||||
if (!mutable_cf_options->disable_auto_compactions && !cfd->IsDropped()) {
|
||||
const auto& mutable_cf_options = cfd->GetLatestMutableCFOptions();
|
||||
if (!mutable_cf_options.disable_auto_compactions && !cfd->IsDropped()) {
|
||||
// NOTE: try to avoid unnecessary copy of MutableCFOptions if
|
||||
// compaction is not necessary. Need to make sure mutex is held
|
||||
// until we make a copy in the following code
|
||||
@@ -3689,7 +3689,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
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 &&
|
||||
if (cfd->ioptions().compaction_style == kCompactionStyleUniversal &&
|
||||
cfd->user_comparator()->timestamp_size() == 0) {
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
@@ -3697,7 +3697,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
&snapshot_checker);
|
||||
assert(is_snapshot_supported_ || snapshots_.empty());
|
||||
}
|
||||
c.reset(cfd->PickCompaction(*mutable_cf_options, mutable_db_options_,
|
||||
c.reset(cfd->PickCompaction(mutable_cf_options, mutable_db_options_,
|
||||
snapshot_seqs, snapshot_checker, log_buffer));
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction():AfterPickCompaction");
|
||||
|
||||
@@ -3711,8 +3711,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c->column_family_data()
|
||||
->current()
|
||||
->storage_info()
|
||||
->ComputeCompactionScore(*(c->immutable_options()),
|
||||
*(c->mutable_cf_options()));
|
||||
->ComputeCompactionScore(c->immutable_options(),
|
||||
c->mutable_cf_options());
|
||||
AddToCompactionQueue(cfd);
|
||||
|
||||
c.reset();
|
||||
@@ -3760,7 +3760,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:BeforeCompaction",
|
||||
c->column_family_data());
|
||||
assert(c->num_input_files(1) == 0);
|
||||
assert(c->column_family_data()->ioptions()->compaction_style ==
|
||||
assert(c->column_family_data()->ioptions().compaction_style ==
|
||||
kCompactionStyleFIFO);
|
||||
|
||||
compaction_job_stats.num_input_files = c->num_input_files(0);
|
||||
@@ -3772,8 +3772,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c->edit()->DeleteFile(c->level(), f->fd.GetNumber());
|
||||
}
|
||||
status = versions_->LogAndApply(
|
||||
c->column_family_data(), *c->mutable_cf_options(), read_options,
|
||||
write_options, c->edit(), &mutex_, directories_.GetDbDir(),
|
||||
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);
|
||||
@@ -3781,8 +3781,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
});
|
||||
io_s = versions_->io_status();
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data(),
|
||||
*c->mutable_cf_options());
|
||||
c->column_family_data(), job_context->superversion_contexts.data());
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Deleted %d files\n",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
c->num_input_files(0));
|
||||
@@ -3804,6 +3803,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
compaction_job_stats.num_input_files = c->num_input_files(0);
|
||||
// Trivial moves do not get compacted remotely
|
||||
compaction_job_stats.is_remote_compaction = false;
|
||||
compaction_job_stats.num_input_files_trivially_moved =
|
||||
compaction_job_stats.num_input_files;
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
compaction_job_stats, job_context->job_id);
|
||||
@@ -3838,7 +3839,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
}
|
||||
}
|
||||
if (c->compaction_reason() == CompactionReason::kLevelMaxLevelSize &&
|
||||
c->immutable_options()->compaction_pri == kRoundRobin) {
|
||||
c->immutable_options().compaction_pri == kRoundRobin) {
|
||||
int start_level = c->start_level();
|
||||
if (start_level > 0) {
|
||||
auto vstorage = c->input_version()->storage_info();
|
||||
@@ -3848,26 +3849,23 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
}
|
||||
}
|
||||
status = versions_->LogAndApply(
|
||||
c->column_family_data(), *c->mutable_cf_options(), read_options,
|
||||
write_options, c->edit(), &mutex_, directories_.GetDbDir(),
|
||||
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;
|
||||
});
|
||||
io_s = versions_->io_status();
|
||||
// Use latest MutableCFOptions
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data(),
|
||||
*c->mutable_cf_options());
|
||||
c->column_family_data(), job_context->superversion_contexts.data());
|
||||
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
c->column_family_data()->internal_stats()->IncBytesMoved(c->output_level(),
|
||||
moved_bytes);
|
||||
{
|
||||
event_logger_.LogToBuffer(log_buffer)
|
||||
<< "job" << job_context->job_id << "event"
|
||||
<< "trivial_move"
|
||||
<< "job" << job_context->job_id << "event" << "trivial_move"
|
||||
<< "destination_level" << c->output_level() << "files" << moved_files
|
||||
<< "total_files_size" << moved_bytes;
|
||||
}
|
||||
@@ -3929,15 +3927,15 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
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_,
|
||||
c->mutable_cf_options().paranoid_file_checks,
|
||||
c->mutable_cf_options().report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, thread_pri, io_tracer_,
|
||||
is_manual ? manual_compaction->canceled
|
||||
: kManualCompactionCanceledFalse_,
|
||||
db_id_, db_session_id_, c->column_family_data()->GetFullHistoryTsLow(),
|
||||
c->trim_ts(), &blob_callback_, &bg_compaction_scheduled_,
|
||||
&bg_bottom_compaction_scheduled_);
|
||||
compaction_job.Prepare();
|
||||
compaction_job.Prepare(std::nullopt /*subcompact to be computed*/);
|
||||
|
||||
std::unique_ptr<std::list<uint64_t>::iterator> min_options_file_number_elem;
|
||||
if (immutable_db_options().compaction_service != nullptr) {
|
||||
@@ -3959,13 +3957,11 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
ReleaseOptionsFileNumber(min_options_file_number_elem);
|
||||
}
|
||||
|
||||
status =
|
||||
compaction_job.Install(*c->mutable_cf_options(), &compaction_released);
|
||||
status = compaction_job.Install(&compaction_released);
|
||||
io_s = compaction_job.io_status();
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data(),
|
||||
*c->mutable_cf_options());
|
||||
c->column_family_data(), job_context->superversion_contexts.data());
|
||||
}
|
||||
*made_progress = true;
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
@@ -4043,8 +4039,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c->column_family_data()
|
||||
->current()
|
||||
->storage_info()
|
||||
->ComputeCompactionScore(*(c->immutable_options()),
|
||||
*(c->mutable_cf_options()));
|
||||
->ComputeCompactionScore(c->immutable_options(),
|
||||
c->mutable_cf_options());
|
||||
if (!cfd->queued_for_compaction()) {
|
||||
AddToCompactionQueue(cfd);
|
||||
}
|
||||
@@ -4079,10 +4075,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
// We only compacted part of the requested range. Update *m
|
||||
// to the range that is left to be compacted.
|
||||
// Universal and FIFO compactions should always compact the whole range
|
||||
assert(m->cfd->ioptions()->compaction_style !=
|
||||
kCompactionStyleUniversal ||
|
||||
m->cfd->ioptions()->num_levels > 1);
|
||||
assert(m->cfd->ioptions()->compaction_style != kCompactionStyleFIFO);
|
||||
assert(m->cfd->ioptions().compaction_style != kCompactionStyleUniversal ||
|
||||
m->cfd->ioptions().num_levels > 1);
|
||||
assert(m->cfd->ioptions().compaction_style != kCompactionStyleFIFO);
|
||||
m->tmp_storage = *m->manual_end;
|
||||
m->begin = &m->tmp_storage;
|
||||
m->incomplete = true;
|
||||
@@ -4229,7 +4224,7 @@ void DBImpl::BuildCompactionJobInfo(
|
||||
for (const auto fmd : *c->inputs(i)) {
|
||||
const FileDescriptor& desc = fmd->fd;
|
||||
const uint64_t file_number = desc.GetNumber();
|
||||
auto fn = TableFileName(c->immutable_options()->cf_paths, file_number,
|
||||
auto fn = TableFileName(c->immutable_options().cf_paths, file_number,
|
||||
desc.GetPathId());
|
||||
compaction_job_info->input_files.push_back(fn);
|
||||
compaction_job_info->input_file_infos.push_back(CompactionFileInfo{
|
||||
@@ -4242,17 +4237,17 @@ void DBImpl::BuildCompactionJobInfo(
|
||||
const FileDescriptor& desc = meta.fd;
|
||||
const uint64_t file_number = desc.GetNumber();
|
||||
compaction_job_info->output_files.push_back(TableFileName(
|
||||
c->immutable_options()->cf_paths, file_number, desc.GetPathId()));
|
||||
c->immutable_options().cf_paths, file_number, desc.GetPathId()));
|
||||
compaction_job_info->output_file_infos.push_back(CompactionFileInfo{
|
||||
newf.first, file_number, meta.oldest_blob_file_number});
|
||||
}
|
||||
compaction_job_info->blob_compression_type =
|
||||
c->mutable_cf_options()->blob_compression_type;
|
||||
c->mutable_cf_options().blob_compression_type;
|
||||
|
||||
// Update BlobFilesInfo.
|
||||
for (const auto& blob_file : c->edit()->GetBlobFileAdditions()) {
|
||||
BlobFileAdditionInfo blob_file_addition_info(
|
||||
BlobFileName(c->immutable_options()->cf_paths.front().path,
|
||||
BlobFileName(c->immutable_options().cf_paths.front().path,
|
||||
blob_file.GetBlobFileNumber()) /*blob_file_path*/,
|
||||
blob_file.GetBlobFileNumber(), blob_file.GetTotalBlobCount(),
|
||||
blob_file.GetTotalBlobBytes());
|
||||
@@ -4263,7 +4258,7 @@ void DBImpl::BuildCompactionJobInfo(
|
||||
// Update BlobFilesGarbageInfo.
|
||||
for (const auto& blob_file : c->edit()->GetBlobFileGarbages()) {
|
||||
BlobFileGarbageInfo blob_file_garbage_info(
|
||||
BlobFileName(c->immutable_options()->cf_paths.front().path,
|
||||
BlobFileName(c->immutable_options().cf_paths.front().path,
|
||||
blob_file.GetBlobFileNumber()) /*blob_file_path*/,
|
||||
blob_file.GetBlobFileNumber(), blob_file.GetGarbageBlobCount(),
|
||||
blob_file.GetGarbageBlobBytes());
|
||||
@@ -4286,7 +4281,8 @@ void DBImpl::BuildCompactionJobInfo(
|
||||
|
||||
void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
ColumnFamilyData* cfd, SuperVersionContext* sv_context,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
std::optional<std::shared_ptr<SeqnoToTimeMapping>>
|
||||
new_seqno_to_time_mapping) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
// Update max_total_in_memory_state_
|
||||
@@ -4301,7 +4297,8 @@ void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
if (UNLIKELY(sv_context->new_superversion == nullptr)) {
|
||||
sv_context->NewSuperVersion();
|
||||
}
|
||||
cfd->InstallSuperVersion(sv_context, mutable_cf_options);
|
||||
cfd->InstallSuperVersion(sv_context, &mutex_,
|
||||
std::move(new_seqno_to_time_mapping));
|
||||
|
||||
// There may be a small data race here. The snapshot tricking bottommost
|
||||
// compaction may already be released here. But assuming there will always be
|
||||
@@ -4310,7 +4307,7 @@ void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
bottommost_files_mark_threshold_ = kMaxSequenceNumber;
|
||||
standalone_range_deletion_files_mark_threshold_ = kMaxSequenceNumber;
|
||||
for (auto* my_cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (!my_cfd->ioptions()->allow_ingest_behind) {
|
||||
if (!my_cfd->ioptions().allow_ingest_behind) {
|
||||
bottommost_files_mark_threshold_ = std::min(
|
||||
bottommost_files_mark_threshold_,
|
||||
my_cfd->current()->storage_info()->bottommost_files_mark_threshold());
|
||||
@@ -4328,9 +4325,10 @@ void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
|
||||
// Update max_total_in_memory_state_
|
||||
max_total_in_memory_state_ = max_total_in_memory_state_ - old_memtable_size +
|
||||
mutable_cf_options.write_buffer_size *
|
||||
mutable_cf_options.max_write_buffer_number;
|
||||
max_total_in_memory_state_ =
|
||||
max_total_in_memory_state_ - old_memtable_size +
|
||||
cfd->GetLatestMutableCFOptions().write_buffer_size *
|
||||
cfd->GetLatestMutableCFOptions().max_write_buffer_number;
|
||||
}
|
||||
|
||||
// ShouldPurge is called by FindObsoleteFiles when doing a full scan,
|
||||
|
||||
@@ -103,8 +103,8 @@ Status DBImpl::TEST_CompactRange(int level, const Slice* begin,
|
||||
cfd = cfh->cfd();
|
||||
}
|
||||
int output_level =
|
||||
(cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO)
|
||||
(cfd->ioptions().compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions().compaction_style == kCompactionStyleFIFO)
|
||||
? level
|
||||
: level + 1;
|
||||
return RunManualCompaction(
|
||||
@@ -239,7 +239,7 @@ void DBImpl::TEST_GetAllBlockCaches(
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (const auto bbto =
|
||||
cfd->GetCurrentMutableCFOptions()
|
||||
->table_factory->GetOptions<BlockBasedTableOptions>()) {
|
||||
.table_factory->GetOptions<BlockBasedTableOptions>()) {
|
||||
cache_set->insert(bbto->block_cache.get());
|
||||
}
|
||||
}
|
||||
@@ -267,7 +267,7 @@ Status DBImpl::TEST_GetLatestMutableCFOptions(
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
*mutable_cf_options = *cfh->cfd()->GetLatestMutableCFOptions();
|
||||
*mutable_cf_options = cfh->cfd()->GetLatestMutableCFOptions();
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -379,10 +379,13 @@ void DBImpl::TEST_VerifyNoObsoleteFilesCached(
|
||||
uint64_t file_number;
|
||||
GetUnaligned(reinterpret_cast<const uint64_t*>(key.data()), &file_number);
|
||||
// Assert file is in live/quarantined set
|
||||
if (live_and_quar_files.find(file_number) == live_and_quar_files.end()) {
|
||||
bool cached_file_is_live_or_quar =
|
||||
live_and_quar_files.find(file_number) != live_and_quar_files.end();
|
||||
if (!cached_file_is_live_or_quar) {
|
||||
// Fail with useful info
|
||||
std::cerr << "File " << file_number << " is not live nor quarantined"
|
||||
<< std::endl;
|
||||
assert(false);
|
||||
assert(cached_file_is_live_or_quar);
|
||||
}
|
||||
};
|
||||
table_cache_->ApplyToAllEntries(fn, {});
|
||||
|
||||
@@ -45,8 +45,8 @@ Status DBImpl::SuggestCompactRange(ColumnFamilyHandle* column_family,
|
||||
}
|
||||
// Since we have some more files to compact, we should also recompute
|
||||
// compaction score
|
||||
vstorage->ComputeCompactionScore(*cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
vstorage->ComputeCompactionScore(cfd->ioptions(),
|
||||
cfd->GetLatestMutableCFOptions());
|
||||
EnqueuePendingCompaction(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -146,13 +146,11 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
f->user_defined_timestamps_persisted);
|
||||
}
|
||||
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
read_options, write_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
status = versions_->LogAndApply(cfd, read_options, write_options, &edit,
|
||||
&mutex_, directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, job_context.superversion_contexts.data(),
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
cfd, job_context.superversion_contexts.data());
|
||||
}
|
||||
} // lock released here
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
|
||||
@@ -159,6 +159,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
|
||||
// Get obsolete files. This function will also update the list of
|
||||
// pending files in VersionSet().
|
||||
assert(versions_);
|
||||
versions_->GetObsoleteFiles(
|
||||
&job_context->sst_delete_files, &job_context->blob_delete_files,
|
||||
&job_context->manifest_delete_files, job_context->min_pending_output);
|
||||
@@ -367,6 +368,7 @@ void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
FileType type, uint64_t number) {
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::DeleteObsoleteFileImpl::BeforeDeletion",
|
||||
const_cast<std::string*>(&fname));
|
||||
IGNORE_STATUS_IF_ERROR(Status::IOError());
|
||||
|
||||
Status file_deletion_status;
|
||||
if (type == kTableFile || type == kBlobFile || type == kWalFile) {
|
||||
@@ -422,6 +424,8 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
// FindObsoleteFiles() should've populated this so nonzero
|
||||
assert(state.manifest_file_number != 0);
|
||||
|
||||
IGNORE_STATUS_IF_ERROR(Status::IOError());
|
||||
|
||||
// Now, convert lists to unordered sets, WITHOUT mutex held; set is slow.
|
||||
std::unordered_set<uint64_t> sst_live_set(state.sst_live.begin(),
|
||||
state.sst_live.end());
|
||||
@@ -1039,7 +1043,7 @@ std::set<std::string> DBImpl::CollectAllDBPaths() {
|
||||
all_db_paths.insert(NormalizePath(db_path.path));
|
||||
}
|
||||
for (const auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (const auto& cf_path : cfd->ioptions()->cf_paths) {
|
||||
for (const auto& cf_path : cfd->ioptions().cf_paths) {
|
||||
all_db_paths.insert(NormalizePath(cf_path.path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,10 +140,8 @@ Status DBImplFollower::TryCatchUpWithLeader() {
|
||||
// to super versions in a lock free manner, it checks the earliest
|
||||
// sequence number to detect if there was a change in version in
|
||||
// the meantime.
|
||||
const MutableCFOptions mutable_cf_options =
|
||||
*cfd->GetLatestMutableCFOptions();
|
||||
MemTable* new_mem = cfd->ConstructNewMemtable(
|
||||
mutable_cf_options, versions_->LastSequence());
|
||||
cfd->GetLatestMutableCFOptions(), versions_->LastSequence());
|
||||
cfd->mem()->SetNextLogNumber(cfd->GetLogNumber());
|
||||
cfd->mem()->ConstructFragmentedRangeTombstones();
|
||||
cfd->imm()->Add(cfd->mem(), &job_context.memtables_to_free);
|
||||
|
||||
+684
-330
File diff suppressed because it is too large.
Load diff
@@ -16,7 +16,6 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
|
||||
DBImplReadOnly::DBImplReadOnly(const DBOptions& db_options,
|
||||
const std::string& dbname)
|
||||
: DBImpl(db_options, dbname, /*seq_per_batch*/ false,
|
||||
@@ -33,7 +32,8 @@ Status DBImplReadOnly::GetImpl(const ReadOptions& read_options,
|
||||
const Slice& key,
|
||||
GetImplOptions& get_impl_options) {
|
||||
assert(get_impl_options.value != nullptr ||
|
||||
get_impl_options.columns != nullptr);
|
||||
get_impl_options.columns != nullptr ||
|
||||
get_impl_options.merge_operands != nullptr);
|
||||
assert(get_impl_options.column_family);
|
||||
|
||||
Status s;
|
||||
@@ -87,7 +87,11 @@ Status DBImplReadOnly::GetImpl(const ReadOptions& read_options,
|
||||
return s;
|
||||
}
|
||||
}
|
||||
// Prepare to store a list of merge operations if merge occurs.
|
||||
MergeContext merge_context;
|
||||
// TODO - Large Result Optimization for Read Only DB
|
||||
// (https://github.com/facebook/rocksdb/pull/10458)
|
||||
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
LookupKey lkey(key, snapshot, read_options.timestamp);
|
||||
PERF_TIMER_STOP(get_snapshot_time);
|
||||
@@ -98,7 +102,8 @@ Status DBImplReadOnly::GetImpl(const ReadOptions& read_options,
|
||||
get_impl_options.value ? get_impl_options.value->GetSelf() : nullptr,
|
||||
get_impl_options.columns, ts, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options,
|
||||
false /* immutable_memtable */, &read_cb)) {
|
||||
false /* immutable_memtable */, &read_cb,
|
||||
/*is_blob_index=*/nullptr, /*do_merge=*/get_impl_options.get_value)) {
|
||||
if (get_impl_options.value) {
|
||||
get_impl_options.value->PinSelf();
|
||||
}
|
||||
@@ -112,7 +117,7 @@ Status DBImplReadOnly::GetImpl(const ReadOptions& read_options,
|
||||
/*value_found*/ nullptr,
|
||||
/*key_exists*/ nullptr, /*seq*/ nullptr, &read_cb,
|
||||
/*is_blob*/ nullptr,
|
||||
/*do_merge*/ true);
|
||||
/*do_merge=*/get_impl_options.get_value);
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
{
|
||||
@@ -122,6 +127,14 @@ Status DBImplReadOnly::GetImpl(const ReadOptions& read_options,
|
||||
size = get_impl_options.value->size();
|
||||
} else if (get_impl_options.columns) {
|
||||
size = get_impl_options.columns->serialized_size();
|
||||
} else if (get_impl_options.merge_operands) {
|
||||
*get_impl_options.number_of_operands =
|
||||
static_cast<int>(merge_context.GetNumOperands());
|
||||
for (const Slice& sl : merge_context.GetOperands()) {
|
||||
size += sl.size();
|
||||
get_impl_options.merge_operands->PinSelf(sl);
|
||||
get_impl_options.merge_operands++;
|
||||
}
|
||||
}
|
||||
RecordTick(stats_, BYTES_READ, size);
|
||||
RecordInHistogram(stats_, BYTES_PER_READ, size);
|
||||
@@ -173,7 +186,7 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& _read_options,
|
||||
: latest_snapshot;
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), super_version->mutable_cf_options,
|
||||
env_, read_options, cfd->ioptions(), super_version->mutable_cf_options,
|
||||
super_version->current, read_seq,
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
super_version->version_number, read_callback);
|
||||
@@ -240,7 +253,7 @@ Status DBImplReadOnly::NewIterators(
|
||||
assert(cfd_to_sv.size() == column_families.size());
|
||||
for (auto [cfd, sv] : cfd_to_sv) {
|
||||
auto* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
env_, read_options, cfd->ioptions(), sv->mutable_cf_options,
|
||||
sv->current, read_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback);
|
||||
@@ -276,7 +289,8 @@ Status OpenForReadOnlyCheckExistence(const DBOptions& db_options,
|
||||
} // namespace
|
||||
|
||||
Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
|
||||
DB** dbptr, bool /*error_if_wal_file_exists*/) {
|
||||
std::unique_ptr<DB>* dbptr,
|
||||
bool /*error_if_wal_file_exists*/) {
|
||||
Status s = OpenForReadOnlyCheckExistence(options, dbname);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -310,7 +324,7 @@ Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
|
||||
Status DB::OpenForReadOnly(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
std::vector<ColumnFamilyHandle*>* handles, std::unique_ptr<DB>* dbptr,
|
||||
bool error_if_wal_file_exists) {
|
||||
// If dbname does not exist in the file system, should not do anything
|
||||
Status s = OpenForReadOnlyCheckExistence(db_options, dbname);
|
||||
@@ -326,7 +340,7 @@ Status DB::OpenForReadOnly(
|
||||
Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
std::vector<ColumnFamilyHandle*>* handles, std::unique_ptr<DB>* dbptr,
|
||||
bool error_if_wal_file_exists) {
|
||||
*dbptr = nullptr;
|
||||
handles->clear();
|
||||
@@ -357,7 +371,7 @@ Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
impl->mutex_.Unlock();
|
||||
sv_context.Clean();
|
||||
if (s.ok()) {
|
||||
*dbptr = impl;
|
||||
dbptr->reset(impl);
|
||||
for (auto* h : *handles) {
|
||||
impl->NewThreadStatusCfInfo(
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(h)->cfd());
|
||||
@@ -372,5 +386,4 @@ Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -156,6 +155,29 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamily;
|
||||
using DBImpl::CreateColumnFamily;
|
||||
Status CreateColumnFamily(const ColumnFamilyOptions& /*cf_options*/,
|
||||
const std::string& /*column_family*/,
|
||||
ColumnFamilyHandle** /*handle*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamilies;
|
||||
using DBImpl::CreateColumnFamilies;
|
||||
Status CreateColumnFamilies(
|
||||
const ColumnFamilyOptions& /*cf_options*/,
|
||||
const std::vector<std::string>& /*column_family_names*/,
|
||||
std::vector<ColumnFamilyHandle*>* /*handles*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
Status CreateColumnFamilies(
|
||||
const std::vector<ColumnFamilyDescriptor>& /*column_families*/,
|
||||
std::vector<ColumnFamilyHandle*>* /*handles*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
// FIXME: some missing overrides for more "write" functions
|
||||
|
||||
protected:
|
||||
@@ -172,7 +194,7 @@ class DBImplReadOnly : public DBImpl {
|
||||
static Status OpenForReadOnlyWithoutCheck(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
std::vector<ColumnFamilyHandle*>* handles, std::unique_ptr<DB>* dbptr,
|
||||
bool error_if_wal_file_exists = false);
|
||||
friend class DB;
|
||||
};
|
||||
|
||||
@@ -55,9 +55,9 @@ Status DBImplSecondary::Recover(
|
||||
// Initial max_total_in_memory_state_ before recovery logs.
|
||||
max_total_in_memory_state_ = 0;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
auto* mutable_cf_options = cfd->GetLatestMutableCFOptions();
|
||||
max_total_in_memory_state_ += mutable_cf_options->write_buffer_size *
|
||||
mutable_cf_options->max_write_buffer_number;
|
||||
const auto& mutable_cf_options = cfd->GetLatestMutableCFOptions();
|
||||
max_total_in_memory_state_ += mutable_cf_options.write_buffer_size *
|
||||
mutable_cf_options.max_write_buffer_number;
|
||||
}
|
||||
if (s.ok()) {
|
||||
default_cf_handle_ = new ColumnFamilyHandleImpl(
|
||||
@@ -270,10 +270,8 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
if (!cfd->mem()->IsEmpty() &&
|
||||
(curr_log_num == std::numeric_limits<uint64_t>::max() ||
|
||||
curr_log_num != log_number)) {
|
||||
const MutableCFOptions mutable_cf_options =
|
||||
*cfd->GetLatestMutableCFOptions();
|
||||
MemTable* new_mem =
|
||||
cfd->ConstructNewMemtable(mutable_cf_options, seq_of_batch);
|
||||
MemTable* new_mem = cfd->ConstructNewMemtable(
|
||||
cfd->GetLatestMutableCFOptions(), seq_of_batch);
|
||||
cfd->mem()->SetNextLogNumber(log_number);
|
||||
cfd->mem()->ConstructFragmentedRangeTombstones();
|
||||
cfd->imm()->Add(cfd->mem(), &job_context->memtables_to_free);
|
||||
@@ -344,7 +342,8 @@ Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
|
||||
const Slice& key,
|
||||
GetImplOptions& get_impl_options) {
|
||||
assert(get_impl_options.value != nullptr ||
|
||||
get_impl_options.columns != nullptr);
|
||||
get_impl_options.columns != nullptr ||
|
||||
get_impl_options.merge_operands != nullptr);
|
||||
assert(get_impl_options.column_family);
|
||||
|
||||
Status s;
|
||||
@@ -399,37 +398,64 @@ Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
|
||||
}
|
||||
}
|
||||
MergeContext merge_context;
|
||||
// TODO - Large Result Optimization for Secondary DB
|
||||
// (https://github.com/facebook/rocksdb/pull/10458)
|
||||
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
LookupKey lkey(key, snapshot, read_options.timestamp);
|
||||
PERF_TIMER_STOP(get_snapshot_time);
|
||||
bool done = false;
|
||||
|
||||
// Look up starts here
|
||||
if (super_version->mem->Get(
|
||||
lkey,
|
||||
get_impl_options.value ? get_impl_options.value->GetSelf() : nullptr,
|
||||
get_impl_options.columns, ts, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options,
|
||||
false /* immutable_memtable */, &read_cb)) {
|
||||
done = true;
|
||||
if (get_impl_options.value) {
|
||||
get_impl_options.value->PinSelf();
|
||||
if (get_impl_options.get_value) {
|
||||
if (super_version->mem->Get(
|
||||
lkey,
|
||||
get_impl_options.value ? get_impl_options.value->GetSelf()
|
||||
: nullptr,
|
||||
get_impl_options.columns, ts, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options,
|
||||
false /* immutable_memtable */, &read_cb,
|
||||
/*is_blob_index=*/nullptr, /*do_merge=*/true)) {
|
||||
done = true;
|
||||
if (get_impl_options.value) {
|
||||
get_impl_options.value->PinSelf();
|
||||
}
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if ((s.ok() || s.IsMergeInProgress()) &&
|
||||
super_version->imm->Get(
|
||||
lkey,
|
||||
get_impl_options.value ? get_impl_options.value->GetSelf()
|
||||
: nullptr,
|
||||
get_impl_options.columns, ts, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options, &read_cb)) {
|
||||
done = true;
|
||||
if (get_impl_options.value) {
|
||||
get_impl_options.value->PinSelf();
|
||||
}
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
}
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if ((s.ok() || s.IsMergeInProgress()) &&
|
||||
super_version->imm->Get(
|
||||
lkey,
|
||||
get_impl_options.value ? get_impl_options.value->GetSelf()
|
||||
: nullptr,
|
||||
get_impl_options.columns, ts, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options, &read_cb)) {
|
||||
done = true;
|
||||
if (get_impl_options.value) {
|
||||
get_impl_options.value->PinSelf();
|
||||
} else {
|
||||
// GetMergeOperands
|
||||
if (super_version->mem->Get(
|
||||
lkey,
|
||||
get_impl_options.value ? get_impl_options.value->GetSelf()
|
||||
: nullptr,
|
||||
get_impl_options.columns, ts, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options,
|
||||
false /* immutable_memtable */, &read_cb,
|
||||
/*is_blob_index=*/nullptr, /*do_merge=*/false)) {
|
||||
done = true;
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if ((s.ok() || s.IsMergeInProgress()) &&
|
||||
super_version->imm->GetMergeOperands(lkey, &s, &merge_context,
|
||||
&max_covering_tombstone_seq,
|
||||
read_options)) {
|
||||
done = true;
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
}
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
}
|
||||
if (!done && !s.ok() && !s.IsMergeInProgress()) {
|
||||
if (!s.ok() && !s.IsMergeInProgress() && !s.IsNotFound()) {
|
||||
assert(done);
|
||||
ReturnAndCleanupSuperVersion(cfd, super_version);
|
||||
return s;
|
||||
}
|
||||
@@ -453,6 +479,14 @@ Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
|
||||
size = get_impl_options.value->size();
|
||||
} else if (get_impl_options.columns) {
|
||||
size = get_impl_options.columns->serialized_size();
|
||||
} else if (get_impl_options.merge_operands) {
|
||||
*get_impl_options.number_of_operands =
|
||||
static_cast<int>(merge_context.GetNumOperands());
|
||||
for (const Slice& sl : merge_context.GetOperands()) {
|
||||
size += sl.size();
|
||||
get_impl_options.merge_operands->PinSelf(sl);
|
||||
get_impl_options.merge_operands++;
|
||||
}
|
||||
}
|
||||
RecordTick(stats_, BYTES_READ, size);
|
||||
RecordTimeToHistogram(stats_, BYTES_PER_READ, size);
|
||||
@@ -533,7 +567,7 @@ ArenaWrappedDBIter* DBImplSecondary::NewIteratorImpl(
|
||||
snapshot = versions_->LastSequence();
|
||||
assert(snapshot != kMaxSequenceNumber);
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfh->cfd()->ioptions(),
|
||||
env_, read_options, cfh->cfd()->ioptions(),
|
||||
super_version->mutable_cf_options, super_version->current, snapshot,
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
super_version->version_number, read_callback, cfh, expose_blob_index,
|
||||
@@ -737,7 +771,8 @@ Status DBImplSecondary::TryCatchUpWithPrimary() {
|
||||
}
|
||||
|
||||
Status DB::OpenAsSecondary(const Options& options, const std::string& dbname,
|
||||
const std::string& secondary_path, DB** dbptr) {
|
||||
const std::string& secondary_path,
|
||||
std::unique_ptr<DB>* dbptr) {
|
||||
*dbptr = nullptr;
|
||||
|
||||
DBOptions db_options(options);
|
||||
@@ -759,7 +794,7 @@ Status DB::OpenAsSecondary(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::string& secondary_path,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr) {
|
||||
std::vector<ColumnFamilyHandle*>* handles, std::unique_ptr<DB>* dbptr) {
|
||||
*dbptr = nullptr;
|
||||
|
||||
DBOptions tmp_opts(db_options);
|
||||
@@ -826,7 +861,7 @@ Status DB::OpenAsSecondary(
|
||||
impl->mutex_.Unlock();
|
||||
sv_context.Clean();
|
||||
if (s.ok()) {
|
||||
*dbptr = impl;
|
||||
dbptr->reset(impl);
|
||||
for (auto h : *handles) {
|
||||
impl->NewThreadStatusCfInfo(
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(h)->cfd());
|
||||
@@ -864,21 +899,24 @@ Status DBImplSecondary::CompactWithoutInstallation(
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
version->GetColumnFamilyMetaData(&cf_meta);
|
||||
|
||||
const MutableCFOptions* mutable_cf_options = cfd->GetLatestMutableCFOptions();
|
||||
ColumnFamilyOptions cf_options = cfd->GetLatestCFOptions();
|
||||
VersionStorageInfo* vstorage = version->storage_info();
|
||||
|
||||
// Use comp_options to reuse some CompactFiles functions
|
||||
CompactionOptions comp_options;
|
||||
comp_options.compression = kDisableCompressionOption;
|
||||
comp_options.output_file_size_limit = MaxFileSizeForLevel(
|
||||
*mutable_cf_options, input.output_level, cf_options.compaction_style,
|
||||
vstorage->base_level(), cf_options.level_compaction_dynamic_level_bytes);
|
||||
cfd->GetLatestMutableCFOptions(), input.output_level,
|
||||
cfd->ioptions().compaction_style, vstorage->base_level(),
|
||||
cfd->ioptions().level_compaction_dynamic_level_bytes);
|
||||
|
||||
std::vector<CompactionInputFiles> input_files;
|
||||
Status s = cfd->compaction_picker()->GetCompactionInputsFromFileNumbers(
|
||||
&input_files, &input_set, vstorage, comp_options);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(
|
||||
immutable_db_options_.info_log,
|
||||
"GetCompactionInputsFromFileNumbers() failed - %s.\n DebugString: %s",
|
||||
s.ToString().c_str(), version->DebugString().c_str());
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -886,7 +924,7 @@ Status DBImplSecondary::CompactWithoutInstallation(
|
||||
assert(cfd->compaction_picker());
|
||||
c.reset(cfd->compaction_picker()->CompactFiles(
|
||||
comp_options, input_files, input.output_level, vstorage,
|
||||
*mutable_cf_options, mutable_db_options_, 0));
|
||||
cfd->GetLatestMutableCFOptions(), mutable_db_options_, 0));
|
||||
assert(c != nullptr);
|
||||
|
||||
c->FinalizeInputInfo(version);
|
||||
@@ -917,6 +955,8 @@ Status DBImplSecondary::CompactWithoutInstallation(
|
||||
options.canceled ? *options.canceled : kManualCompactionCanceledFalse_,
|
||||
input.db_id, db_session_id_, secondary_path_, input, result);
|
||||
|
||||
compaction_job.Prepare();
|
||||
|
||||
mutex_.Unlock();
|
||||
s = compaction_job.Run();
|
||||
mutex_.Lock();
|
||||
@@ -954,6 +994,7 @@ Status DB::OpenAndCompact(
|
||||
DBOptions db_options;
|
||||
ConfigOptions config_options;
|
||||
config_options.env = override_options.env;
|
||||
config_options.ignore_unknown_options = true;
|
||||
std::vector<ColumnFamilyDescriptor> all_column_families;
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
@@ -1057,5 +1098,4 @@ Status DB::OpenAndCompact(
|
||||
output, override_options);
|
||||
}
|
||||
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -53,7 +52,8 @@ class LogReaderContainer {
|
||||
Logger* info_log;
|
||||
std::string fname;
|
||||
Status* status; // nullptr if immutable_db_options_.paranoid_checks==false
|
||||
void Corruption(size_t bytes, const Status& s) override {
|
||||
void Corruption(size_t bytes, const Status& s,
|
||||
uint64_t /*log_number*/ = kMaxSequenceNumber) override {
|
||||
ROCKS_LOG_WARN(info_log, "%s%s: dropping %d bytes; %s",
|
||||
(this->status == nullptr ? "(ignoring error) " : ""),
|
||||
fname.c_str(), static_cast<int>(bytes),
|
||||
|
||||
+40
-23
@@ -67,7 +67,7 @@ Status DBImpl::Merge(const WriteOptions& o, ColumnFamilyHandle* column_family,
|
||||
return s;
|
||||
}
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
if (!cfh->cfd()->ioptions()->merge_operator) {
|
||||
if (!cfh->cfd()->ioptions().merge_operator) {
|
||||
return Status::NotSupported("Provide a merge_operator when opening DB");
|
||||
} else {
|
||||
return DB::Merge(o, column_family, key, val);
|
||||
@@ -205,7 +205,7 @@ Status DBImpl::IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
|
||||
ColumnFamilySet* cf_set = versions_->GetColumnFamilySet();
|
||||
|
||||
// Create WBWIMemTables
|
||||
for (const auto [cf_id, stat] : wbwi->GetCFStats()) {
|
||||
for (const auto& [cf_id, stat] : wbwi->GetCFStats()) {
|
||||
ColumnFamilyData* cfd = cf_set->GetColumnFamily(cf_id);
|
||||
if (!cfd) {
|
||||
if (ignore_missing_cf) {
|
||||
@@ -232,8 +232,8 @@ Status DBImpl::IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
|
||||
return s;
|
||||
}
|
||||
WBWIMemTable* wbwi_memtable =
|
||||
new WBWIMemTable(wbwi, cfd->user_comparator(), cf_id, cfd->ioptions(),
|
||||
cfd->GetLatestMutableCFOptions(), stat);
|
||||
new WBWIMemTable(wbwi, cfd->user_comparator(), cf_id, &cfd->ioptions(),
|
||||
&cfd->GetLatestMutableCFOptions(), stat);
|
||||
wbwi_memtable->Ref();
|
||||
wbwi_memtable->AssignSequenceNumbers(assigned_seqno);
|
||||
// This is needed to keep the WAL that contains Prepare alive until
|
||||
@@ -587,6 +587,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
IOStatus io_s;
|
||||
Status pre_release_cb_status;
|
||||
size_t seq_inc = 0;
|
||||
if (status.ok()) {
|
||||
// Rules for when we can update the memtable concurrently
|
||||
// 1. supported by memtable
|
||||
@@ -640,7 +641,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// disable_memtable in between; although we do not write this batch to
|
||||
// memtable it still consumes a seq. Otherwise, if !seq_per_batch_, we inc
|
||||
// the seq per valid written key to mem.
|
||||
size_t seq_inc = seq_per_batch_ ? valid_batches : total_count;
|
||||
seq_inc = seq_per_batch_ ? valid_batches : total_count;
|
||||
if (wbwi) {
|
||||
// Reserve sequence numbers for the ingested memtable. We need to reserve
|
||||
// at lease this amount for recovery. During recovery,
|
||||
@@ -713,6 +714,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
assert(last_sequence != kMaxSequenceNumber);
|
||||
const SequenceNumber current_sequence = last_sequence + 1;
|
||||
last_sequence += seq_inc;
|
||||
// Seqno assigned to this write are [current_sequence, last_sequence]
|
||||
|
||||
if (log_context.need_log_sync) {
|
||||
VersionEdit synced_wals;
|
||||
@@ -836,13 +838,17 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
if (status.ok() && w.status.ok()) {
|
||||
// w.batch contains (potentially empty) commit time batch updates,
|
||||
// only ingest wbwi if w.batch is applied to memtable successfully
|
||||
assert(wbwi->GetWriteBatch()->Count() > 0);
|
||||
|
||||
uint32_t memtable_update_count = w.batch->Count();
|
||||
SequenceNumber lb = versions_->LastSequence() + memtable_update_count + 1;
|
||||
SequenceNumber ub = versions_->LastSequence() + memtable_update_count +
|
||||
wbwi->GetWriteBatch()->Count();
|
||||
assert(ub == last_sequence);
|
||||
uint32_t wbwi_count = wbwi->GetWriteBatch()->Count();
|
||||
// Seqno assigned to this write are [last_seq + 1 - seq_inc, last_seq].
|
||||
// seq_inc includes w.batch (memtable updates) and wbwi
|
||||
// w.batch gets first `memtable_update_count` sequence numbers.
|
||||
// wbwi gets the rest `wbwi_count` sequence numbers.
|
||||
assert(seq_inc == memtable_update_count + wbwi_count);
|
||||
assert(wbwi_count > 0);
|
||||
assert(last_sequence != kMaxSequenceNumber);
|
||||
SequenceNumber lb = last_sequence + 1 - wbwi_count;
|
||||
SequenceNumber ub = last_sequence;
|
||||
if (two_write_queues_) {
|
||||
assert(ub <= versions_->LastAllocatedSequence());
|
||||
}
|
||||
@@ -1558,7 +1564,8 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
const WriteOptions& write_options,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
uint64_t* log_size,
|
||||
LogFileNumberSize& log_file_number_size) {
|
||||
LogFileNumberSize& log_file_number_size,
|
||||
SequenceNumber sequence) {
|
||||
assert(log_size != nullptr);
|
||||
|
||||
Slice log_entry = WriteBatchInternal::Contents(&merged_batch);
|
||||
@@ -1584,7 +1591,7 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
io_s = log_writer->AddRecord(write_options, log_entry);
|
||||
io_s = log_writer->AddRecord(write_options, log_entry, sequence);
|
||||
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Unlock();
|
||||
@@ -1634,7 +1641,7 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
write_options.rate_limiter_priority =
|
||||
write_group.leader->rate_limiter_priority;
|
||||
io_s = WriteToWAL(*merged_batch, write_options, log_writer, log_used,
|
||||
&log_size, log_file_number_size);
|
||||
&log_size, log_file_number_size, sequence);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
@@ -1760,7 +1767,7 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
write_options.rate_limiter_priority =
|
||||
write_group.leader->rate_limiter_priority;
|
||||
io_s = WriteToWAL(*merged_batch, write_options, log_writer, log_used,
|
||||
&log_size, log_file_number_size);
|
||||
&log_size, log_file_number_size, sequence);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
@@ -2421,7 +2428,9 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
}
|
||||
uint64_t new_log_number =
|
||||
creating_new_log ? versions_->NewFileNumber() : logfile_number_;
|
||||
const MutableCFOptions mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
// For use outside of holding DB mutex
|
||||
const MutableCFOptions mutable_cf_options_copy =
|
||||
cfd->GetLatestMutableCFOptions();
|
||||
|
||||
// Set memtable_info for memtable sealed callback
|
||||
// TODO: memtable_info for `new_imm`
|
||||
@@ -2431,7 +2440,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
memtable_info.earliest_seqno = cfd->mem()->GetEarliestSequenceNumber();
|
||||
memtable_info.num_entries = cfd->mem()->NumEntries();
|
||||
memtable_info.num_deletes = cfd->mem()->NumDeletion();
|
||||
if (!cfd->ioptions()->persist_user_defined_timestamps &&
|
||||
if (!cfd->ioptions().persist_user_defined_timestamps &&
|
||||
cfd->user_comparator()->timestamp_size() > 0) {
|
||||
const Slice& newest_udt = cfd->mem()->GetNewestUDT();
|
||||
memtable_info.newest_udt.assign(newest_udt.data(), newest_udt.size());
|
||||
@@ -2440,13 +2449,22 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
// flush happens before logging, but that should be ok.
|
||||
int num_imm_unflushed = cfd->imm()->NumNotFlushed();
|
||||
const auto preallocate_block_size =
|
||||
GetWalPreallocateBlockSize(mutable_cf_options.write_buffer_size);
|
||||
GetWalPreallocateBlockSize(mutable_cf_options_copy.write_buffer_size);
|
||||
mutex_.Unlock();
|
||||
if (creating_new_log) {
|
||||
PredecessorWALInfo info;
|
||||
log_write_mutex_.Lock();
|
||||
if (!logs_.empty()) {
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
info = PredecessorWALInfo(cur_log_writer->get_log_number(),
|
||||
cur_log_writer->file()->GetFileSize(),
|
||||
cur_log_writer->GetLastSeqnoRecorded());
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
// TODO: Write buffer size passed in should be max of all CF's instead
|
||||
// of mutable_cf_options.write_buffer_size.
|
||||
io_s = CreateWAL(write_options, new_log_number, recycle_log_number,
|
||||
preallocate_block_size, &new_log);
|
||||
preallocate_block_size, info, &new_log);
|
||||
if (s.ok()) {
|
||||
s = io_s;
|
||||
}
|
||||
@@ -2464,8 +2482,8 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
} else {
|
||||
seq = versions_->LastSequence();
|
||||
}
|
||||
new_mem =
|
||||
cfd->ConstructNewMemtable(mutable_cf_options, /*earliest_seq=*/seq);
|
||||
new_mem = cfd->ConstructNewMemtable(mutable_cf_options_copy,
|
||||
/*earliest_seq=*/seq);
|
||||
context->superversion_context.NewSuperVersion();
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
@@ -2620,8 +2638,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
}
|
||||
new_mem->Ref();
|
||||
cfd->SetMemtable(new_mem);
|
||||
InstallSuperVersionAndScheduleWork(cfd, &context->superversion_context,
|
||||
mutable_cf_options);
|
||||
InstallSuperVersionAndScheduleWork(cfd, &context->superversion_context);
|
||||
|
||||
// Notify client that memtable is sealed, now that we have successfully
|
||||
// installed a new memtable
|
||||
|
||||
@@ -97,12 +97,20 @@ class CorruptionFS : public FileSystemWrapper {
|
||||
for (size_t i = 0; i < num_reqs; ++i) {
|
||||
FSReadRequest& req = reqs[i];
|
||||
if (fs_.fs_buffer_) {
|
||||
FSAllocationPtr buffer(new char[req.len], [](void* ptr) {
|
||||
delete[] static_cast<char*>(ptr);
|
||||
});
|
||||
req.fs_scratch = std::move(buffer);
|
||||
// See https://github.com/facebook/rocksdb/pull/13195 for why we
|
||||
// want to set up our test implementation for FSAllocationPtr this
|
||||
// way.
|
||||
char* internalData = new char[req.len];
|
||||
req.status = Read(req.offset, req.len, options, &req.result,
|
||||
static_cast<char*>(req.fs_scratch.get()), dbg);
|
||||
internalData, dbg);
|
||||
|
||||
Slice* internalSlice = new Slice(internalData, req.len);
|
||||
FSAllocationPtr internalPtr(internalSlice, [](void* ptr) {
|
||||
delete[] static_cast<const char*>(
|
||||
static_cast<Slice*>(ptr)->data_);
|
||||
delete static_cast<Slice*>(ptr);
|
||||
});
|
||||
req.fs_scratch = std::move(internalPtr);
|
||||
} else {
|
||||
req.status = Read(req.offset, req.len, options, &req.result,
|
||||
req.scratch, dbg);
|
||||
|
||||
@@ -2544,6 +2544,145 @@ TEST_P(DBIteratorTest, RefreshWithSnapshot) {
|
||||
ASSERT_OK(db_->Close());
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, AutoRefreshIterator) {
|
||||
constexpr int kNumKeys = 1000;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
for (const DBIter::Direction direction :
|
||||
{DBIter::kForward, DBIter::kReverse}) {
|
||||
for (const bool auto_refresh_enabled : {false, true}) {
|
||||
for (const bool explicit_snapshot : {false, true}) {
|
||||
DestroyAndReopen(options);
|
||||
// Multi dimensional iterator:
|
||||
//
|
||||
// L0 (level iterator): [key000000]
|
||||
// L1 (table iterator): [key000001]
|
||||
// Memtable : [key000000, key000999]
|
||||
for (int i = 0; i < kNumKeys + 2; i++) {
|
||||
ASSERT_OK(Put(Key(i % kNumKeys), "val" + std::to_string(i)));
|
||||
if (i <= 1) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
if (i == 0) {
|
||||
MoveFilesToLevel(1);
|
||||
}
|
||||
}
|
||||
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<ManagedSnapshot> snapshot = nullptr;
|
||||
if (explicit_snapshot) {
|
||||
snapshot = std::make_unique<ManagedSnapshot>(db_);
|
||||
}
|
||||
read_options.snapshot =
|
||||
explicit_snapshot ? snapshot->snapshot() : nullptr;
|
||||
read_options.auto_refresh_iterator_with_snapshot = auto_refresh_enabled;
|
||||
std::unique_ptr<Iterator> iter(NewIterator(read_options));
|
||||
|
||||
int trigger_compact_on_it = kNumKeys / 2;
|
||||
|
||||
// This update should NOT be visible from the iterator.
|
||||
ASSERT_OK(Put(Key(trigger_compact_on_it + 1), "new val"));
|
||||
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
uint64_t all_memtables_size_before_refresh;
|
||||
uint64_t all_memtables_size_after_refresh;
|
||||
|
||||
std::string prop_value;
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.super-version-number",
|
||||
&prop_value));
|
||||
int superversion_number = std::stoi(prop_value);
|
||||
|
||||
std::vector<LiveFileMetaData> old_files;
|
||||
db_->GetLiveFilesMetaData(&old_files);
|
||||
|
||||
int expected_next_key_int;
|
||||
if (direction == DBIter::kForward) {
|
||||
expected_next_key_int = 0;
|
||||
iter->SeekToFirst();
|
||||
} else { // DBIter::kReverse
|
||||
expected_next_key_int = kNumKeys - 1;
|
||||
iter->SeekToLast();
|
||||
}
|
||||
|
||||
int it_num = 0;
|
||||
std::unordered_map<std::string, std::string> kvs;
|
||||
while (iter->Valid()) {
|
||||
ASSERT_OK(iter->status());
|
||||
it_num++;
|
||||
if (it_num == trigger_compact_on_it) {
|
||||
// Bump the superversion by manually scheduling flush + compaction.
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr,
|
||||
nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
|
||||
// For accuracy, capture the memtables size right before consecutive
|
||||
// iterator call to Next() will update its' stale superversion ref.
|
||||
dbfull()->GetIntProperty("rocksdb.size-all-mem-tables",
|
||||
&all_memtables_size_before_refresh);
|
||||
}
|
||||
|
||||
if (it_num == trigger_compact_on_it + 1) {
|
||||
dbfull()->GetIntProperty("rocksdb.size-all-mem-tables",
|
||||
&all_memtables_size_after_refresh);
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.super-version-number",
|
||||
&prop_value));
|
||||
uint64_t new_superversion_number = std::stoi(prop_value);
|
||||
Status expected_status_for_preexisting_files;
|
||||
if (auto_refresh_enabled && explicit_snapshot) {
|
||||
// Iterator is expected to detect its' superversion staleness.
|
||||
ASSERT_LT(superversion_number, new_superversion_number);
|
||||
// ... and since our iterator was the only reference to that very
|
||||
// superversion, we expect most of the active memory to be
|
||||
// returned upon automatical iterator refresh.
|
||||
ASSERT_GT(all_memtables_size_before_refresh,
|
||||
all_memtables_size_after_refresh);
|
||||
expected_status_for_preexisting_files = Status::NotFound();
|
||||
} else {
|
||||
ASSERT_EQ(superversion_number, new_superversion_number);
|
||||
ASSERT_EQ(all_memtables_size_after_refresh,
|
||||
all_memtables_size_before_refresh);
|
||||
expected_status_for_preexisting_files = Status::OK();
|
||||
}
|
||||
|
||||
for (const auto& file : old_files) {
|
||||
ASSERT_EQ(env_->FileExists(file.db_path + "/" + file.name),
|
||||
expected_status_for_preexisting_files);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we're visiting the keys in desired order and at most once!
|
||||
ASSERT_EQ(IdFromKey(iter->key().ToString()), expected_next_key_int);
|
||||
kvs[iter->key().ToString()] = iter->value().ToString();
|
||||
|
||||
if (direction == DBIter::kForward) {
|
||||
iter->Next();
|
||||
expected_next_key_int++;
|
||||
} else {
|
||||
iter->Prev();
|
||||
expected_next_key_int--;
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
// Data validation.
|
||||
ASSERT_EQ(kvs.size(), kNumKeys);
|
||||
for (int i = 0; i < kNumKeys; i++) {
|
||||
auto kv = kvs.find(Key(i));
|
||||
ASSERT_TRUE(kv != kvs.end());
|
||||
int val = i;
|
||||
if (i <= 1) {
|
||||
val += kNumKeys;
|
||||
}
|
||||
ASSERT_EQ(kv->second, "val" + std::to_string(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, CreationFailure) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::NewInternalIterator:StatusCallback", [](void* arg) {
|
||||
|
||||
@@ -332,7 +332,6 @@ TEST_F(DBTestXactLogIterator, TransactionLogIteratorBlobs) {
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/utilities/debug.h"
|
||||
#include "table/block_based/block_builder.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
#include "utilities/merge_operators/sortlist.h"
|
||||
@@ -120,8 +120,11 @@ TEST_F(DBMergeOperandTest, FlushedMergeOperandReadAfterFreeBug) {
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
int limit = 2;
|
||||
// Use only the latest two merge operands.
|
||||
options.merge_operator = std::make_shared<LimitedStringAppendMergeOp>(2, ',');
|
||||
options.merge_operator =
|
||||
std::make_shared<LimitedStringAppendMergeOp>(limit, ',');
|
||||
Reopen(options);
|
||||
int num_records = 4;
|
||||
int number_of_operands = 0;
|
||||
@@ -263,6 +266,7 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k3.2", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(number_of_operands, 2);
|
||||
ASSERT_EQ(values[0], "cd");
|
||||
ASSERT_EQ(values[1], "de");
|
||||
|
||||
@@ -280,6 +284,7 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k4", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(number_of_operands, 4);
|
||||
ASSERT_EQ(values[0], "ba");
|
||||
ASSERT_EQ(values[1], "cb");
|
||||
ASSERT_EQ(values[2], "dc");
|
||||
@@ -299,9 +304,26 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k5", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(number_of_operands, 4);
|
||||
ASSERT_EQ(values[0], "remember");
|
||||
ASSERT_EQ(values[1], "i");
|
||||
ASSERT_EQ(values[2], "am");
|
||||
ASSERT_EQ(values[3], "rocks");
|
||||
|
||||
// GetMergeOperands() in ReadOnly DB
|
||||
ASSERT_OK(Merge("k6", "better"));
|
||||
ASSERT_OK(Merge("k6", "call"));
|
||||
ASSERT_OK(Merge("k6", "saul"));
|
||||
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
std::vector<PinnableSlice> readonly_values(num_records);
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k6", readonly_values.data(),
|
||||
&merge_operands_info, &number_of_operands));
|
||||
ASSERT_EQ(number_of_operands, 3);
|
||||
ASSERT_EQ(readonly_values[0], "better");
|
||||
ASSERT_EQ(readonly_values[1], "call");
|
||||
ASSERT_EQ(readonly_values[2], "saul");
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, BlobDBGetMergeOperandsBasic) {
|
||||
@@ -577,6 +599,42 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBaseDeletionInImmMem) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandCallbackStopAtImm) {
|
||||
Options options = CurrentOptions();
|
||||
options.max_write_buffer_number = 10;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
ASSERT_OK(db_->PauseBackgroundWork());
|
||||
ASSERT_OK(Merge("key", "v1"));
|
||||
ASSERT_OK(dbfull()->TEST_SwitchMemtable());
|
||||
// Keep this merge in an immutable memtable
|
||||
uint64_t num_imm = 0;
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty(DB::Properties::kNumImmutableMemTable, &num_imm));
|
||||
ASSERT_EQ(num_imm, 1);
|
||||
ASSERT_OK(Merge("key", "v2"));
|
||||
|
||||
std::vector<PinnableSlice> merge_operands(2);
|
||||
GetMergeOperandsOptions merge_operands_info;
|
||||
merge_operands_info.expected_max_number_of_operands = 2;
|
||||
int num_fetched = 0;
|
||||
merge_operands_info.continue_cb = [&num_fetched](Slice /* value */) {
|
||||
num_fetched++;
|
||||
// Stop in the first immutable memtable.
|
||||
return num_fetched < 2;
|
||||
};
|
||||
int num_merge_operands = 0;
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"key", merge_operands.data(),
|
||||
&merge_operands_info, &num_merge_operands));
|
||||
ASSERT_EQ(2, num_merge_operands);
|
||||
ASSERT_EQ(2, num_fetched);
|
||||
|
||||
ASSERT_EQ("v1", merge_operands[0]);
|
||||
ASSERT_EQ("v2", merge_operands[1]);
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -120,6 +120,10 @@ TEST_F(DBMergeOperatorTest, LimitMergeOperands) {
|
||||
ASSERT_OK(Merge("k3", "de"));
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "k3", &value));
|
||||
ASSERT_EQ(value, "cd,de");
|
||||
// Tests that merge operands reach exact limit at memtable.
|
||||
ASSERT_OK(Merge("k3", "fg"));
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "k3", &value));
|
||||
ASSERT_EQ(value, "de,fg");
|
||||
|
||||
// All K4 values are in different levels
|
||||
ASSERT_OK(Merge("k4", "ab"));
|
||||
@@ -967,7 +971,7 @@ TEST_F(DBMergeOperatorTest, MaxSuccessiveMergesBaseValues) {
|
||||
|
||||
// No base value
|
||||
{
|
||||
constexpr char key[] = "key1";
|
||||
const std::string key = "key1";
|
||||
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), db_->DefaultColumnFamily(), key, foo));
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), db_->DefaultColumnFamily(), key, bar));
|
||||
@@ -990,7 +994,7 @@ TEST_F(DBMergeOperatorTest, MaxSuccessiveMergesBaseValues) {
|
||||
|
||||
// Plain base value
|
||||
{
|
||||
constexpr char key[] = "key2";
|
||||
const std::string key = "key2";
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), db_->DefaultColumnFamily(), key, foo));
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), db_->DefaultColumnFamily(), key, bar));
|
||||
@@ -1015,7 +1019,7 @@ TEST_F(DBMergeOperatorTest, MaxSuccessiveMergesBaseValues) {
|
||||
|
||||
// Wide-column base value
|
||||
{
|
||||
constexpr char key[] = "key3";
|
||||
const std::string key = "key3";
|
||||
const WideColumns columns{{kDefaultWideColumnName, foo}, {bar, baz}};
|
||||
|
||||
ASSERT_OK(db_->PutEntity(WriteOptions(), db_->DefaultColumnFamily(), key,
|
||||
|
||||
+65
-2
@@ -70,7 +70,8 @@ class DBOptionsTest : public DBTestBase {
|
||||
options.env = env_;
|
||||
ImmutableDBOptions db_options(options);
|
||||
test::RandomInitCFOptions(&options, options, rnd);
|
||||
auto sanitized_options = SanitizeOptions(db_options, options);
|
||||
auto sanitized_options =
|
||||
SanitizeCfOptions(db_options, /*read_only*/ false, options);
|
||||
auto opt_map = GetMutableCFOptionsMap(sanitized_options);
|
||||
delete options.compaction_filter;
|
||||
return opt_map;
|
||||
@@ -1451,7 +1452,6 @@ TEST_F(DBOptionsTest, ChangeCompression) {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBOptionsTest, BottommostCompressionOptsWithFallbackType) {
|
||||
// Verify the bottommost compression options still take effect even when the
|
||||
// bottommost compression type is left at its default value. Verify for both
|
||||
@@ -1596,6 +1596,69 @@ TEST_F(DBOptionsTest, TempOptionsFailTest) {
|
||||
ASSERT_FALSE(found_temp_file);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetOptionsNoManifestWrite) {
|
||||
ASSERT_OK(Put("x", "x"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// In addition to checking manifest file, we want to ensure that SetOptions
|
||||
// is essentially atomic, without releasing the DB mutex between applying
|
||||
// the options to the cfd and installing new Version and SuperVersion. We
|
||||
// probabilistically verify that by attempting to catch an inconsistency.
|
||||
auto* const cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())->cfd();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
std::optional<std::thread> t;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::LogAndApply:WakeUpAndNotDone", [&](void* arg) {
|
||||
auto* mu = static_cast<InstrumentedMutex*>(arg);
|
||||
// Option not yet modified
|
||||
ASSERT_FALSE(cfd->GetLatestMutableCFOptions().disable_auto_compactions);
|
||||
ASSERT_FALSE(
|
||||
cfd->current()->GetMutableCFOptions().disable_auto_compactions);
|
||||
ASSERT_FALSE(
|
||||
cfd->GetCurrentMutableCFOptions().disable_auto_compactions);
|
||||
t = std::thread([mu, cfd]() {
|
||||
InstrumentedMutexLock l(mu);
|
||||
// Assuming above correctness, we can only acquire the mutex after
|
||||
// options fully installed.
|
||||
ASSERT_TRUE(
|
||||
cfd->GetLatestMutableCFOptions().disable_auto_compactions);
|
||||
ASSERT_TRUE(
|
||||
cfd->current()->GetMutableCFOptions().disable_auto_compactions);
|
||||
ASSERT_TRUE(
|
||||
cfd->GetCurrentMutableCFOptions().disable_auto_compactions);
|
||||
});
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Baseline manifest file info
|
||||
std::vector<std::string> live_files;
|
||||
uint64_t orig_manifest_file_size;
|
||||
ASSERT_OK(dbfull()->GetLiveFiles(live_files, &orig_manifest_file_size));
|
||||
uint64_t orig_manifest_file_num = dbfull()->TEST_Current_Manifest_FileNo();
|
||||
|
||||
// Although this test mostly concerns SetOptions, we also include SetDBOptions
|
||||
// just for the added scope
|
||||
ASSERT_OK(db_->SetDBOptions({{"max_open_files", "100"}}));
|
||||
ASSERT_OK(db_->SetOptions({{"disable_auto_compactions", "true"}}));
|
||||
|
||||
// Verify that our above check was activated and completed
|
||||
ASSERT_TRUE(t.has_value());
|
||||
t->join();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
// Verify manifest was not written to
|
||||
uint64_t new_manifest_file_size;
|
||||
ASSERT_OK(dbfull()->GetLiveFiles(live_files, &new_manifest_file_size));
|
||||
uint64_t new_manifest_file_num = dbfull()->TEST_Current_Manifest_FileNo();
|
||||
ASSERT_EQ(orig_manifest_file_num, new_manifest_file_num);
|
||||
ASSERT_EQ(orig_manifest_file_size, new_manifest_file_size);
|
||||
|
||||
ASSERT_EQ(Get("x"), "x");
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -226,7 +226,6 @@ TEST_P(DBRateLimiterOnReadTest, Iterator) {
|
||||
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
}
|
||||
|
||||
|
||||
TEST_P(DBRateLimiterOnReadTest, VerifyChecksum) {
|
||||
if (use_direct_io_ && !IsDirectIOSupported()) {
|
||||
return;
|
||||
@@ -271,7 +270,6 @@ TEST_P(DBRateLimiterOnReadTest, VerifyFileChecksums) {
|
||||
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
}
|
||||
|
||||
|
||||
class DBRateLimiterOnWriteTest : public DBTestBase {
|
||||
public:
|
||||
explicit DBRateLimiterOnWriteTest()
|
||||
|
||||
+39
-2
@@ -14,10 +14,9 @@
|
||||
#include "rocksdb/utilities/transaction_db.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/merge_operators/string_append/stringappend2.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBSecondaryTestBase : public DBBasicTestWithTimestampBase {
|
||||
public:
|
||||
explicit DBSecondaryTestBase(const std::string& dbname)
|
||||
@@ -161,6 +160,7 @@ TEST_F(DBSecondaryTest, NonExistingDb) {
|
||||
TEST_F(DBSecondaryTest, ReopenAsSecondary) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.preserve_internal_time_seconds = 300;
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("foo", "foo_value"));
|
||||
ASSERT_OK(Put("bar", "bar_value"));
|
||||
@@ -331,6 +331,43 @@ TEST_F(DBSecondaryTest, InternalCompactionMultiLevels) {
|
||||
// cfh, input1, &result));
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, GetMergeOperands) {
|
||||
Options options;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Merge("k1", "v1"));
|
||||
ASSERT_OK(Merge("k1", "v2"));
|
||||
ASSERT_OK(Merge("k1", "v3"));
|
||||
ASSERT_OK(Merge("k1", "v4"));
|
||||
|
||||
options.max_open_files = -1;
|
||||
OpenSecondary(options);
|
||||
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
|
||||
int num_records = 4;
|
||||
int number_of_operands = 0;
|
||||
std::vector<PinnableSlice> values(num_records);
|
||||
GetMergeOperandsOptions merge_operands_info;
|
||||
merge_operands_info.expected_max_number_of_operands = num_records;
|
||||
|
||||
auto cfh = db_secondary_->DefaultColumnFamily();
|
||||
|
||||
const Status s = db_secondary_->GetMergeOperands(
|
||||
ReadOptions(), cfh, "k1", values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsMergeInProgress());
|
||||
|
||||
ASSERT_EQ(number_of_operands, 4);
|
||||
ASSERT_EQ(values[0].ToString(), "v1");
|
||||
ASSERT_EQ(values[1].ToString(), "v2");
|
||||
ASSERT_EQ(values[2].ToString(), "v3");
|
||||
ASSERT_EQ(values[3].ToString(), "v4");
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, InternalCompactionCompactedFiles) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
|
||||
@@ -1991,7 +1991,6 @@ TEST_F(DBSSTTest, DBWithSFMForBlobFilesAtomicFlush) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -198,7 +198,6 @@ TEST_F(DBStatisticsTest, ExcludeTickers) {
|
||||
ASSERT_GT(options.statistics->getTickerCount(BYTES_READ), 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBStatisticsTest, VerifyChecksumReadStat) {
|
||||
Options options = CurrentOptions();
|
||||
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "util/atomic.h"
|
||||
#include "util/random.h"
|
||||
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// A helper function that ensures the table properties returned in
|
||||
@@ -230,6 +229,56 @@ TEST_F(DBTablePropertiesTest, CreateOnDeletionCollectorFactory) {
|
||||
ASSERT_EQ(0.5, del_factory->GetDeletionRatio());
|
||||
}
|
||||
|
||||
TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesByLevelTest) {
|
||||
Random rnd(202);
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = 4096;
|
||||
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 = 2048;
|
||||
options.max_bytes_for_level_base = 40960;
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
options.hard_pending_compaction_bytes_limit = 16 * 1024;
|
||||
options.num_levels = 8;
|
||||
options.env = env_;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// build a decent LSM
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
EXPECT_OK(Put(test::RandomKey(&rnd, 5), rnd.RandomString(102)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
if (NumTableFilesAtLevel(0) == 0) {
|
||||
EXPECT_OK(Put(test::RandomKey(&rnd, 5), rnd.RandomString(102)));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
ASSERT_OK(db_->PauseBackgroundWork());
|
||||
|
||||
// Ensure that we have at least L0, L1 and L2
|
||||
ASSERT_GT(NumTableFilesAtLevel(0), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(2), 0);
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
db_->GetColumnFamilyMetaData(&cf_meta);
|
||||
std::vector<std::unique_ptr<TablePropertiesCollection>> levels_props;
|
||||
ASSERT_OK(db_->GetPropertiesOfTablesByLevel(db_->DefaultColumnFamily(),
|
||||
&levels_props));
|
||||
for (int i = 0; i < 8; i++) {
|
||||
const std::unique_ptr<TablePropertiesCollection>& level_props =
|
||||
levels_props[i];
|
||||
ASSERT_EQ(level_props->size(), cf_meta.levels[i].files.size());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
// Test params:
|
||||
// 1) whether to enable user-defined timestamps
|
||||
class DBTablePropertiesInRangeTest : public DBTestBase,
|
||||
@@ -293,7 +342,7 @@ class DBTablePropertiesInRangeTest : public DBTestBase,
|
||||
keys.reserve(range_size * 2);
|
||||
for (auto& r : ranges) {
|
||||
auto [start, limit] = MaybeAddTimestampsToRange(
|
||||
&r.start, &r.limit, ts_sz, &keys.emplace_back(), &keys.emplace_back(),
|
||||
r.start, r.limit, ts_sz, &keys.emplace_back(), &keys.emplace_back(),
|
||||
/*exclusive_end=*/false);
|
||||
EXPECT_TRUE(start.has_value());
|
||||
EXPECT_TRUE(limit.has_value());
|
||||
@@ -744,7 +793,6 @@ INSTANTIATE_TEST_CASE_P(DBTablePropertiesTest, DBTablePropertiesTest,
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
@@ -703,7 +703,6 @@ TEST_P(DBTestTailingIterator, SeekToFirstWithUpperBoundBug) {
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
+204
-11
@@ -59,11 +59,13 @@
|
||||
#include "rocksdb/utilities/checkpoint.h"
|
||||
#include "rocksdb/utilities/optimistic_transaction_db.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/random.h"
|
||||
#include "util/rate_limiter_impl.h"
|
||||
@@ -1161,7 +1163,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);
|
||||
@@ -3181,6 +3182,15 @@ class ModelDB : public DB {
|
||||
return Status();
|
||||
}
|
||||
|
||||
using DB::GetPropertiesOfTablesByLevel;
|
||||
Status GetPropertiesOfTablesByLevel(
|
||||
ColumnFamilyHandle* /* column_family */,
|
||||
std::vector<
|
||||
std::unique_ptr<TablePropertiesCollection>>* /* props_by_level */)
|
||||
override {
|
||||
return Status();
|
||||
}
|
||||
|
||||
using DB::KeyMayExist;
|
||||
bool KeyMayExist(const ReadOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
@@ -3410,8 +3420,6 @@ class ModelDB : public DB {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
Status DeleteFile(std::string /*name*/) override { return Status::OK(); }
|
||||
|
||||
Status GetUpdatesSince(
|
||||
ROCKSDB_NAMESPACE::SequenceNumber,
|
||||
std::unique_ptr<ROCKSDB_NAMESPACE::TransactionLogIterator>*,
|
||||
@@ -3715,6 +3723,139 @@ TEST_F(DBTest, BlockBasedTablePrefixIndexTest) {
|
||||
ASSERT_EQ("v2", Get("k2"));
|
||||
}
|
||||
|
||||
TEST_F(DBTest, SetOptionsEffectiveInSuperVersions) {
|
||||
// Basically, to test the SetOptions take effect with (and only with)
|
||||
// new SuperVersions, and remain in effect through some things like
|
||||
// flush and compaction, we use some queries that depend on the current
|
||||
// prefix extractor.
|
||||
//
|
||||
// Making the semantics of read options dependent on the current state of
|
||||
// mutable options is kind of an anti-pattern that prefix_seek_opt_in_only
|
||||
// is helping to phase out. However, this is useful for rather directly
|
||||
// testing the expected behavior of mutable options handling.
|
||||
ReadOptions ropts;
|
||||
ropts.prefix_same_as_start = true;
|
||||
Options options = CurrentOptions();
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(5));
|
||||
options.prefix_seek_opt_in_only = false;
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("goat1", "g1"));
|
||||
ASSERT_OK(Put("goat2", "g2"));
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ropts));
|
||||
|
||||
auto VerifyTransform4 = [&](int caller_line) {
|
||||
SCOPED_TRACE("Called from " + std::to_string(caller_line));
|
||||
// Nothing with this prefix
|
||||
iter->Seek("game1");
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
iter->Seek("goat1");
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("goat1", iter->key());
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("goat2", iter->key());
|
||||
};
|
||||
|
||||
auto VerifyTransform5 = [&](int caller_line) {
|
||||
SCOPED_TRACE("Called from " + std::to_string(caller_line));
|
||||
// Nothing with this prefix
|
||||
iter->Seek("game1");
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
iter->Seek("goat1");
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("goat1", iter->key());
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
iter->Seek("goat2");
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("goat2", iter->key());
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
};
|
||||
|
||||
for (int i = 0;; ++i) {
|
||||
SCOPED_TRACE("Iteration " + std::to_string(i));
|
||||
// Baseline
|
||||
VerifyTransform5(__LINE__);
|
||||
|
||||
if (i == 0) {
|
||||
// Test a "normal" change with nothing happening in parallel
|
||||
ASSERT_OK(db_->SetOptions({{"prefix_extractor", "fixed:4"}}));
|
||||
|
||||
// Iterator still uses old superversion
|
||||
VerifyTransform5(__LINE__);
|
||||
|
||||
// Refresh updates the SuperVersion
|
||||
ASSERT_OK(iter->Refresh());
|
||||
} else if (i == 1) {
|
||||
// Test a setting change in parallel with flush
|
||||
iter = nullptr;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:num_memtables", [&](void*) {
|
||||
// During flush, without DB mutex held
|
||||
ASSERT_OK(db_->SetOptions({{"prefix_extractor", "fixed:4"}}));
|
||||
iter.reset(db_->NewIterator(ropts));
|
||||
VerifyTransform4(__LINE__);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
// Callback was called
|
||||
ASSERT_NE(iter, nullptr);
|
||||
} else if (i == 2) {
|
||||
// Test a setting change in parallel with compaction
|
||||
iter = nullptr;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():EndStatusSet", [&](void*) {
|
||||
// During compaction, without DB mutex held
|
||||
ASSERT_OK(db_->SetOptions({{"prefix_extractor", "fixed:4"}}));
|
||||
iter.reset(db_->NewIterator(ropts));
|
||||
VerifyTransform4(__LINE__);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
// Need data overlapping that L0 file to prevent trivial move
|
||||
ASSERT_OK(Put("aaaaa", "a"));
|
||||
ASSERT_OK(Put("zzzzz", "a"));
|
||||
ASSERT_OK(CompactRange({}, {}, {}));
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
// Callback was called
|
||||
ASSERT_NE(iter, nullptr);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
// Change has taken effect
|
||||
VerifyTransform4(__LINE__);
|
||||
// Same after a new iterator (in case a new SuperVersion reverted the
|
||||
// setting)
|
||||
iter.reset(db_->NewIterator(ropts));
|
||||
VerifyTransform4(__LINE__);
|
||||
|
||||
// Back to baseline setting
|
||||
ASSERT_OK(db_->SetOptions({{"prefix_extractor", "fixed:5"}}));
|
||||
// New iterator uses latest SuperVersion
|
||||
iter.reset(db_->NewIterator(ropts));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest, BlockBasedTablePrefixHashIndexTest) {
|
||||
// create a DB with block prefix index
|
||||
BlockBasedTableOptions table_options;
|
||||
@@ -4378,7 +4519,6 @@ TEST_F(DBTest, ConcurrentMemtableNotSupported) {
|
||||
ASSERT_NOK(db_->CreateColumnFamily(cf_options, "name", &handle));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBTest, SanitizeNumThreads) {
|
||||
for (int attempt = 0; attempt < 2; attempt++) {
|
||||
const size_t kTotalTasks = 8;
|
||||
@@ -5161,7 +5301,6 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
for (int i = 0; i < kNKeys; i++) {
|
||||
keys[i] = i;
|
||||
}
|
||||
RandomShuffle(std::begin(keys), std::end(keys));
|
||||
|
||||
Random rnd(301);
|
||||
Options options;
|
||||
@@ -5186,7 +5325,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
options.compression_per_level[0] = kNoCompression;
|
||||
// No compression for the Ln whre L0 is compacted to
|
||||
options.compression_per_level[1] = kNoCompression;
|
||||
// Snpapy compression for Ln+1
|
||||
// Snappy compression for Ln+1
|
||||
options.compression_per_level[2] = kSnappyCompression;
|
||||
|
||||
OnFileDeletionListener* listener = new OnFileDeletionListener();
|
||||
@@ -5198,6 +5337,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
// 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());
|
||||
@@ -5216,8 +5356,9 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
// 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 = 21; i < 120; i++) {
|
||||
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());
|
||||
@@ -5238,9 +5379,44 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
}));
|
||||
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);
|
||||
ASSERT_OK(dbfull()->DeleteFile(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());
|
||||
|
||||
@@ -5287,6 +5463,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
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;
|
||||
|
||||
@@ -5788,7 +5965,6 @@ TEST_F(DBTest, FileCreationRandomFailure) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBTest, DynamicMiscOptions) {
|
||||
// Test max_sequential_skip_in_iterations
|
||||
Options options;
|
||||
@@ -5919,6 +6095,11 @@ TEST_F(DBTest, L0L1L2AndUpHitCounter) {
|
||||
}
|
||||
|
||||
TEST_F(DBTest, EncodeDecompressedBlockSizeTest) {
|
||||
bool& allow_unsupported_fv =
|
||||
BlockBasedTableFactory::AllowUnsupportedFormatVersion();
|
||||
SaveAndRestore guard(&allow_unsupported_fv);
|
||||
ASSERT_FALSE(allow_unsupported_fv);
|
||||
|
||||
// iter 0 -- zlib
|
||||
// iter 1 -- bzip2
|
||||
// iter 2 -- lz4
|
||||
@@ -5941,7 +6122,16 @@ TEST_F(DBTest, EncodeDecompressedBlockSizeTest) {
|
||||
table_options.format_version = first_table_version;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
Options options = CurrentOptions();
|
||||
|
||||
// Hack to generate old files (checked in factory construction)
|
||||
allow_unsupported_fv = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ASSERT_EQ(options.table_factory->GetOptions<BlockBasedTableOptions>()
|
||||
->format_version,
|
||||
first_table_version);
|
||||
// Able to read old files without the hack
|
||||
allow_unsupported_fv = false;
|
||||
|
||||
options.create_if_missing = true;
|
||||
options.compression = comp;
|
||||
DestroyAndReopen(options);
|
||||
@@ -5953,9 +6143,14 @@ TEST_F(DBTest, EncodeDecompressedBlockSizeTest) {
|
||||
// compressible string
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
table_options.format_version = first_table_version == 1 ? 2 : 1;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
// format_version (for writing) is sanitized to minimum supported
|
||||
ASSERT_EQ(options.table_factory->GetOptions<BlockBasedTableOptions>()
|
||||
->format_version,
|
||||
BlockBasedTableFactory::kMinSupportedFormatVersion);
|
||||
Reopen(options);
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
@@ -7230,7 +7425,6 @@ TEST_F(DBTest, ReusePinnableSlice) {
|
||||
1);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBTest, DeletingOldWalAfterDrop) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"Test:AllowFlushes", "DBImpl::BGWorkFlush"},
|
||||
@@ -7355,7 +7549,6 @@ TEST_F(DBTest, LargeBlockSizeTest) {
|
||||
ASSERT_NOK(TryReopenWithColumnFamilies({"default", "pikachu"}, options));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBTest, CreationTimeOfOldestFile) {
|
||||
const int kNumKeysPerFile = 32;
|
||||
const int kNumLevelFiles = 2;
|
||||
|
||||
+5
-14
@@ -135,7 +135,6 @@ TEST_F(DBTest2, PartitionedIndexUserToInternalKey) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PrefixFullBloomWithReverseComparator
|
||||
: public DBTestBase,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
@@ -1476,8 +1475,7 @@ TEST_P(PresetCompressionDictTest, Flush) {
|
||||
// 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 &&
|
||||
compression_type_ != kZSTDNotFinalCompression) {
|
||||
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.
|
||||
@@ -1554,8 +1552,7 @@ TEST_P(PresetCompressionDictTest, CompactNonBottommost) {
|
||||
// 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 &&
|
||||
compression_type_ != kZSTDNotFinalCompression) {
|
||||
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.
|
||||
@@ -1616,8 +1613,7 @@ TEST_P(PresetCompressionDictTest, CompactBottommost) {
|
||||
// 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 &&
|
||||
compression_type_ != kZSTDNotFinalCompression) {
|
||||
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.
|
||||
@@ -1977,7 +1973,6 @@ TEST_F(DBTest2, CompactionStall) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBTest2, FirstSnapshotTest) {
|
||||
Options options;
|
||||
options.write_buffer_size = 100000; // Small write buffer
|
||||
@@ -3600,7 +3595,6 @@ TEST_F(DBTest2, OptimizeForSmallDB) {
|
||||
value.Reset();
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBTest2, IterRaceFlush1) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
|
||||
@@ -4104,7 +4098,6 @@ TEST_F(DBTest2, ReadCallbackTest) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBTest2, LiveFilesOmitObsoleteFiles) {
|
||||
// Regression test for race condition where an obsolete file is returned to
|
||||
// user as a "live file" but then deleted, all while file deletions are
|
||||
@@ -4432,7 +4425,7 @@ TEST_F(DBTest2, TraceAndReplay) {
|
||||
db2->NewDefaultReplayer(handles, std::move(trace_reader), &replayer));
|
||||
|
||||
TraceExecutionResultHandler res_handler;
|
||||
std::function<void(Status, std::unique_ptr<TraceRecordResult> &&)> res_cb =
|
||||
std::function<void(Status, std::unique_ptr<TraceRecordResult>&&)> res_cb =
|
||||
[&res_handler](Status exec_s, std::unique_ptr<TraceRecordResult>&& res) {
|
||||
ASSERT_TRUE(exec_s.ok() || exec_s.IsNotSupported());
|
||||
if (res != nullptr) {
|
||||
@@ -5164,7 +5157,6 @@ TEST_F(DBTest2, TraceWithFilter) {
|
||||
ASSERT_EQ(count, 6);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBTest2, PinnableSliceAndMmapReads) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -7693,7 +7685,6 @@ TEST_F(DBTest2, RecoverEpochNumber) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBTest2, RenameDirectory) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
@@ -8006,7 +7997,7 @@ TEST_F(DBTest2, GetLatestSeqAndTsForKey) {
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(GET_HIT_L0));
|
||||
}
|
||||
|
||||
#if defined(ZSTD_ADVANCED)
|
||||
#if defined(ZSTD)
|
||||
TEST_F(DBTest2, ZSTDChecksum) {
|
||||
// Verify that corruption during decompression is caught.
|
||||
Options options = CurrentOptions();
|
||||
|
||||
+14
-5
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "cache/cache_reservation_manager.h"
|
||||
#include "db/forward_iterator.h"
|
||||
#include "env/fs_readonly.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "port/lang.h"
|
||||
#include "rocksdb/cache.h"
|
||||
@@ -122,7 +123,6 @@ DBTestBase::~DBTestBase() {
|
||||
}
|
||||
|
||||
bool DBTestBase::ShouldSkipOptions(int option_config, int skip_mask) {
|
||||
|
||||
if ((skip_mask & kSkipUniversalCompaction) &&
|
||||
(option_config == kUniversalCompaction ||
|
||||
option_config == kUniversalCompactionMultiLevel ||
|
||||
@@ -717,6 +717,17 @@ Status DBTestBase::ReadOnlyReopen(const Options& options) {
|
||||
return DB::OpenForReadOnly(options, dbname_, &db_);
|
||||
}
|
||||
|
||||
Status DBTestBase::EnforcedReadOnlyReopen(const Options& options) {
|
||||
Close();
|
||||
Options options_copy = options;
|
||||
MaybeInstallTimeElapseOnlySleep(options_copy);
|
||||
auto fs_read_only =
|
||||
std::make_shared<ReadOnlyFileSystem>(env_->GetFileSystem());
|
||||
env_read_only_ = std::make_shared<CompositeEnvWrapper>(env_, fs_read_only);
|
||||
options_copy.env = env_read_only_.get();
|
||||
return DB::OpenForReadOnly(options_copy, dbname_, &db_);
|
||||
}
|
||||
|
||||
Status DBTestBase::TryReopen(const Options& options) {
|
||||
Close();
|
||||
last_options_.table_factory.reset();
|
||||
@@ -1208,7 +1219,6 @@ std::string DBTestBase::FilesPerLevel(int cf) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::vector<uint64_t> DBTestBase::GetBlobFileNumbers() {
|
||||
VersionSet* const versions = dbfull()->GetVersionSet();
|
||||
assert(versions);
|
||||
@@ -1667,7 +1677,7 @@ void DBTestBase::VerifyDBFromMap(
|
||||
data_iter = true_data.begin();
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next(), ++data_iter) {
|
||||
ASSERT_EQ(iter->key().ToString(), data_iter->first);
|
||||
ASSERT_EQ(iter->value().ToString(), data_iter->second);
|
||||
ASSERT_EQ(iter->value().ToString(), data_iter->second);
|
||||
iter_cnt++;
|
||||
total_reads++;
|
||||
}
|
||||
@@ -1683,7 +1693,7 @@ void DBTestBase::VerifyDBFromMap(
|
||||
auto data_rev = true_data.rbegin();
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev(), data_rev++) {
|
||||
ASSERT_EQ(iter->key().ToString(), data_rev->first);
|
||||
ASSERT_EQ(iter->value().ToString(), data_rev->second);
|
||||
ASSERT_EQ(iter->value().ToString(), data_rev->second);
|
||||
iter_cnt++;
|
||||
total_reads++;
|
||||
}
|
||||
@@ -1765,7 +1775,6 @@ void DBTestBase::VerifyDBInternal(
|
||||
iter->~InternalIterator();
|
||||
}
|
||||
|
||||
|
||||
uint64_t DBTestBase::GetNumberOfSstFilesForColumnFamily(
|
||||
DB* db, std::string column_family_name) {
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
|
||||
@@ -1062,6 +1062,7 @@ class DBTestBase : public testing::Test {
|
||||
MockEnv* mem_env_;
|
||||
Env* encrypted_env_;
|
||||
SpecialEnv* env_;
|
||||
std::shared_ptr<Env> env_read_only_;
|
||||
std::shared_ptr<Env> env_guard_;
|
||||
DB* db_;
|
||||
std::vector<ColumnFamilyHandle*> handles_;
|
||||
@@ -1106,6 +1107,11 @@ class DBTestBase : public testing::Test {
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
// Expects valid key created by Key().
|
||||
static int IdFromKey(const std::string& key) {
|
||||
return std::stoi(key.substr(3));
|
||||
}
|
||||
|
||||
static bool ShouldSkipOptions(int option_config, int skip_mask = kNoSkip);
|
||||
|
||||
// Switch to a fresh database with the next option configuration to
|
||||
@@ -1173,6 +1179,9 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
Status ReadOnlyReopen(const Options& options);
|
||||
|
||||
// With a filesystem wrapper that fails on attempted write
|
||||
Status EnforcedReadOnlyReopen(const Options& options);
|
||||
|
||||
Status TryReopen(const Options& options);
|
||||
|
||||
bool IsDirectIOSupported();
|
||||
|
||||
@@ -2369,7 +2369,6 @@ TEST_F(DBTestUniversalCompaction2, PeriodicCompactionOffpeak) {
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
+201
-12
@@ -1153,6 +1153,7 @@ TEST_F(DBWALTest, FullPurgePreservesRecycledLog) {
|
||||
dbfull()->FindObsoleteFiles(&job_context, true /* force */);
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
dbfull()->PurgeObsoleteFiles(job_context);
|
||||
job_context.Clean();
|
||||
|
||||
if (i == 0) {
|
||||
ASSERT_OK(
|
||||
@@ -1838,9 +1839,194 @@ class RecoveryTestHelper {
|
||||
}
|
||||
};
|
||||
|
||||
class DBWALTestWithParams : public DBWALTestBase,
|
||||
public ::testing::WithParamInterface<
|
||||
std::tuple<bool, int, int, CompressionType>> {
|
||||
TEST_F(DBWALTest, TrackAndVerifyWALsRecycleWAL) {
|
||||
Options options = CurrentOptions();
|
||||
options.avoid_flush_during_shutdown = true;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
options.recycle_log_file_num = 1;
|
||||
options.track_and_verify_wals = true;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("key_ignore", "wal_to_recycle"));
|
||||
ASSERT_OK(Put("key_ignore1", "wal_to_recycle"));
|
||||
ASSERT_OK(Put("key_ignore2", "wal_to_recycle"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put("key_ignore", "wal_to_recycle"));
|
||||
ASSERT_OK(Put("key_ignore1", "wal_to_recycle"));
|
||||
ASSERT_OK(Put("key_ignore2", "wal_to_recycle"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Stop background flush to avoid deleting any WAL
|
||||
options.env->SetBackgroundThreads(1, Env::HIGH);
|
||||
test::SleepingBackgroundTask sleeping_task;
|
||||
options.env->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
&sleeping_task, Env::Priority::HIGH);
|
||||
|
||||
// Recycle the first WAL
|
||||
ASSERT_OK(Put("key1", "old_value"));
|
||||
// Recycle the second WAL
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(Put("key1", "new_value"));
|
||||
|
||||
// Create a WAL hole on sequence number by truncating the first WAL to 0 byte
|
||||
VectorWalPtr log_files;
|
||||
ASSERT_OK(db_->GetSortedWalFiles(log_files));
|
||||
ASSERT_EQ(log_files.size(), 2);
|
||||
std::string log_name = LogFileName(dbname_, log_files.front()->LogNumber());
|
||||
Close();
|
||||
// Drop `Put("key1", "old_value")` in the first WAL
|
||||
ASSERT_OK(test::TruncateFile(options.env, log_name, 0 /* new_length */));
|
||||
|
||||
Status s = DB::Open(options, dbname_, &db_);
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
ASSERT_EQ("wal_to_recycle", Get("key_ignore2"));
|
||||
ASSERT_EQ("NOT_FOUND", Get("key1"));
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
class DBWALTrackAndVerifyWALsWithParamsTest
|
||||
: public DBWALTestBase,
|
||||
public ::testing::WithParamInterface<WALRecoveryMode> {
|
||||
public:
|
||||
DBWALTrackAndVerifyWALsWithParamsTest()
|
||||
: DBWALTestBase("/db_wal_track_and_verify_wals_with_params_test") {}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBWALTrackAndVerifyWALsWithParamsTest,
|
||||
DBWALTrackAndVerifyWALsWithParamsTest,
|
||||
::testing::Values(WALRecoveryMode::kTolerateCorruptedTailRecords,
|
||||
WALRecoveryMode::kAbsoluteConsistency,
|
||||
WALRecoveryMode::kPointInTimeRecovery,
|
||||
WALRecoveryMode::kSkipAnyCorruptedRecords));
|
||||
|
||||
TEST_P(DBWALTrackAndVerifyWALsWithParamsTest, Basic) {
|
||||
Options options = CurrentOptions();
|
||||
options.avoid_flush_during_shutdown = true;
|
||||
options.track_and_verify_wals = true;
|
||||
options.wal_recovery_mode = GetParam();
|
||||
|
||||
// Stop background flush to avoid deleting any WAL
|
||||
options.env->SetBackgroundThreads(1, Env::HIGH);
|
||||
test::SleepingBackgroundTask sleeping_task;
|
||||
options.env->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
&sleeping_task, Env::Priority::HIGH);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("key1", "old_value"));
|
||||
SequenceNumber last_seqno_recorded_in_fist_wal =
|
||||
dbfull()->GetLatestSequenceNumber();
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(Put("key1", "new_value"));
|
||||
|
||||
VectorWalPtr log_files;
|
||||
ASSERT_OK(db_->GetSortedWalFiles(log_files));
|
||||
ASSERT_EQ(log_files.size(), 2);
|
||||
uint64_t first_log_number = log_files.front()->LogNumber();
|
||||
std::string first_log_name = LogFileName(dbname_, first_log_number);
|
||||
std::string second_log_name =
|
||||
LogFileName(dbname_, log_files.back()->LogNumber());
|
||||
|
||||
if (i == 0) {
|
||||
// Delete the obsolete WAL and verify it will not be seen as a WAL hole
|
||||
sleeping_task.WakeUp();
|
||||
sleeping_task.WaitUntilDone();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
// Stop background flush to avoid deleting any WAL
|
||||
sleeping_task.Reset();
|
||||
options.env->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
&sleeping_task, Env::Priority::HIGH);
|
||||
Close();
|
||||
} else if (i == 1) {
|
||||
// Create a WAL hole on WAL number by deleting the first WAL and verify
|
||||
// the hole will be detected
|
||||
Close();
|
||||
ASSERT_OK(options.env->DeleteFile(first_log_name));
|
||||
} else if (i == 2) {
|
||||
// Create a WAL hole on sequence number by truncating the first WAL and
|
||||
// verify the hole will be detected
|
||||
Close();
|
||||
ASSERT_OK(
|
||||
test::TruncateFile(options.env, first_log_name, 0 /* new_length */));
|
||||
} else if (i == 3) {
|
||||
// Create a WAL hole on size difference by truncating the first WAL and
|
||||
// mocking a correct sequence number to force triggering corruption based
|
||||
// on size instead of sequence number and verify the hole will be detected
|
||||
Close();
|
||||
ASSERT_OK(
|
||||
test::TruncateFile(options.env, first_log_name, 0 /* new_length */));
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::UpdatePredecessorWALInfo", [&](void* arg) {
|
||||
std::pair<uint64_t, SequenceNumber*>* pair =
|
||||
static_cast<std::pair<uint64_t, SequenceNumber*>*>(arg);
|
||||
if (pair->first == first_log_number) {
|
||||
*(pair->second) = last_seqno_recorded_in_fist_wal;
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
} else if (i == 4) {
|
||||
// Delete all wals and verify opening a DB with no WAL will be detected
|
||||
Close();
|
||||
ASSERT_OK(options.env->DeleteFile(first_log_name));
|
||||
ASSERT_OK(options.env->DeleteFile(second_log_name));
|
||||
}
|
||||
|
||||
Status s = DB::Open(options, dbname_, &db_);
|
||||
|
||||
if (i == 0) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("new_value", Get("key1"));
|
||||
continue;
|
||||
} else if (i == 3) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
} else if (i == 4) {
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(
|
||||
s.ToString().find("Opening an existing DB with no WAL files") !=
|
||||
std::string::npos);
|
||||
Close();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (options.wal_recovery_mode == WALRecoveryMode::kPointInTimeRecovery) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("NOT_FOUND", Get("key1"));
|
||||
} else if (options.wal_recovery_mode ==
|
||||
WALRecoveryMode::kAbsoluteConsistency ||
|
||||
options.wal_recovery_mode ==
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords) {
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
std::string msg;
|
||||
if (i == 1) {
|
||||
msg = "Missing WAL";
|
||||
} else if (i == 2) {
|
||||
msg = "Mismatched last sequence number recorded in the WAL";
|
||||
} else if (i == 3) {
|
||||
msg = "Mismatched size of the WAL";
|
||||
}
|
||||
ASSERT_TRUE(s.ToString().find(msg) != std::string::npos);
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("new_value", Get("key1"));
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
class DBWALTestWithParams
|
||||
: public DBWALTestBase,
|
||||
public ::testing::WithParamInterface<
|
||||
std::tuple<bool, int, int, CompressionType, bool>> {
|
||||
public:
|
||||
DBWALTestWithParams() : DBWALTestBase("/db_wal_test_with_params") {}
|
||||
};
|
||||
@@ -1853,7 +2039,8 @@ INSTANTIATE_TEST_CASE_P(
|
||||
RecoveryTestHelper::kWALFilesCount,
|
||||
1),
|
||||
::testing::Values(CompressionType::kNoCompression,
|
||||
CompressionType::kZSTD)));
|
||||
CompressionType::kZSTD),
|
||||
::testing::Bool()));
|
||||
|
||||
class DBWALTestWithParamsVaryingRecoveryMode
|
||||
: public DBWALTestBase,
|
||||
@@ -1891,6 +2078,7 @@ TEST_P(DBWALTestWithParams, kTolerateCorruptedTailRecords) {
|
||||
|
||||
// Fill data for testing
|
||||
Options options = CurrentOptions();
|
||||
options.track_and_verify_wals = std::get<4>(GetParam());
|
||||
const size_t row_count = RecoveryTestHelper::FillData(this, &options);
|
||||
// test checksum failure or parsing
|
||||
RecoveryTestHelper::CorruptWAL(this, options, corrupt_offset * .3,
|
||||
@@ -1914,6 +2102,7 @@ TEST_P(DBWALTestWithParams, kTolerateCorruptedTailRecords) {
|
||||
TEST_P(DBWALTestWithParams, kAbsoluteConsistency) {
|
||||
// Verify clean slate behavior
|
||||
Options options = CurrentOptions();
|
||||
options.track_and_verify_wals = std::get<4>(GetParam());
|
||||
const size_t row_count = RecoveryTestHelper::FillData(this, &options);
|
||||
options.create_if_missing = false;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
@@ -2124,7 +2313,6 @@ TEST_F(DBWALTest, FixSyncWalOnObseletedWalWithNewManifestCausingMissingWAL) {
|
||||
wal_synced = true;
|
||||
});
|
||||
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
@@ -2165,6 +2353,7 @@ TEST_P(DBWALTestWithParams, kPointInTimeRecovery) {
|
||||
|
||||
// Fill data for testing
|
||||
Options options = CurrentOptions();
|
||||
options.track_and_verify_wals = std::get<4>(GetParam());
|
||||
options.wal_compression = compression_type;
|
||||
const size_t row_count = RecoveryTestHelper::FillData(this, &options);
|
||||
|
||||
@@ -2222,6 +2411,7 @@ TEST_P(DBWALTestWithParams, kSkipAnyCorruptedRecords) {
|
||||
|
||||
// Fill data for testing
|
||||
Options options = CurrentOptions();
|
||||
options.track_and_verify_wals = std::get<4>(GetParam());
|
||||
options.wal_compression = compression_type;
|
||||
const size_t row_count = RecoveryTestHelper::FillData(this, &options);
|
||||
|
||||
@@ -2699,12 +2889,12 @@ TEST_F(DBWALTest, ReadOnlyRecoveryNoTruncate) {
|
||||
// create DB and close with file truncate disabled
|
||||
std::atomic_bool enable_truncate{false};
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"PosixWritableFile::Close", [&](void* arg) {
|
||||
if (!enable_truncate) {
|
||||
*(static_cast<size_t*>(arg)) = 0;
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack("PosixWritableFile::Close",
|
||||
[&](void* arg) {
|
||||
if (!enable_truncate) {
|
||||
*(static_cast<size_t*>(arg)) = 0;
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
@@ -2793,7 +2983,6 @@ TEST_F(DBWALTest, WalInManifestButNotInSortedWals) {
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBWALTest, WalTermTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
|
||||
@@ -1480,6 +1480,55 @@ TEST_F(DBBasicTestWithTimestamp, ReseekToUserKeyBeforeSavedKey) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp,
|
||||
FIXME_ReverseIterationWithBlobAndUnpreparedValue) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
options.enable_blob_files = true;
|
||||
options.max_sequential_skip_in_iterations = 0;
|
||||
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
constexpr uint64_t kMaxKey = 1024;
|
||||
|
||||
const std::vector<std::string> write_timestamps = {Timestamp(1, 0),
|
||||
Timestamp(3, 0)};
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
for (size_t i = 0; i < write_timestamps.size(); ++i) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key1(key), write_timestamps[i],
|
||||
"value" + std::to_string(i)));
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
{
|
||||
const std::string read_timestamp_str = Timestamp(4, 0);
|
||||
const Slice read_timestamp(read_timestamp_str);
|
||||
|
||||
ReadOptions read_opts;
|
||||
read_opts.timestamp = &read_timestamp;
|
||||
read_opts.allow_unprepared_value = true;
|
||||
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
|
||||
it->SeekForPrev(Key1(kMaxKey));
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_OK(it->status());
|
||||
|
||||
// FIXME: PrepareValue() should succeed and status() should remain OK
|
||||
ASSERT_FALSE(it->PrepareValue());
|
||||
ASSERT_TRUE(it->status().IsCorruption());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MultiGetWithFastLocalBloom) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -3257,7 +3306,6 @@ TEST_F(DBBasicTestWithTimestamp, MultiGetNoReturnTs) {
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Timestamp, DBBasicTestWithTimestampCompressionSettings,
|
||||
::testing::Combine(
|
||||
|
||||
@@ -780,7 +780,6 @@ TEST_P(DBWriteBufferManagerTest, MixedSlowDownOptionsMultipleDB) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
|
||||
// Tests a `WriteBufferManager` constructed with `allow_stall == false` does not
|
||||
// thrash memtable switching when full and a CF receives multiple writes.
|
||||
// Instead, we expect to switch a CF's memtable for flush only when that CF does
|
||||
|
||||
+70
-6
@@ -83,6 +83,8 @@ extern const ValueType kValueTypeForSeekForPrev;
|
||||
|
||||
// A range of user keys used internally by RocksDB. Also see `Range` used by
|
||||
// public APIs.
|
||||
// TODO: merge with Range in pubic API, but this is generally inclusive limit
|
||||
// and it is maybe exclusive limit
|
||||
struct UserKeyRange {
|
||||
// In case of user_defined timestamp, if enabled, `start` and `limit` should
|
||||
// include user_defined timestamps.
|
||||
@@ -93,18 +95,17 @@ struct UserKeyRange {
|
||||
UserKeyRange(const Slice& s, const Slice& l) : start(s), limit(l) {}
|
||||
};
|
||||
|
||||
// A range of user keys used internally by RocksDB. Also see `RangePtr` used by
|
||||
// A range of user keys used internally by RocksDB. Also see `RangeOpt` used by
|
||||
// public APIs.
|
||||
struct UserKeyRangePtr {
|
||||
struct UserKeyRangeOpt {
|
||||
// In case of user_defined timestamp, if enabled, `start` and `limit` should
|
||||
// point to key with timestamp part.
|
||||
// An optional range start, if missing, indicating a start before all keys.
|
||||
std::optional<Slice> start;
|
||||
OptSlice start;
|
||||
// An optional range end, if missing, indicating an end after all keys.
|
||||
std::optional<Slice> limit;
|
||||
OptSlice limit;
|
||||
|
||||
UserKeyRangePtr(const std::optional<Slice>& s, const std::optional<Slice>& l)
|
||||
: start(s), limit(l) {}
|
||||
UserKeyRangeOpt(const OptSlice& s, const OptSlice& l) : start(s), limit(l) {}
|
||||
};
|
||||
|
||||
// Checks whether a type is an inline value type
|
||||
@@ -469,6 +470,7 @@ class InternalKey {
|
||||
|
||||
Slice user_key() const { return ExtractUserKey(rep_); }
|
||||
size_t size() const { return rep_.size(); }
|
||||
bool unset() const { return rep_.empty(); }
|
||||
|
||||
void Set(const Slice& _user_key, SequenceNumber s, ValueType t) {
|
||||
SetFrom(ParsedInternalKey(_user_key, s, t));
|
||||
@@ -498,6 +500,8 @@ class InternalKey {
|
||||
// Intended only to be used together with ConvertFromUserKey().
|
||||
std::string* rep() { return &rep_; }
|
||||
|
||||
const std::string* const_rep() const { return &rep_; }
|
||||
|
||||
// Assuming that *rep() contains a user key, this method makes internal key
|
||||
// out of it in-place. This saves a memcpy compared to Set()/SetFrom().
|
||||
void ConvertFromUserKey(SequenceNumber s, ValueType t) {
|
||||
@@ -1179,4 +1183,64 @@ struct ParsedInternalKeyComparator {
|
||||
const InternalKeyComparator* cmp;
|
||||
};
|
||||
|
||||
class PredecessorWALInfo {
|
||||
public:
|
||||
PredecessorWALInfo()
|
||||
: log_number_(0),
|
||||
size_bytes_(0),
|
||||
last_seqno_recorded_(0),
|
||||
initialized_(false) {}
|
||||
|
||||
explicit PredecessorWALInfo(uint64_t log_number, uint64_t size_bytes,
|
||||
SequenceNumber last_seqno_recorded)
|
||||
: log_number_(log_number),
|
||||
size_bytes_(size_bytes),
|
||||
last_seqno_recorded_(last_seqno_recorded),
|
||||
initialized_(true) {}
|
||||
|
||||
uint64_t GetLogNumber() const {
|
||||
assert(initialized_);
|
||||
return log_number_;
|
||||
}
|
||||
|
||||
uint64_t GetSizeBytes() const {
|
||||
assert(initialized_);
|
||||
return size_bytes_;
|
||||
}
|
||||
|
||||
SequenceNumber GetLastSeqnoRecorded() const {
|
||||
assert(initialized_);
|
||||
return last_seqno_recorded_;
|
||||
}
|
||||
|
||||
bool IsInitialized() const { return initialized_; }
|
||||
|
||||
inline void EncodeTo(std::string* dst) const {
|
||||
assert(dst != nullptr);
|
||||
assert(initialized_);
|
||||
PutFixed64(dst, log_number_);
|
||||
PutFixed64(dst, size_bytes_);
|
||||
PutFixed64(dst, last_seqno_recorded_);
|
||||
}
|
||||
|
||||
inline Status DecodeFrom(Slice* src) {
|
||||
if (!GetFixed64(src, &log_number_)) {
|
||||
return Status::Corruption("Error decoding log number");
|
||||
}
|
||||
if (!GetFixed64(src, &size_bytes_)) {
|
||||
return Status::Corruption("Error decoding size bytes");
|
||||
}
|
||||
if (!GetFixed64(src, &last_seqno_recorded_)) {
|
||||
return Status::Corruption("Error decoding last seqno recorded");
|
||||
}
|
||||
initialized_ = true;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t log_number_;
|
||||
uint64_t size_bytes_;
|
||||
SequenceNumber last_seqno_recorded_;
|
||||
bool initialized_;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -135,57 +135,6 @@ class DeleteFileTest : public DBTestBase {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DeleteFileTest, AddKeysAndQueryLevels) {
|
||||
Options options = CurrentOptions();
|
||||
SetOptions(&options);
|
||||
Destroy(options);
|
||||
options.create_if_missing = true;
|
||||
Reopen(options);
|
||||
|
||||
CreateTwoLevels();
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
|
||||
std::string level1file;
|
||||
int level1keycount = 0;
|
||||
std::string level2file;
|
||||
int level2keycount = 0;
|
||||
int level1index = 0;
|
||||
int level2index = 1;
|
||||
|
||||
ASSERT_EQ((int)metadata.size(), 2);
|
||||
if (metadata[0].level == 2) {
|
||||
level1index = 1;
|
||||
level2index = 0;
|
||||
}
|
||||
|
||||
level1file = metadata[level1index].name;
|
||||
int startkey = atoi(metadata[level1index].smallestkey.c_str());
|
||||
int endkey = atoi(metadata[level1index].largestkey.c_str());
|
||||
level1keycount = (endkey - startkey + 1);
|
||||
level2file = metadata[level2index].name;
|
||||
startkey = atoi(metadata[level2index].smallestkey.c_str());
|
||||
endkey = atoi(metadata[level2index].largestkey.c_str());
|
||||
level2keycount = (endkey - startkey + 1);
|
||||
|
||||
// COntrolled setup. Levels 1 and 2 should both have 50K files.
|
||||
// This is a little fragile as it depends on the current
|
||||
// compaction heuristics.
|
||||
ASSERT_EQ(level1keycount, 50000);
|
||||
ASSERT_EQ(level2keycount, 50000);
|
||||
|
||||
Status status = db_->DeleteFile("0.sst");
|
||||
ASSERT_TRUE(status.IsInvalidArgument());
|
||||
|
||||
// intermediate level files cannot be deleted.
|
||||
status = db_->DeleteFile(level1file);
|
||||
ASSERT_TRUE(status.IsInvalidArgument());
|
||||
|
||||
// Lowest level file deletion should succeed.
|
||||
status = db_->DeleteFile(level2file);
|
||||
ASSERT_OK(status);
|
||||
}
|
||||
|
||||
TEST_F(DeleteFileTest, PurgeObsoleteFilesTest) {
|
||||
Options options = CurrentOptions();
|
||||
SetOptions(&options);
|
||||
@@ -496,145 +445,6 @@ TEST_F(DeleteFileTest, BackgroundPurgeTestMultipleJobs) {
|
||||
CheckFileTypeCounts(dbname_, 0, 1, 1);
|
||||
}
|
||||
|
||||
TEST_F(DeleteFileTest, DeleteFileWithIterator) {
|
||||
Options options = CurrentOptions();
|
||||
SetOptions(&options);
|
||||
Destroy(options);
|
||||
options.create_if_missing = true;
|
||||
Reopen(options);
|
||||
|
||||
CreateTwoLevels();
|
||||
ReadOptions read_options;
|
||||
Iterator* it = db_->NewIterator(read_options);
|
||||
ASSERT_OK(it->status());
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
|
||||
std::string level2file;
|
||||
|
||||
ASSERT_EQ(metadata.size(), static_cast<size_t>(2));
|
||||
if (metadata[0].level == 1) {
|
||||
level2file = metadata[1].name;
|
||||
} else {
|
||||
level2file = metadata[0].name;
|
||||
}
|
||||
|
||||
Status status = db_->DeleteFile(level2file);
|
||||
fprintf(stdout, "Deletion status %s: %s\n", level2file.c_str(),
|
||||
status.ToString().c_str());
|
||||
ASSERT_OK(status);
|
||||
it->SeekToFirst();
|
||||
int numKeysIterated = 0;
|
||||
while (it->Valid()) {
|
||||
numKeysIterated++;
|
||||
it->Next();
|
||||
}
|
||||
ASSERT_EQ(numKeysIterated, 50000);
|
||||
delete it;
|
||||
}
|
||||
|
||||
TEST_F(DeleteFileTest, DeleteLogFiles) {
|
||||
Options options = CurrentOptions();
|
||||
SetOptions(&options);
|
||||
Destroy(options);
|
||||
options.create_if_missing = true;
|
||||
Reopen(options);
|
||||
|
||||
AddKeys(10, 0);
|
||||
VectorLogPtr logfiles;
|
||||
ASSERT_OK(db_->GetSortedWalFiles(logfiles));
|
||||
ASSERT_GT(logfiles.size(), 0UL);
|
||||
// Take the last log file which is expected to be alive and try to delete it
|
||||
// Should not succeed because live logs are not allowed to be deleted
|
||||
std::unique_ptr<LogFile> alive_log = std::move(logfiles.back());
|
||||
ASSERT_EQ(alive_log->Type(), kAliveLogFile);
|
||||
ASSERT_OK(env_->FileExists(wal_dir_ + "/" + alive_log->PathName()));
|
||||
fprintf(stdout, "Deleting alive log file %s\n",
|
||||
alive_log->PathName().c_str());
|
||||
ASSERT_NOK(db_->DeleteFile(alive_log->PathName()));
|
||||
ASSERT_OK(env_->FileExists(wal_dir_ + "/" + alive_log->PathName()));
|
||||
logfiles.clear();
|
||||
|
||||
// Call Flush to bring about a new working log file and add more keys
|
||||
// Call Flush again to flush out memtable and move alive log to archived log
|
||||
// and try to delete the archived log file
|
||||
FlushOptions fopts;
|
||||
ASSERT_OK(db_->Flush(fopts));
|
||||
AddKeys(10, 0);
|
||||
ASSERT_OK(db_->Flush(fopts));
|
||||
ASSERT_OK(db_->GetSortedWalFiles(logfiles));
|
||||
ASSERT_GT(logfiles.size(), 0UL);
|
||||
std::unique_ptr<LogFile> archived_log = std::move(logfiles.front());
|
||||
ASSERT_EQ(archived_log->Type(), kArchivedLogFile);
|
||||
ASSERT_OK(env_->FileExists(wal_dir_ + "/" + archived_log->PathName()));
|
||||
fprintf(stdout, "Deleting archived log file %s\n",
|
||||
archived_log->PathName().c_str());
|
||||
ASSERT_OK(db_->DeleteFile(archived_log->PathName()));
|
||||
ASSERT_TRUE(
|
||||
env_->FileExists(wal_dir_ + "/" + archived_log->PathName()).IsNotFound());
|
||||
}
|
||||
|
||||
TEST_F(DeleteFileTest, DeleteNonDefaultColumnFamily) {
|
||||
Options options = CurrentOptions();
|
||||
SetOptions(&options);
|
||||
Destroy(options);
|
||||
options.create_if_missing = true;
|
||||
Reopen(options);
|
||||
CreateAndReopenWithCF({"new_cf"}, options);
|
||||
|
||||
Random rnd(5);
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles_[1], test::RandomKey(&rnd, 10),
|
||||
test::RandomKey(&rnd, 10)));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions(), handles_[1]));
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles_[1], test::RandomKey(&rnd, 10),
|
||||
test::RandomKey(&rnd, 10)));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions(), handles_[1]));
|
||||
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
ASSERT_EQ(2U, metadata.size());
|
||||
ASSERT_EQ("new_cf", metadata[0].column_family_name);
|
||||
ASSERT_EQ("new_cf", metadata[1].column_family_name);
|
||||
auto old_file = metadata[0].smallest_seqno < metadata[1].smallest_seqno
|
||||
? metadata[0].name
|
||||
: metadata[1].name;
|
||||
auto new_file = metadata[0].smallest_seqno > metadata[1].smallest_seqno
|
||||
? metadata[0].name
|
||||
: metadata[1].name;
|
||||
ASSERT_TRUE(db_->DeleteFile(new_file).IsInvalidArgument());
|
||||
ASSERT_OK(db_->DeleteFile(old_file));
|
||||
|
||||
{
|
||||
std::unique_ptr<Iterator> itr(db_->NewIterator(ReadOptions(), handles_[1]));
|
||||
ASSERT_OK(itr->status());
|
||||
int count = 0;
|
||||
for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
|
||||
ASSERT_OK(itr->status());
|
||||
++count;
|
||||
}
|
||||
ASSERT_OK(itr->status());
|
||||
ASSERT_EQ(count, 1000);
|
||||
}
|
||||
|
||||
Close();
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "new_cf"}, options);
|
||||
|
||||
{
|
||||
std::unique_ptr<Iterator> itr(db_->NewIterator(ReadOptions(), handles_[1]));
|
||||
int count = 0;
|
||||
for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
|
||||
ASSERT_OK(itr->status());
|
||||
++count;
|
||||
}
|
||||
ASSERT_OK(itr->status());
|
||||
ASSERT_EQ(count, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+12
-3
@@ -227,7 +227,7 @@ std::map<std::tuple<BackgroundErrorReason, bool>, Status::Severity>
|
||||
Status::Severity::kFatalError},
|
||||
};
|
||||
|
||||
void ErrorHandler::CancelErrorRecovery() {
|
||||
void ErrorHandler::CancelErrorRecoveryForShutDown() {
|
||||
db_mutex_->AssertHeld();
|
||||
|
||||
// We'll release the lock before calling sfm, so make sure no new
|
||||
@@ -573,6 +573,8 @@ Status ErrorHandler::ClearBGError() {
|
||||
|
||||
// Signal that recovery succeeded
|
||||
if (recovery_error_.ok()) {
|
||||
// If this assertion fails, it means likely bg error is not set after a
|
||||
// file is quarantined during MANIFEST write.
|
||||
assert(files_to_quarantine_.empty());
|
||||
Status old_bg_error = bg_error_;
|
||||
// old_bg_error is only for notifying listeners, so may not be checked
|
||||
@@ -585,8 +587,15 @@ Status ErrorHandler::ClearBGError() {
|
||||
recovery_error_.PermitUncheckedError();
|
||||
recovery_in_prog_ = false;
|
||||
soft_error_no_bg_work_ = false;
|
||||
EventHelpers::NotifyOnErrorRecoveryEnd(db_options_.listeners, old_bg_error,
|
||||
bg_error_, db_mutex_);
|
||||
if (!db_->shutdown_initiated_) {
|
||||
// NotifyOnErrorRecoveryEnd() may release and re-acquire db_mutex_.
|
||||
// Prevent DB from being closed while we notify listeners. DB close will
|
||||
// wait until allow_db_shutdown_ = true, see ReadyForShutdown().
|
||||
allow_db_shutdown_ = false;
|
||||
EventHelpers::NotifyOnErrorRecoveryEnd(
|
||||
db_options_.listeners, old_bg_error, bg_error_, db_mutex_);
|
||||
allow_db_shutdown_ = true;
|
||||
}
|
||||
}
|
||||
return recovery_error_;
|
||||
}
|
||||
|
||||
Loaded 100 of 445 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user