mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41214f0b4e | ||
|
|
94ec0b4e6d | ||
|
|
08f93221f5 | ||
|
|
a8e954024f | ||
|
|
bcc51fcc2e | ||
|
|
78a1fcba4c | ||
|
|
c79e69809e | ||
|
|
4226a32413 |
No files matched your search
@@ -0,0 +1,905 @@
|
||||
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
|
||||
@@ -0,0 +1,6 @@
|
||||
# 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
|
||||
@@ -1,13 +1,13 @@
|
||||
name: facebook/rocksdb/benchmark-linux
|
||||
on: workflow_dispatch
|
||||
permissions: {}
|
||||
# FIXME: Disabled temporarily
|
||||
# schedule:
|
||||
# - cron: 7 */2 * * * # At minute 7 past every 2nd hour
|
||||
jobs:
|
||||
# FIXME: when this job is fixed, it should be given a cron schedule like
|
||||
# schedule:
|
||||
# - cron: 0 * * * *
|
||||
# workflow_dispatch:
|
||||
benchmark-linux:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: ubuntu-latest # FIXME: change this back to self-hosted when ready
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/build-for-benchmarks"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
name: facebook/rocksdb/nightly
|
||||
on: workflow_dispatch
|
||||
permissions: {}
|
||||
jobs:
|
||||
# These jobs would be in nightly but are failing or otherwise broken for
|
||||
# some reason.
|
||||
|
||||
@@ -3,7 +3,6 @@ on:
|
||||
schedule:
|
||||
- cron: 0 9 * * *
|
||||
workflow_dispatch:
|
||||
permissions: {}
|
||||
jobs:
|
||||
build-format-compatible:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
@@ -60,15 +59,12 @@ jobs:
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: clang-13
|
||||
CXX: clang++-13
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/build-folly"
|
||||
- run: LIB_MODE=static USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check
|
||||
- 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
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-valgrind:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
@@ -80,7 +76,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: make V=1 -j32 valgrind_test
|
||||
- run: PORTABLE=1 make V=1 -j32 valgrind_test
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-windows-vs2022-avx2:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
name: facebook/rocksdb/pr-jobs-candidate
|
||||
on: workflow_dispatch
|
||||
permissions: {}
|
||||
jobs:
|
||||
# These jobs would be in pr-jobs but are failing or otherwise broken for
|
||||
# some reason.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
name: facebook/rocksdb/pr-jobs
|
||||
on: [push, pull_request]
|
||||
permissions: {}
|
||||
jobs:
|
||||
# NOTE: multiple workflows would be recommended, but the current GHA UI in
|
||||
# PRs doesn't make it clear when there's an overall error with a workflow,
|
||||
@@ -31,7 +30,7 @@ jobs:
|
||||
# ======================== Fast Initial Checks ====================== #
|
||||
check-format-and-targets:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
with:
|
||||
@@ -45,7 +44,7 @@ jobs:
|
||||
- name: Install argparse
|
||||
run: pip install argparse
|
||||
- name: Download 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
|
||||
run: wget https://raw.githubusercontent.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
|
||||
- name: Check format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
- name: Compare buckify output
|
||||
@@ -91,9 +90,6 @@ jobs:
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -108,47 +104,38 @@ jobs:
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY_LITE=1 -DWITH_GFLAGS=1 .. && make V=1 -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-make-with-folly:
|
||||
build-linux-gcc-7-with-folly:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/build-folly"
|
||||
- run: USE_FOLLY=1 LIB_MODE=static V=1 make -j32 check
|
||||
- run: USE_FOLLY=1 LIB_MODE=static CC=gcc-7 CXX=g++-7 V=1 make -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-make-with-folly-lite-no-test:
|
||||
build-linux-gcc-7-with-folly-lite-no-test:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- run: USE_FOLLY_LITE=1 V=1 make -j32 all
|
||||
- run: USE_FOLLY_LITE=1 CC=gcc-7 CXX=g++-7 V=1 make -j32 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-folly-coroutines:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
@@ -390,9 +377,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- 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
|
||||
- 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
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang13-mini-tsan:
|
||||
build-linux-clang10-mini-tsan:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 32-core-ubuntu
|
||||
|
||||
@@ -47,9 +47,6 @@ package/
|
||||
unity.a
|
||||
tags
|
||||
etags
|
||||
GPATH
|
||||
GRTAGS
|
||||
GTAGS
|
||||
rocksdb_dump
|
||||
rocksdb_undump
|
||||
db_test2
|
||||
@@ -91,7 +88,6 @@ buckifier/__pycache__
|
||||
.arcconfig
|
||||
|
||||
compile_commands.json
|
||||
meta_gen_cpp_compile_commands.json
|
||||
clang-format-diff.py
|
||||
.py3/
|
||||
|
||||
|
||||
+6
-23
@@ -32,7 +32,7 @@
|
||||
# 3. cmake ..
|
||||
# 4. make -j
|
||||
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/")
|
||||
include(ReadVersion)
|
||||
@@ -267,8 +267,8 @@ endif(CMAKE_SYSTEM_PROCESSOR MATCHES "s390x")
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "loongarch64")
|
||||
CHECK_C_COMPILER_FLAG("-march=loongarch64" HAS_LOONGARCH64)
|
||||
if(HAS_LOONGARCH64)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=loongarch64 -mtune=loongarch64")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=loongarch64 -mtune=loongarch64")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=loongarch64 -mtune=loongarch64")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=loongarch64 -mtune=loongarch64")
|
||||
endif(HAS_LOONGARCH64)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "loongarch64")
|
||||
|
||||
@@ -488,8 +488,6 @@ if(CMAKE_SYSTEM_NAME MATCHES "Cygwin")
|
||||
add_definitions(-fno-builtin-memcmp -DCYGWIN)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||
add_definitions(-DOS_MACOSX)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "iOS")
|
||||
add_definitions(-DOS_MACOSX -DIOS_CROSS_COMPILE)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
add_definitions(-DOS_LINUX)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "SunOS")
|
||||
@@ -617,7 +615,7 @@ if(USE_FOLLY)
|
||||
FMT_INST_PATH)
|
||||
exec_program(ls ARGS -d ${FOLLY_INST_PATH}/../gflags* OUTPUT_VARIABLE
|
||||
GFLAGS_INST_PATH)
|
||||
set(Boost_DIR ${BOOST_INST_PATH}/lib/cmake/Boost-1.83.0)
|
||||
set(Boost_DIR ${BOOST_INST_PATH}/lib/cmake/Boost-1.78.0)
|
||||
if(EXISTS ${FMT_INST_PATH}/lib64)
|
||||
set(fmt_DIR ${FMT_INST_PATH}/lib64/cmake/fmt)
|
||||
else()
|
||||
@@ -693,7 +691,6 @@ set(SOURCES
|
||||
db/db_impl/db_impl_write.cc
|
||||
db/db_impl/db_impl_compaction_flush.cc
|
||||
db/db_impl/db_impl_files.cc
|
||||
db/db_impl/db_impl_follower.cc
|
||||
db/db_impl/db_impl_open.cc
|
||||
db/db_impl/db_impl_debug.cc
|
||||
db/db_impl/db_impl_experimental.cc
|
||||
@@ -716,7 +713,6 @@ set(SOURCES
|
||||
db/log_reader.cc
|
||||
db/log_writer.cc
|
||||
db/malloc_stats.cc
|
||||
db/manifest_ops.cc
|
||||
db/memtable.cc
|
||||
db/memtable_list.cc
|
||||
db/merge_helper.cc
|
||||
@@ -752,7 +748,6 @@ set(SOURCES
|
||||
env/env_encryption.cc
|
||||
env/file_system.cc
|
||||
env/file_system_tracer.cc
|
||||
env/fs_on_demand.cc
|
||||
env/fs_remap.cc
|
||||
env/mock_env.cc
|
||||
env/unique_id_gen.cc
|
||||
@@ -780,7 +775,6 @@ set(SOURCES
|
||||
memtable/hash_skiplist_rep.cc
|
||||
memtable/skiplistrep.cc
|
||||
memtable/vectorrep.cc
|
||||
memtable/wbwi_memtable.cc
|
||||
memtable/write_buffer_manager.cc
|
||||
monitoring/histogram.cc
|
||||
monitoring/histogram_windowing.cc
|
||||
@@ -835,7 +829,6 @@ 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
|
||||
@@ -940,11 +933,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
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/trace/file_trace_reader_writer.cc
|
||||
utilities/trace/replayer_impl.cc
|
||||
@@ -1047,8 +1037,10 @@ endif()
|
||||
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
db/db_impl/db_impl_follower.cc
|
||||
port/port_posix.cc
|
||||
env/env_posix.cc
|
||||
env/fs_on_demand.cc
|
||||
env/fs_posix.cc
|
||||
env/io_posix.cc)
|
||||
endif()
|
||||
@@ -1057,7 +1049,6 @@ if(USE_FOLLY_LITE)
|
||||
list(APPEND SOURCES
|
||||
third-party/folly/folly/container/detail/F14Table.cpp
|
||||
third-party/folly/folly/detail/Futex.cpp
|
||||
third-party/folly/folly/lang/Exception.cpp
|
||||
third-party/folly/folly/lang/SafeAssert.cpp
|
||||
third-party/folly/folly/lang/ToAscii.cpp
|
||||
third-party/folly/folly/ScopeGuard.cpp
|
||||
@@ -1065,12 +1056,6 @@ if(USE_FOLLY_LITE)
|
||||
third-party/folly/folly/synchronization/DistributedMutex.cpp
|
||||
third-party/folly/folly/synchronization/ParkingLot.cpp)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third-party/folly)
|
||||
exec_program(python3 ${PROJECT_SOURCE_DIR}/third-party/folly ARGS
|
||||
build/fbcode_builder/getdeps.py show-source-dir boost OUTPUT_VARIABLE
|
||||
BOOST_SOURCE_PATH)
|
||||
exec_program(ls ARGS -d ${BOOST_SOURCE_PATH}/boost* OUTPUT_VARIABLE
|
||||
BOOST_INCLUDE_DIR)
|
||||
include_directories(${BOOST_INCLUDE_DIR})
|
||||
add_definitions(-DUSE_FOLLY -DFOLLY_NO_CONFIG)
|
||||
list(APPEND THIRDPARTY_LIBS glog)
|
||||
endif()
|
||||
@@ -1494,7 +1479,6 @@ if(WITH_TESTS)
|
||||
utilities/persistent_cache/persistent_cache_test.cc
|
||||
utilities/simulator_cache/cache_simulator_test.cc
|
||||
utilities/simulator_cache/sim_cache_test.cc
|
||||
utilities/table_properties_collectors/compact_for_tiering_collector_test.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
utilities/transactions/transaction_test.cc
|
||||
@@ -1574,7 +1558,6 @@ 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})
|
||||
|
||||
+13
-244
@@ -1,252 +1,21 @@
|
||||
# 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())
|
||||
|
||||
## 9.2.2 (05/31/2024)
|
||||
### 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
|
||||
* Adds a new table property "rocksdb.newest.key.time" which records the unix timestamp of the newest key. Uses this table property for FIFO TTL and temperature change compaction.
|
||||
|
||||
### Public API Changes
|
||||
* Added a new API `Transaction::GetAttributeGroupIterator` that can be used to create a multi-column-family attribute group iterator over the specified column families, including the data from both the transaction and the underlying database. This API is currently supported for optimistic and write-committed pessimistic transactions.
|
||||
* Added a new API `Transaction::GetCoalescingIterator` that can be used to create a multi-column-family coalescing iterator over the specified column families, including the data from both the transaction and the underlying database. This API is currently supported for optimistic and write-committed pessimistic transactions.
|
||||
|
||||
### Behavior Changes
|
||||
* `BaseDeltaIterator` now honors the read option `allow_unprepared_value`.
|
||||
|
||||
### Bug Fixes
|
||||
* `BaseDeltaIterator` now calls `PrepareValue` on the base iterator in case it has been created with the `allow_unprepared_value` read option set. Earlier, such base iterators could lead to incorrect values being exposed from `BaseDeltaIterator`.
|
||||
* Fix a leak of obsolete blob files left open until DB::Close(). This bug was introduced in version 9.4.0.
|
||||
* Fix missing cases of corruption retry during DB open and read API processing.
|
||||
* Fix a bug for transaction db with 2pc where an old WAL may be retained longer than needed (#13127).
|
||||
* Fix leaks of some open SST files (until `DB::Close()`) that are written but never become live due to various failures. (We now have a check for such leaks with no outstanding issues.)
|
||||
* Fix a bug for replaying WALs for WriteCommitted transaction DB when its user-defined timestamps setting is toggled on/off between DB sessions.
|
||||
|
||||
### Performance Improvements
|
||||
* Fix regression in issue #12038 due to `Options::compaction_readahead_size` greater than `max_sectors_kb` (i.e, largest I/O size that the OS issues to a block device defined in linux)
|
||||
|
||||
## 9.8.0 (10/25/2024)
|
||||
### New Features
|
||||
* All non-`block_cache` options in `BlockBasedTableOptions` are now mutable with `DB::SetOptions()`. See also Bug Fixes below.
|
||||
* When using iterators with BlobDB, it is now possible to load large values on an on-demand basis, i.e. only if they are actually needed by the application. This can save I/O in use cases where the values associated with certain keys are not needed. For more details, see the new read option `allow_unprepared_value` and the iterator API `PrepareValue`.
|
||||
* Add a new file ingestion option `IngestExternalFileOptions::fill_cache` to support not adding blocks from ingested files into block cache during file ingestion.
|
||||
* The option `allow_unprepared_value` is now also supported for multi-column-family iterators (i.e. `CoalescingIterator` and `AttributeGroupIterator`).
|
||||
* When a file with just one range deletion (standalone range deletion file) is ingested via bulk loading, it will be marked for compaction. During compaction, this type of files can be used to directly filter out some input files that are not protected by any snapshots and completely deleted by the standalone range deletion file.
|
||||
|
||||
### Behavior Changes
|
||||
* During file ingestion, overlapping files level assignment are done in multiple batches, so that they can potentially be assigned to lower levels other than always land on L0.
|
||||
* OPTIONS file to be loaded by remote worker is now preserved so that it does not get purged by the primary host. A similar technique as how we are preserving new SST files from getting purged is used for this. min_options_file_numbers_ is tracked like pending_outputs_ is tracked.
|
||||
* Trim readahead_size during scans so data blocks containing keys that are not in the same prefix as the seek key in `Seek()` are not prefetched when `ReadOptions::auto_readahead_size=true` (default value) and `ReadOptions::prefix_same_as_start = true`
|
||||
* Assigning levels for external files are done in the same way for universal compaction and leveled compaction. The old behavior tends to assign files to L0 while the new behavior will assign the files to the lowest level possible.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a longstanding race condition in SetOptions for `block_based_table_factory` options. The fix has some subtle behavior changes because of copying and replacing the TableFactory on a change with SetOptions, including requiring an Iterator::Refresh() for an existing Iterator to use the latest options.
|
||||
* Fix under counting of allocated memory in the compressed secondary cache due to looking at the compressed block size rather than the actual memory allocated, which could be larger due to internal fragmentation.
|
||||
* `GetApproximateMemTableStats()` could return disastrously bad estimates 5-25% of the time. The function has been re-engineered to return much better estimates with similar CPU cost.
|
||||
* Skip insertion of compressed blocks in the secondary cache if the lowest_used_cache_tier DB option is kVolatileTier.
|
||||
* Fix an issue in level compaction where a small CF with small compaction debt can cause the DB to allow parallel compactions. (#13054)
|
||||
* Several DB option settings could be lost through `GetOptionsFromString()`, possibly elsewhere as well. Affected options, now fixed:`background_close_inactive_wals`, `write_dbid_to_manifest`, `write_identity_file`, `prefix_seek_opt_in_only`
|
||||
|
||||
## 9.7.0 (09/20/2024)
|
||||
### New Features
|
||||
* Make Cache a customizable class that can be instantiated by the object registry.
|
||||
* Add new option `prefix_seek_opt_in_only` that makes iterators generally safer when you might set a `prefix_extractor`. When `prefix_seek_opt_in_only=true`, which is expected to be the future default, prefix seek is only used when `prefix_same_as_start` or `auto_prefix_mode` are set. Also, `prefix_same_as_start` and `auto_prefix_mode` now allow prefix filtering even with `total_order_seek=true`.
|
||||
* Add a new table property "rocksdb.key.largest.seqno" which records the largest sequence number of all keys in file. It is verified to be zero during SST file ingestion.
|
||||
|
||||
### Behavior Changes
|
||||
* Changed the semantics of the BlobDB configuration option `blob_garbage_collection_force_threshold` to define a threshold for the overall garbage ratio of all blob files currently eligible for garbage collection (according to `blob_garbage_collection_age_cutoff`). This can provide better control over space amplification at the cost of slightly higher write amplification.
|
||||
* Set `write_dbid_to_manifest=true` by default. This means DB ID will now be preserved through backups, checkpoints, etc. by default. Also add `write_identity_file` option which can be set to false for anticipated future behavior.
|
||||
* In FIFO compaction, compactions for changing file temperature (configured by option `file_temperature_age_thresholds`) will compact one file at a time, instead of merging multiple eligible file together (#13018).
|
||||
* Support ingesting db generated files using hard link, i.e. IngestExternalFileOptions::move_files/link_files and IngestExternalFileOptions::allow_db_generated_files.
|
||||
* Add a new file ingestion option `IngestExternalFileOptions::link_files` to hard link input files and preserve original files links after ingestion.
|
||||
* DB::Close now untracks files in SstFileManager, making avaialble any space used
|
||||
by them. Prior to this change they would be orphaned until the DB is re-opened.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in CompactRange() where result files may not be compacted in any future compaction. This can only happen when users configure CompactRangeOptions::change_level to true and the change level step of manual compaction fails (#13009).
|
||||
* Fix handling of dynamic change of `prefix_extractor` with memtable prefix filter. Previously, prefix seek could mix different prefix interpretations between memtable and SST files. Now the latest `prefix_extractor` at the time of iterator creation or refresh is respected.
|
||||
* Fix a bug with manual_wal_flush and auto error recovery from WAL failure that may cause CFs to be inconsistent (#12995). The fix will set potential WAL write failure as fatal error when manual_wal_flush is true, and disables auto error recovery from these errors.
|
||||
|
||||
## 9.6.0 (08/19/2024)
|
||||
### New Features
|
||||
* Best efforts recovery supports recovering to incomplete Version with a clean seqno cut that presents a valid point in time view from the user's perspective, if versioning history doesn't include atomic flush.
|
||||
* New option `BlockBasedTableOptions::decouple_partitioned_filters` should improve efficiency in serving read queries because filter and index partitions can consistently target the configured `metadata_block_size`. This option is currently opt-in.
|
||||
* Introduce a new mutable CF option `paranoid_memory_checks`. It enables additional validation on data integrity during reads/scanning. Currently, skip list based memtable will validate key ordering during look up and scans.
|
||||
|
||||
### Public API Changes
|
||||
* Add ticker stats to count file read retries due to checksum mismatch
|
||||
* Adds optional installation callback function for remote compaction
|
||||
|
||||
### Behavior Changes
|
||||
* There may be less intra-L0 compaction triggered by total L0 size being too small. We now use compensated file size (tombstones are assigned some value size) when calculating L0 size and reduce the threshold for L0 size limit. This is to avoid accumulating too much data/tombstones in L0.
|
||||
|
||||
### Bug Fixes
|
||||
* Make DestroyDB supports slow deletion when it's configured in `SstFileManager`. The slow deletion is subject to the configured `rate_bytes_per_sec`, but not subject to the `max_trash_db_ratio`.
|
||||
* Fixed a bug where we set unprep_seqs_ even when WriteImpl() fails. This was caught by stress test write fault injection in WriteImpl(). This may have incorrectly caused iteration creation failure for unvalidated writes or returned wrong result for WriteUnpreparedTxn::GetUnpreparedSequenceNumbers().
|
||||
* Fixed a bug where successful write right after error recovery for last failed write finishes causes duplicate WAL entries
|
||||
* Fixed a data race involving the background error status in `unordered_write` mode.
|
||||
* Fix a bug where file snapshot functions like backup, checkpoint may attempt to copy a non-existing manifest file. #12882
|
||||
* Fix a bug where per kv checksum corruption may be ignored in MultiGet().
|
||||
* Fix a race condition in pessimistic transactions that could allow multiple transactions with the same name to be registered simultaneously, resulting in a crash or other unpredictable behavior.
|
||||
|
||||
## 9.5.0 (07/19/2024)
|
||||
### Public API Changes
|
||||
* Introduced new C API function rocksdb_writebatch_iterate_cf for column family-aware iteration over the contents of a WriteBatch
|
||||
* Add support to ingest SST files generated by a DB instead of SstFileWriter. This can be enabled with experimental option `IngestExternalFileOptions::allow_db_generated_files`.
|
||||
|
||||
### Behavior Changes
|
||||
* When calculating total log size for the `log_size_for_flush` argument in `CreateCheckpoint` API, the size of the archived log will not be included to avoid unnecessary flush
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a major bug in which an iterator using prefix filtering and SeekForPrev might miss data when the DB is using `whole_key_filtering=false` and `partition_filters=true`.
|
||||
* Fixed a bug where `OnErrorRecoveryBegin()` is not called before auto recovery starts.
|
||||
* Fixed a bug where event listener reads ErrorHandler's `bg_error_` member without holding db mutex(#12803).
|
||||
* Fixed a bug in handling MANIFEST write error that caused the latest valid MANIFEST file to get deleted, resulting in the DB being unopenable.
|
||||
* Fixed a race between error recovery due to manifest sync or write failure and external SST file ingestion. Both attempt to write a new manifest file, which causes an assertion failure.
|
||||
|
||||
### Performance Improvements
|
||||
* Fix an issue where compactions were opening table files and reading table properties while holding db mutex_.
|
||||
* Reduce unnecessary filesystem queries and DB mutex acquires in creating backups and checkpoints.
|
||||
|
||||
## 9.4.0 (06/23/2024)
|
||||
### New Features
|
||||
* Added a `CompactForTieringCollectorFactory` to auto trigger compaction for tiering use case.
|
||||
* Optimistic transactions and pessimistic transactions with the WriteCommitted policy now support the `GetEntityForUpdate` API.
|
||||
* Added a new "count" command to the ldb repl shell. By default, it prints a count of keys in the database from start to end. The options --from=<key> and/or --to=<key> can be specified to limit the range.
|
||||
* Add `rocksdb_writebatch_update_timestamps`, `rocksdb_writebatch_wi_update_timestamps` in C API.
|
||||
* Add `rocksdb_iter_refresh` in C API.
|
||||
* Add `rocksdb_writebatch_create_with_params`, `rocksdb_writebatch_wi_create_with_params` to create WB and WBWI with all options in C API
|
||||
|
||||
### Public API Changes
|
||||
* Deprecated names `LogFile` and `VectorLogPtr` in favor of new names `WalFile` and `VectorWalPtr`.
|
||||
* Introduce a new universal compaction option CompactionOptionsUniversal::max_read_amp which allows user to define the limit on the number of sorted runs separately from the trigger for compaction (`level0_file_num_compaction_trigger`) #12477.
|
||||
|
||||
### Behavior Changes
|
||||
* Inactive WALs are immediately closed upon being fully sync-ed rather than in a background thread. This is to ensure LinkFile() is not called on files still open for write, which might not be supported by some FileSystem implementations. This should not be a performance issue, but an opt-out is available with with new DB option `background_close_inactive_wals`.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a rare case in which a hard-linked WAL in a Checkpoint is not fully synced (so might lose data on power loss).
|
||||
* Fixed the output of the `ldb dump_wal` command for `PutEntity` records so it prints the key and correctly resets the hexadecimal formatting flag after printing the wide-column entity.
|
||||
* Fixed an issue where `PutEntity` records were handled incorrectly while rebuilding transactions during recovery.
|
||||
* Various read operations could ignore various ReadOptions that might be relevant. Fixed many such cases, which can result in behavior change but a better reflection of specified options.
|
||||
|
||||
### Performance Improvements
|
||||
* Improved write throughput to memtable when there's a large number of concurrent writers and allow_concurrent_memtable_write=true(#12545)
|
||||
|
||||
## 9.3.0 (05/17/2024)
|
||||
### New Features
|
||||
* Optimistic transactions and pessimistic transactions with the WriteCommitted policy now support the `GetEntity` API.
|
||||
* Added new `Iterator` property, "rocksdb.iterator.is-value-pinned", for checking whether the `Slice` returned by `Iterator::value()` can be used until the `Iterator` is destroyed.
|
||||
* Optimistic transactions and WriteCommitted pessimistic transactions now support the `MultiGetEntity` API.
|
||||
* Optimistic transactions and pessimistic transactions with the WriteCommitted policy now support the `PutEntity` API. Support for read APIs and other write policies (WritePrepared, WriteUnprepared) will be added later.
|
||||
|
||||
### Public API Changes
|
||||
* Exposed block based metadata cache options via C API
|
||||
* Exposed compaction pri via c api.
|
||||
* Add a kAdmPolicyAllowAll option to TieredAdmissionPolicy that admits all blocks evicted from the primary block cache into the compressed secondary cache.
|
||||
|
||||
### Behavior Changes
|
||||
* CompactRange() with change_level=true on a CF with FIFO compaction will return Status::NotSupported().
|
||||
* External file ingestion with FIFO compaction will always ingest to L0.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a bug for databases using `DBOptions::allow_2pc == true` (all `TransactionDB`s except `OptimisticTransactionDB`) that have exactly one column family. Due to a missing WAL sync, attempting to open the DB could have returned a `Status::Corruption` with a message like "SST file is ahead of WALs".
|
||||
* Fix a bug in CreateColumnFamilyWithImport() where if multiple CFs are imported, we were not resetting files' epoch number and L0 files can have overlapping key range but the same epoch number.
|
||||
* Fixed race conditions when `ColumnFamilyOptions::inplace_update_support == true` between user overwrites and reads on the same key.
|
||||
* Fix a bug where `CompactFiles()` can compact files of range conflict with other ongoing compactions' when `preclude_last_level_data_seconds > 0` is used
|
||||
* Fixed a false positive `Status::Corruption` reported when reopening a DB that used `DBOptions::recycle_log_file_num > 0` and `DBOptions::wal_compression != kNoCompression`.
|
||||
* While WAL is locked with LockWAL(), some operations like Flush() and IngestExternalFile() are now blocked as they should have been.
|
||||
* Fixed a bug causing stale memory access when using the TieredSecondaryCache with an NVM secondary cache, and a file system that supports return an FS allocated buffer for MultiRead (FSSupportedOps::kFSBuffer is set).
|
||||
|
||||
## 9.2.1 (05/03/2024)
|
||||
### Public API Changes
|
||||
* Add a kAdmPolicyAllowAll option to TieredAdmissionPolicy that admits all blocks evicted from the primary block cache into the compressed secondary cache.
|
||||
|
||||
## 9.2.0 (05/01/2024)
|
||||
### New Features
|
||||
* Added two options `deadline` and `max_size_bytes` for CacheDumper to exit early
|
||||
* Added a new API `GetEntityFromBatchAndDB` to `WriteBatchWithIndex` that can be used for wide-column point lookups with read-your-own-writes consistency. Similarly to `GetFromBatchAndDB`, the API can combine data from the write batch with data from the underlying database if needed. See the API comments for more details.
|
||||
* [Experimental] Introduce two new cross-column-family iterators - CoalescingIterator and AttributeGroupIterator. The CoalescingIterator enables users to iterate over multiple column families and access their values and columns. During this iteration, if the same key exists in more than one column family, the keys in the later column family will overshadow the previous ones. The AttributeGroupIterator allows users to gather wide columns per Column Family and create attribute groups while iterating over keys across all CFs.
|
||||
* Added a new API `MultiGetEntityFromBatchAndDB` to `WriteBatchWithIndex` that can be used for batched wide-column point lookups with read-your-own-writes consistency. Similarly to `MultiGetFromBatchAndDB`, the API can combine data from the write batch with data from the underlying database if needed. See the API comments for more details.
|
||||
* Adds a `SstFileReader::NewTableIterator` API to support programmatically read a SST file as a raw table file.
|
||||
* *Adds a `SstFileReader::NewTableIterator` API to support programmatically read a SST file as a raw table file.
|
||||
* Add an option to `WaitForCompactOptions` - `wait_for_purge` to make `WaitForCompact()` API wait for background purge to complete
|
||||
|
||||
### Public API Changes
|
||||
@@ -275,24 +44,24 @@ the whole DB to be dropped right after migration if the migrated data is larger
|
||||
* Fixed a bug where wrong padded bytes are used to generate file checksum and `DataVerificationInfo::checksum` upon file creation
|
||||
* Correctly implemented the move semantics of `PinnableWideColumns`.
|
||||
* Fixed a bug when the recycle_log_file_num in DBOptions is changed from 0 to non-zero when a DB is reopened. On a subsequent reopen, if a log file created when recycle_log_file_num==0 was reused previously, is alive and is empty, we could end up inserting stale WAL records into the memtable.
|
||||
* Fix a bug where obsolete files' deletion during DB::Open are not rate limited with `SstFilemManager`'s slow deletion feature even if it's configured.
|
||||
* *Fix a bug where obsolete files' deletion during DB::Open are not rate limited with `SstFilemManager`'s slow deletion feature even if it's configured.
|
||||
|
||||
## 9.1.0 (03/22/2024)
|
||||
### New Features
|
||||
* Added an option, `GetMergeOperandsOptions::continue_cb`, to give users the ability to end `GetMergeOperands()`'s lookup process before all merge operands were found.
|
||||
* Add sanity checks for ingesting external files that currently checks if the user key comparator used to create the file is compatible with the column family's user key comparator.
|
||||
* *Add sanity checks for ingesting external files that currently checks if the user key comparator used to create the file is compatible with the column family's user key comparator.
|
||||
*Support ingesting external files for column family that has user-defined timestamps in memtable only enabled.
|
||||
* On file systems that support storage level data checksum and reconstruction, retry SST block reads for point lookups, scans, and flush and compaction if there's a checksum mismatch on the initial read.
|
||||
* Some enhancements and fixes to experimental Temperature handling features, including new `default_write_temperature` CF option and opening an `SstFileWriter` with a temperature.
|
||||
* `WriteBatchWithIndex` now supports wide-column point lookups via the `GetEntityFromBatch` API. See the API comments for more details.
|
||||
* Implement experimental features: API `Iterator::GetProperty("rocksdb.iterator.write-time")` to allow users to get data's approximate write unix time and write data with a specific write time via `WriteBatch::TimedPut` API.
|
||||
* *Implement experimental features: API `Iterator::GetProperty("rocksdb.iterator.write-time")` to allow users to get data's approximate write unix time and write data with a specific write time via `WriteBatch::TimedPut` API.
|
||||
|
||||
### Public API Changes
|
||||
* Best-effort recovery (`best_efforts_recovery == true`) may now be used together with atomic flush (`atomic_flush == true`). The all-or-nothing recovery guarantee for atomically flushed data will be upheld.
|
||||
* Remove deprecated option `bottommost_temperature`, already replaced by `last_level_temperature`
|
||||
* Added new PerfContext counters for block cache bytes read - block_cache_index_read_byte, block_cache_filter_read_byte, block_cache_compression_dict_read_byte, and block_cache_read_byte.
|
||||
* Deprecate experimental Remote Compaction APIs - StartV2() and WaitForCompleteV2() and introduce Schedule() and Wait(). The new APIs essentially does the same thing as the old APIs. They allow taking externally generated unique id to wait for remote compaction to complete.
|
||||
* For API `WriteCommittedTransaction::GetForUpdate`, if the column family enables user-defined timestamp, it was mandated that argument `do_validate` cannot be false, and UDT based validation has to be done with a user set read timestamp. It's updated to make the UDT based validation optional if user sets `do_validate` to false and does not set a read timestamp. With this, `GetForUpdate` skips UDT based validation and it's users' responsibility to enforce the UDT invariant. SO DO NOT skip this UDT-based validation if users do not have ways to enforce the UDT invariant. Ways to enforce the invariant on the users side include manage a monotonically increasing timestamp, commit transactions in a single thread etc.
|
||||
* *For API `WriteCommittedTransaction::GetForUpdate`, if the column family enables user-defined timestamp, it was mandated that argument `do_validate` cannot be false, and UDT based validation has to be done with a user set read timestamp. It's updated to make the UDT based validation optional if user sets `do_validate` to false and does not set a read timestamp. With this, `GetForUpdate` skips UDT based validation and it's users' responsibility to enforce the UDT invariant. SO DO NOT skip this UDT-based validation if users do not have ways to enforce the UDT invariant. Ways to enforce the invariant on the users side include manage a monotonically increasing timestamp, commit transactions in a single thread etc.
|
||||
* Defined a new PerfLevel `kEnableWait` to measure time spent by user threads blocked in RocksDB other than mutex, such as a write thread waiting to be added to a write group, a write thread delayed or stalled etc.
|
||||
* `RateLimiter`'s API no longer requires the burst size to be the refill size. Users of `NewGenericRateLimiter()` can now provide burst size in `single_burst_bytes`. Implementors of `RateLimiter::SetSingleBurstBytes()` need to adapt their implementations to match the changed API doc.
|
||||
* Add `write_memtable_time` to the newly introduced PerfLevel `kEnableWait`.
|
||||
@@ -325,7 +94,7 @@ MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 1024 thrpt 25 76
|
||||
## 9.0.0 (02/16/2024)
|
||||
### New Features
|
||||
* Provide support for FSBuffer for point lookups. Also added support for scans and compactions that don't go through prefetching.
|
||||
* Make `SstFileWriter` create SST files without persisting user defined timestamps when the `Option.persist_user_defined_timestamps` flag is set to false.
|
||||
* *Make `SstFileWriter` create SST files without persisting user defined timestamps when the `Option.persist_user_defined_timestamps` flag is set to false.
|
||||
* Add support for user-defined timestamps in APIs `DeleteFilesInRanges` and `GetPropertiesOfTablesInRange`.
|
||||
* Mark wal\_compression feature as production-ready. Currently only compatible with ZSTD compression.
|
||||
|
||||
@@ -335,9 +104,9 @@ MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 1024 thrpt 25 76
|
||||
* Exposed mode option to Rate Limiter via c api.
|
||||
* Removed deprecated option `access_hint_on_compaction_start`
|
||||
* Removed deprecated option `ColumnFamilyOptions::check_flush_compaction_key_order`
|
||||
* Remove the default `WritableFile::GetFileSize` and `FSWritableFile::GetFileSize` implementation that returns 0 and make it pure virtual, so that subclasses are enforced to explicitly provide an implementation.
|
||||
* *Remove the default `WritableFile::GetFileSize` and `FSWritableFile::GetFileSize` implementation that returns 0 and make it pure virtual, so that subclasses are enforced to explicitly provide an implementation.
|
||||
* Removed deprecated option `ColumnFamilyOptions::level_compaction_dynamic_file_size`
|
||||
* Removed tickers with typos "rocksdb.error.handler.bg.errro.count", "rocksdb.error.handler.bg.io.errro.count", "rocksdb.error.handler.bg.retryable.io.errro.count".
|
||||
* *Removed tickers with typos "rocksdb.error.handler.bg.errro.count", "rocksdb.error.handler.bg.io.errro.count", "rocksdb.error.handler.bg.retryable.io.errro.count".
|
||||
* Remove the force mode for `EnableFileDeletions` API because it is unsafe with no known legitimate use.
|
||||
* Removed deprecated option `ColumnFamilyOptions::ignore_max_compaction_bytes_for_input`
|
||||
* `sst_dump --command=check` now compares the number of records in a table with `num_entries` in table property, and reports corruption if there is a mismatch. API `SstFileDumper::ReadSequential()` is updated to optionally do this verification. (#12322)
|
||||
|
||||
@@ -2,8 +2,7 @@ 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
|
||||
* https://github.com/rocksdict/RocksDict
|
||||
* http://python-rocksdb.readthedocs.io/en/latest/ (unmaintained)
|
||||
* http://python-rocksdb.readthedocs.io/en/latest/
|
||||
* http://pyrocksdb.readthedocs.org/en/latest/ (unmaintained)
|
||||
* Perl - https://metacpan.org/pod/RocksDB
|
||||
* Node.js - https://npmjs.org/package/rocksdb
|
||||
|
||||
@@ -103,7 +103,6 @@ dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
|
||||
export LIB_MODE="$(LIB_MODE)"; \
|
||||
export ROCKSDB_CXX_STANDARD="$(ROCKSDB_CXX_STANDARD)"; \
|
||||
export USE_FOLLY="$(USE_FOLLY)"; \
|
||||
export USE_FOLLY_LITE="$(USE_FOLLY_LITE)"; \
|
||||
"$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
# this file is generated by the previous line to set build flags and sources
|
||||
include make_config.mk
|
||||
@@ -501,17 +500,6 @@ endif
|
||||
ifeq ($(USE_FOLLY_LITE),1)
|
||||
# Path to the Folly source code and include files
|
||||
FOLLY_DIR = ./third-party/folly
|
||||
ifneq ($(strip $(BOOST_SOURCE_PATH)),)
|
||||
BOOST_INCLUDE = $(shell (ls -d $(BOOST_SOURCE_PATH)/boost*/))
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PLATFORM_CCFLAGS += -I$(BOOST_INCLUDE)
|
||||
PLATFORM_CXXFLAGS += -I$(BOOST_INCLUDE)
|
||||
else
|
||||
PLATFORM_CCFLAGS += -isystem $(BOOST_INCLUDE)
|
||||
PLATFORM_CXXFLAGS += -isystem $(BOOST_INCLUDE)
|
||||
endif
|
||||
endif # BOOST_SOURCE_PATH
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PLATFORM_CCFLAGS += -I$(FOLLY_DIR)
|
||||
@@ -630,11 +618,6 @@ VALGRIND_VER := $(join $(VALGRIND_VER),valgrind)
|
||||
VALGRIND_OPTS = --error-exitcode=$(VALGRIND_ERROR) --leak-check=full
|
||||
# Not yet supported: --show-leak-kinds=definite,possible,reachable --errors-for-leak-kinds=definite,possible,reachable
|
||||
|
||||
# Work around valgrind hanging on systems with limited internet access
|
||||
ifneq ($(shell which git 2>/dev/null && git config --get https.proxy),)
|
||||
export DEBUGINFOD_URLS=
|
||||
endif
|
||||
|
||||
TEST_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(TEST_LIB_SOURCES) $(MOCK_LIB_SOURCES)) $(GTEST)
|
||||
BENCH_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(BENCH_LIB_SOURCES))
|
||||
CACHE_BENCH_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(CACHE_BENCH_LIB_SOURCES))
|
||||
@@ -653,37 +636,26 @@ TESTS = $(patsubst %.cc, %, $(notdir $(TEST_MAIN_SOURCES)))
|
||||
TESTS += $(patsubst %.c, %, $(notdir $(TEST_MAIN_SOURCES_C)))
|
||||
TESTS += $(PLUGIN_TESTS)
|
||||
|
||||
# `make check-headers` to verify that each header file includes its own deps
|
||||
# and that public headers do not depend on internal headers
|
||||
# `make check-headers` to very that each header file includes its own
|
||||
# dependencies
|
||||
ifneq ($(filter check-headers, $(MAKECMDGOALS)),)
|
||||
# TODO: add/support JNI headers
|
||||
DEV_HEADER_DIRS := $(sort include/ $(dir $(ALL_SOURCES)))
|
||||
# Some headers like in port/ are platform-specific
|
||||
DEV_HEADERS_TO_CHECK := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|lua/|range_tree/|secondary_index/')
|
||||
PUBLIC_HEADERS_TO_CHECK := $(shell $(FIND) include/ -type f -name '*.h' | grep -E -v 'lua/')
|
||||
DEV_HEADERS := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|lua/|range_tree/')
|
||||
else
|
||||
DEV_HEADERS_TO_CHECK :=
|
||||
PUBLIC_HEADERS_TO_CHECK :=
|
||||
DEV_HEADERS :=
|
||||
endif
|
||||
HEADER_OK_FILES = $(patsubst %.h, %.h.ok, $(DEV_HEADERS_TO_CHECK)) \
|
||||
$(patsubst %.h, %.h.pub, $(PUBLIC_HEADERS_TO_CHECK))
|
||||
HEADER_OK_FILES = $(patsubst %.h, %.h.ok, $(DEV_HEADERS))
|
||||
|
||||
AM_V_CCH = $(am__v_CCH_$(V))
|
||||
am__v_CCH_ = $(am__v_CCH_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_CCH_0 = @echo " CC.h " $<;
|
||||
am__v_CCH_1 =
|
||||
|
||||
# verify headers include their own dependencies, under dev build settings
|
||||
%.h.ok: %.h # .h.ok not actually created, so re-checked on each invocation
|
||||
# -DROCKSDB_NAMESPACE=42 ensures the namespace header is included
|
||||
$(AM_V_CCH) echo '#include "$<"' | $(CXX) $(CXXFLAGS) \
|
||||
-DROCKSDB_NAMESPACE=42 -x c++ -c - -o /dev/null
|
||||
|
||||
# verify public headers do not depend on internal headers, under typical
|
||||
# user build settings
|
||||
%.h.pub: %.h # .h.pub not actually created, so re-checked on each invocation
|
||||
$(AM_V_CCH) cd include/ && echo '#include "$(patsubst include/%,%,$<)"' | \
|
||||
$(CXX) -I. -DROCKSDB_NAMESPACE=42 -x c++ -c - -o /dev/null
|
||||
$(AM_V_CCH) echo '#include "$<"' | $(CXX) $(CXXFLAGS) -DROCKSDB_NAMESPACE=42 -x c++ -c - -o /dev/null
|
||||
|
||||
check-headers: $(HEADER_OK_FILES)
|
||||
|
||||
@@ -1169,16 +1141,16 @@ ubsan_crash_test_with_best_efforts_recovery: clean
|
||||
$(MAKE) clean
|
||||
|
||||
full_valgrind_test:
|
||||
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 PORTABLE=1 $(MAKE) valgrind_check
|
||||
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
|
||||
|
||||
full_valgrind_test_some:
|
||||
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 PORTABLE=1 $(MAKE) valgrind_check_some
|
||||
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check_some
|
||||
|
||||
valgrind_test:
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 PORTABLE=1 $(MAKE) valgrind_check
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
|
||||
|
||||
valgrind_test_some:
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 PORTABLE=1 $(MAKE) valgrind_check_some
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check_some
|
||||
|
||||
valgrind_check: $(TESTS)
|
||||
$(MAKE) DRIVER="$(VALGRIND_VER) $(VALGRIND_OPTS)" gen_parallel_tests
|
||||
@@ -1659,9 +1631,6 @@ compaction_job_stats_test: $(OBJ_DIR)/db/compaction/compaction_job_stats_test.o
|
||||
compaction_service_test: $(OBJ_DIR)/db/compaction/compaction_service_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
compact_for_tiering_collector_test: $(OBJ_DIR)/utilities/table_properties_collectors/compact_for_tiering_collector_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
compact_on_deletion_collector_test: $(OBJ_DIR)/utilities/table_properties_collectors/compact_on_deletion_collector_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -2144,8 +2113,8 @@ ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.8
|
||||
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
|
||||
BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
|
||||
SNAPPY_VER ?= 1.2.1
|
||||
SNAPPY_SHA256 ?= 736aeb64d86566d2236ddffa2865ee5d7a82d26c9016b36218fcc27ea4f09f86
|
||||
SNAPPY_VER ?= 1.1.8
|
||||
SNAPPY_SHA256 ?= 16b677f07832a612b0836178db7f374e414f94657c138e6993cbfc5dcc58651f
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
LZ4_VER ?= 1.9.4
|
||||
LZ4_SHA256 ?= 0b0e3aa07c8c063ddf40b082bdf7e37a1562bda40a0ff5272957f3e987e0e54b
|
||||
@@ -2242,7 +2211,7 @@ libsnappy.a: snappy-$(SNAPPY_VER).tar.gz
|
||||
-rm -rf snappy-$(SNAPPY_VER)
|
||||
tar xvzf snappy-$(SNAPPY_VER).tar.gz
|
||||
mkdir snappy-$(SNAPPY_VER)/build
|
||||
cd snappy-$(SNAPPY_VER)/build && CFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DSNAPPY_BUILD_BENCHMARKS=OFF -DSNAPPY_BUILD_TESTS=OFF --compile-no-warning-as-error ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cd snappy-$(SNAPPY_VER)/build && CFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cp snappy-$(SNAPPY_VER)/build/libsnappy.a .
|
||||
|
||||
lz4-$(LZ4_VER).tar.gz:
|
||||
@@ -2489,13 +2458,14 @@ checkout_folly:
|
||||
fi
|
||||
@# Pin to a particular version for public CI, so that PR authors don't
|
||||
@# need to worry about folly breaking our integration. Update periodically
|
||||
cd third-party/folly && git reset --hard 78286282478e1ae05b2e8cbcf0e2139eab283bea
|
||||
cd third-party/folly && git reset --hard beacd86d63cd71c904632262e6c36f60874d78ba
|
||||
@# A hack to remove boost dependency.
|
||||
@# NOTE: this hack is only needed if building using USE_FOLLY_LITE
|
||||
perl -pi -e 's/^(#include <boost)/\/\/$$1/' third-party/folly/folly/functional/Invoke.h
|
||||
@# NOTE: this hack is required for clang in some cases
|
||||
perl -pi -e 's/int rv = syscall/int rv = (int)syscall/' third-party/folly/folly/detail/Futex.cpp
|
||||
@# NOTE: this hack is required for gcc in some cases
|
||||
perl -pi -e 's/(__has_include.<experimental.memory_resource>.)/__cpp_rtti && $$1/' third-party/folly/folly/memory/MemoryResource.h
|
||||
@# NOTE: boost source will be needed for any build including `USE_FOLLY_LITE` builds as those depend on boost headers
|
||||
cd third-party/folly && $(PYTHON) build/fbcode_builder/getdeps.py fetch boost
|
||||
|
||||
CXX_M_FLAGS = $(filter -m%, $(CXXFLAGS))
|
||||
|
||||
@@ -2507,6 +2477,8 @@ build_folly:
|
||||
echo "Please run checkout_folly first"; \
|
||||
false; \
|
||||
fi
|
||||
# Restore the original version of Invoke.h with boost dependency
|
||||
cd third-party/folly && ${GIT_COMMAND} checkout folly/functional/Invoke.h
|
||||
cd third-party/folly && \
|
||||
CXXFLAGS=" $(CXX_M_FLAGS) -DHAVE_CXX11_ATOMIC " $(PYTHON) build/fbcode_builder/getdeps.py build --no-tests
|
||||
|
||||
|
||||
+7
-51
@@ -4,7 +4,6 @@
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
load("//rocks/buckifier:defs.bzl", "cpp_library_wrapper","rocks_cpp_library_wrapper","cpp_binary_wrapper","cpp_unittest_wrapper","fancy_bench_wrapper","add_c_test_wrapper")
|
||||
load("@fbcode_macros//build_defs:export_files.bzl", "export_file")
|
||||
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
@@ -83,7 +82,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"db/log_writer.cc",
|
||||
"db/logs_with_prep_tracker.cc",
|
||||
"db/malloc_stats.cc",
|
||||
"db/manifest_ops.cc",
|
||||
"db/memtable.cc",
|
||||
"db/memtable_list.cc",
|
||||
"db/merge_helper.cc",
|
||||
@@ -150,7 +148,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"memtable/hash_skiplist_rep.cc",
|
||||
"memtable/skiplistrep.cc",
|
||||
"memtable/vectorrep.cc",
|
||||
"memtable/wbwi_memtable.cc",
|
||||
"memtable/write_buffer_manager.cc",
|
||||
"monitoring/histogram.cc",
|
||||
"monitoring/histogram_windowing.cc",
|
||||
@@ -214,7 +211,6 @@ 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",
|
||||
@@ -318,11 +314,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",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/trace/file_trace_reader_writer.cc",
|
||||
"utilities/trace/replayer_impl.cc",
|
||||
@@ -367,14 +360,9 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"//folly/experimental/coro:coroutine",
|
||||
"//folly/experimental/coro:task",
|
||||
"//folly/synchronization:distributed_mutex",
|
||||
], headers=glob(["**/*.h"]), link_whole=False, extra_test_libs=False)
|
||||
], headers=None, link_whole=False, extra_test_libs=False)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[], deps=[":rocksdb_lib"], headers=[], link_whole=True, extra_test_libs=False)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_with_faiss_lib", srcs=["utilities/secondary_index/faiss_ivf_index.cc"], deps=[
|
||||
"//faiss:faiss",
|
||||
":rocksdb_lib",
|
||||
], headers=[], link_whole=False, extra_test_libs=False)
|
||||
cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[], deps=[":rocksdb_lib"], headers=None, link_whole=True, extra_test_libs=False)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_test_lib", srcs=[
|
||||
"db/db_test_util.cc",
|
||||
@@ -388,39 +376,23 @@ cpp_library_wrapper(name="rocksdb_test_lib", srcs=[
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
"utilities/agg_merge/test_agg_merge.cc",
|
||||
"utilities/cassandra/test_utils.cc",
|
||||
], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=True)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_with_faiss_test_lib", srcs=[
|
||||
"db/db_test_util.cc",
|
||||
"db/db_with_timestamp_test_util.cc",
|
||||
"table/mock_table.cc",
|
||||
"test_util/mock_time_env.cc",
|
||||
"test_util/secondary_cache_test_util.cc",
|
||||
"test_util/testharness.cc",
|
||||
"test_util/testutil.cc",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
"utilities/agg_merge/test_agg_merge.cc",
|
||||
"utilities/cassandra/test_utils.cc",
|
||||
], deps=[":rocksdb_with_faiss_lib"], headers=[], link_whole=False, extra_test_libs=True)
|
||||
], deps=[":rocksdb_lib"], headers=None, link_whole=False, extra_test_libs=True)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_tools_lib", srcs=[
|
||||
"test_util/testutil.cc",
|
||||
"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)
|
||||
], deps=[":rocksdb_lib"], headers=None, link_whole=False, extra_test_libs=False)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_cache_bench_tools_lib", srcs=["cache/cache_bench_tool.cc"], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=False)
|
||||
cpp_library_wrapper(name="rocksdb_cache_bench_tools_lib", srcs=["cache/cache_bench_tool.cc"], deps=[":rocksdb_lib"], headers=None, link_whole=False, extra_test_libs=False)
|
||||
|
||||
rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
|
||||
"db_stress_tool/batched_ops_stress.cc",
|
||||
"db_stress_tool/cf_consistency_stress.cc",
|
||||
"db_stress_tool/db_stress_common.cc",
|
||||
"db_stress_tool/db_stress_driver.cc",
|
||||
"db_stress_tool/db_stress_filters.cc",
|
||||
"db_stress_tool/db_stress_gflags.cc",
|
||||
"db_stress_tool/db_stress_listener.cc",
|
||||
"db_stress_tool/db_stress_shared_state.cc",
|
||||
@@ -435,15 +407,11 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
|
||||
"test_util/testutil.cc",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
], headers=[])
|
||||
], headers=None)
|
||||
|
||||
|
||||
cpp_binary_wrapper(name="ldb", srcs=["tools/ldb.cc"], deps=[":rocksdb_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
|
||||
|
||||
cpp_binary_wrapper(name="db_stress", srcs=["db_stress_tool/db_stress.cc"], deps=[":rocksdb_stress_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
|
||||
|
||||
cpp_binary_wrapper(name="db_bench", srcs=["tools/db_bench.cc"], deps=[":rocksdb_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
|
||||
|
||||
cpp_binary_wrapper(name="cache_bench", srcs=["cache/cache_bench.cc"], deps=[":rocksdb_cache_bench_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
|
||||
|
||||
cpp_binary_wrapper(name="ribbon_bench", srcs=["microbench/ribbon_bench.cc"], deps=[], extra_preprocessor_flags=[], extra_bench_libs=True)
|
||||
@@ -4655,12 +4623,6 @@ cpp_unittest_wrapper(name="compact_files_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="compact_for_tiering_collector_test",
|
||||
srcs=["utilities/table_properties_collectors/compact_for_tiering_collector_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="compact_on_deletion_collector_test",
|
||||
srcs=["utilities/table_properties_collectors/compact_on_deletion_collector_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
@@ -5051,7 +5013,7 @@ cpp_unittest_wrapper(name="dynamic_bloom_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_library_wrapper(name="env_basic_test_lib", srcs=["env/env_basic_test.cc"], deps=[":rocksdb_test_lib"], headers=[], link_whole=False, extra_test_libs=True)
|
||||
cpp_library_wrapper(name="env_basic_test_lib", srcs=["env/env_basic_test.cc"], deps=[":rocksdb_test_lib"], headers=None, link_whole=False, extra_test_libs=True)
|
||||
|
||||
cpp_unittest_wrapper(name="env_basic_test",
|
||||
srcs=["env/env_basic_test.cc"],
|
||||
@@ -5101,12 +5063,6 @@ cpp_unittest_wrapper(name="external_sst_file_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="faiss_ivf_index_test",
|
||||
srcs=["utilities/secondary_index/faiss_ivf_index_test.cc"],
|
||||
deps=[":rocksdb_with_faiss_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="fault_injection_test",
|
||||
srcs=["db/fault_injection_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
@@ -38,11 +38,12 @@ Snowflake [uses](https://www.snowflake.com/blog/how-foundationdb-powers-snowflak
|
||||
The Bing search engine from Microsoft uses RocksDB as the storage engine for its web data platform: https://blogs.bing.com/Engineering-Blog/october-2021/RocksDB-in-Microsoft-Bing
|
||||
|
||||
## LinkedIn
|
||||
1. [Venice](https://venicedb.org/) is a derived data platform using RocksDB as its storage engine. It is LinkedIn's ML feature store, powering thousands of recommender use cases, including the Feed, Video recommendations, and People You May Know.
|
||||
2. LinkedIn's follow feed for storing user's activities. Check out the blog post: https://engineering.linkedin.com/blog/2016/03/followfeed--linkedin-s-feed-made-faster-and-smarter
|
||||
3. Apache Samza, open source framework for stream processing.
|
||||
Two different use cases at Linkedin are using RocksDB as a storage engine:
|
||||
|
||||
Learn more about LinkedIn's follow feed and Apache Samza in a Tech Talk by Ankit Gupta and Naveen Somasundaram: http://www.youtube.com/watch?v=plqVp_OnSzg
|
||||
1. LinkedIn's follow feed for storing user's activities. Check out the blog post: https://engineering.linkedin.com/blog/2016/03/followfeed--linkedin-s-feed-made-faster-and-smarter
|
||||
2. Apache Samza, open source framework for stream processing
|
||||
|
||||
Learn more about those use cases in a Tech Talk by Ankit Gupta and Naveen Somasundaram: http://www.youtube.com/watch?v=plqVp_OnSzg
|
||||
|
||||
## Yahoo
|
||||
Yahoo is using RocksDB as a storage engine for their biggest distributed data store Sherpa. Learn more about it here: http://yahooeng.tumblr.com/post/120730204806/sherpa-scales-new-heights
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
try:
|
||||
from builtins import str
|
||||
@@ -10,14 +11,14 @@ import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
from targets_builder import TARGETSBuilder, LiteralValue
|
||||
from targets_builder import TARGETSBuilder
|
||||
|
||||
from util import ColorString
|
||||
|
||||
# This script generates BUCK file for Buck.
|
||||
# This script generates TARGETS file for Buck.
|
||||
# Buck is a build tool specifying dependencies among different build targets.
|
||||
# User can pass extra dependencies as a JSON object via command line, and this
|
||||
# script can include these dependencies in the generate BUCK file.
|
||||
# script can include these dependencies in the generate TARGETS file.
|
||||
# Usage:
|
||||
# $python3 buckifier/buckify_rocksdb.py
|
||||
# (This generates a TARGET file without user-specified dependency for unit
|
||||
@@ -28,7 +29,7 @@ from util import ColorString
|
||||
# "extra_compiler_flags": ["-DFOO_BAR", "-Os"]
|
||||
# }
|
||||
# }'
|
||||
# (Generated BUCK file has test_dep and mock1 as dependencies for RocksDB
|
||||
# (Generated TARGETS file has test_dep and mock1 as dependencies for RocksDB
|
||||
# unit tests, and will use the extra_compiler_flags to compile the unit test
|
||||
# source.)
|
||||
|
||||
@@ -114,9 +115,9 @@ def get_dependencies():
|
||||
return deps_map
|
||||
|
||||
|
||||
# Prepare BUCK file for buck
|
||||
def generate_buck(repo_path, deps_map):
|
||||
print(ColorString.info("Generating BUCK"))
|
||||
# Prepare TARGETS file for buck
|
||||
def generate_targets(repo_path, deps_map):
|
||||
print(ColorString.info("Generating TARGETS"))
|
||||
# parsed src.mk file
|
||||
src_mk = parse_src_mk(repo_path)
|
||||
# get all .cc files
|
||||
@@ -131,12 +132,12 @@ def generate_buck(repo_path, deps_map):
|
||||
if len(sys.argv) >= 2:
|
||||
# Heuristically quote and canonicalize whitespace for inclusion
|
||||
# in how the file was generated.
|
||||
extra_argv = " '{}'".format(" ".join(sys.argv[1].split()))
|
||||
extra_argv = " '{0}'".format(" ".join(sys.argv[1].split()))
|
||||
|
||||
BUCK = TARGETSBuilder("%s/BUCK" % repo_path, extra_argv)
|
||||
TARGETS = TARGETSBuilder("%s/TARGETS" % repo_path, extra_argv)
|
||||
|
||||
# rocksdb_lib
|
||||
BUCK.add_library(
|
||||
TARGETS.add_library(
|
||||
"rocksdb_lib",
|
||||
src_mk["LIB_SOURCES"] +
|
||||
# always add range_tree, it's only excluded on ppc64, which we don't use internally
|
||||
@@ -149,29 +150,20 @@ def generate_buck(repo_path, deps_map):
|
||||
"//folly/experimental/coro:task",
|
||||
"//folly/synchronization:distributed_mutex",
|
||||
],
|
||||
headers=LiteralValue("glob([\"**/*.h\"])")
|
||||
)
|
||||
# rocksdb_whole_archive_lib
|
||||
BUCK.add_library(
|
||||
TARGETS.add_library(
|
||||
"rocksdb_whole_archive_lib",
|
||||
[],
|
||||
deps=[
|
||||
":rocksdb_lib",
|
||||
],
|
||||
headers=None,
|
||||
extra_external_deps="",
|
||||
link_whole=True,
|
||||
)
|
||||
# rocksdb_with_faiss_lib
|
||||
BUCK.add_library(
|
||||
"rocksdb_with_faiss_lib",
|
||||
src_mk.get("WITH_FAISS_LIB_SOURCES", []),
|
||||
deps=[
|
||||
"//faiss:faiss",
|
||||
":rocksdb_lib",
|
||||
],
|
||||
)
|
||||
# rocksdb_test_lib
|
||||
BUCK.add_library(
|
||||
TARGETS.add_library(
|
||||
"rocksdb_test_lib",
|
||||
src_mk.get("MOCK_LIB_SOURCES", [])
|
||||
+ src_mk.get("TEST_LIB_SOURCES", [])
|
||||
@@ -180,20 +172,8 @@ def generate_buck(repo_path, deps_map):
|
||||
[":rocksdb_lib"],
|
||||
extra_test_libs=True,
|
||||
)
|
||||
# rocksdb_with_faiss_test_lib
|
||||
BUCK.add_library(
|
||||
"rocksdb_with_faiss_test_lib",
|
||||
src_mk.get("MOCK_LIB_SOURCES", [])
|
||||
+ src_mk.get("TEST_LIB_SOURCES", [])
|
||||
+ src_mk.get("EXP_LIB_SOURCES", [])
|
||||
+ src_mk.get("ANALYZER_LIB_SOURCES", []),
|
||||
deps=[
|
||||
":rocksdb_with_faiss_lib",
|
||||
],
|
||||
extra_test_libs=True,
|
||||
)
|
||||
# rocksdb_tools_lib
|
||||
BUCK.add_library(
|
||||
TARGETS.add_library(
|
||||
"rocksdb_tools_lib",
|
||||
src_mk.get("BENCH_LIB_SOURCES", [])
|
||||
+ src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
@@ -201,51 +181,43 @@ def generate_buck(repo_path, deps_map):
|
||||
[":rocksdb_lib"],
|
||||
)
|
||||
# rocksdb_cache_bench_tools_lib
|
||||
BUCK.add_library(
|
||||
TARGETS.add_library(
|
||||
"rocksdb_cache_bench_tools_lib",
|
||||
src_mk.get("CACHE_BENCH_LIB_SOURCES", []),
|
||||
[":rocksdb_lib"],
|
||||
)
|
||||
# rocksdb_stress_lib
|
||||
BUCK.add_rocksdb_library(
|
||||
TARGETS.add_rocksdb_library(
|
||||
"rocksdb_stress_lib",
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
+ src_mk.get("STRESS_LIB_SOURCES", [])
|
||||
+ ["test_util/testutil.cc"],
|
||||
)
|
||||
# ldb binary
|
||||
BUCK.add_binary(
|
||||
"ldb", ["tools/ldb.cc"], [":rocksdb_tools_lib"]
|
||||
)
|
||||
# db_stress binary
|
||||
BUCK.add_binary(
|
||||
TARGETS.add_binary(
|
||||
"db_stress", ["db_stress_tool/db_stress.cc"], [":rocksdb_stress_lib"]
|
||||
)
|
||||
# db_bench binary
|
||||
BUCK.add_binary(
|
||||
"db_bench", ["tools/db_bench.cc"], [":rocksdb_tools_lib"]
|
||||
)
|
||||
# cache_bench binary
|
||||
BUCK.add_binary(
|
||||
TARGETS.add_binary(
|
||||
"cache_bench", ["cache/cache_bench.cc"], [":rocksdb_cache_bench_tools_lib"]
|
||||
)
|
||||
# bench binaries
|
||||
for src in src_mk.get("MICROBENCH_SOURCES", []):
|
||||
name = src.rsplit("/", 1)[1].split(".")[0] if "/" in src else src.split(".")[0]
|
||||
BUCK.add_binary(name, [src], [], extra_bench_libs=True)
|
||||
print(f"Extra dependencies:\n{json.dumps(deps_map)}")
|
||||
TARGETS.add_binary(name, [src], [], extra_bench_libs=True)
|
||||
print("Extra dependencies:\n{0}".format(json.dumps(deps_map)))
|
||||
|
||||
# Dictionary test executable name -> relative source file path
|
||||
test_source_map = {}
|
||||
|
||||
# c_test.c is added through BUCK.add_c_test(). If there
|
||||
# c_test.c is added through TARGETS.add_c_test(). If there
|
||||
# are more than one .c test file, we need to extend
|
||||
# BUCK.add_c_test() to include other C tests too.
|
||||
# TARGETS.add_c_test() to include other C tests too.
|
||||
for test_src in src_mk.get("TEST_MAIN_SOURCES_C", []):
|
||||
if test_src != "db/c_test.c":
|
||||
print("Don't know how to deal with " + test_src)
|
||||
return False
|
||||
BUCK.add_c_test()
|
||||
TARGETS.add_c_test()
|
||||
|
||||
try:
|
||||
with open(f"{repo_path}/buckifier/bench.json") as json_file:
|
||||
@@ -260,7 +232,7 @@ def generate_buck(repo_path, deps_map):
|
||||
for metric in overloaded_metric_list:
|
||||
if not isinstance(metric, dict):
|
||||
clean_benchmarks[binary][benchmark].append(metric)
|
||||
BUCK.add_fancy_bench_config(
|
||||
TARGETS.add_fancy_bench_config(
|
||||
config_dict["name"],
|
||||
clean_benchmarks,
|
||||
False,
|
||||
@@ -282,7 +254,7 @@ def generate_buck(repo_path, deps_map):
|
||||
if not isinstance(metric, dict):
|
||||
clean_benchmarks[binary][benchmark].append(metric)
|
||||
for config_dict in slow_fancy_bench_config_list:
|
||||
BUCK.add_fancy_bench_config(
|
||||
TARGETS.add_fancy_bench_config(
|
||||
config_dict["name"] + "_slow",
|
||||
clean_benchmarks,
|
||||
True,
|
||||
@@ -295,20 +267,15 @@ def generate_buck(repo_path, deps_map):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
BUCK.add_test_header()
|
||||
TARGETS.add_test_header()
|
||||
|
||||
for test_src in src_mk.get("TEST_MAIN_SOURCES", []):
|
||||
test = test_src.split(".c")[0].strip().split("/")[-1].strip()
|
||||
test_source_map[test] = (test_src, False)
|
||||
test_source_map[test] = test_src
|
||||
print("" + test + " " + test_src)
|
||||
|
||||
for test_src in src_mk.get("WITH_FAISS_TEST_MAIN_SOURCES", []):
|
||||
test = test_src.split(".c")[0].strip().split("/")[-1].strip()
|
||||
test_source_map[test] = (test_src, True)
|
||||
print("" + test + " " + test_src + " [FAISS]")
|
||||
|
||||
for target_alias, deps in deps_map.items():
|
||||
for test, (test_src, with_faiss) in sorted(test_source_map.items()):
|
||||
for test, test_src in sorted(test_source_map.items()):
|
||||
if len(test) == 0:
|
||||
print(ColorString.warning("Failed to get test name for %s" % test_src))
|
||||
continue
|
||||
@@ -317,39 +284,31 @@ def generate_buck(repo_path, deps_map):
|
||||
|
||||
if test in _EXPORTED_TEST_LIBS:
|
||||
test_library = "%s_lib" % test_target_name
|
||||
BUCK.add_library(
|
||||
TARGETS.add_library(
|
||||
test_library,
|
||||
[test_src],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_test_libs=True,
|
||||
)
|
||||
BUCK.register_test(
|
||||
TARGETS.register_test(
|
||||
test_target_name,
|
||||
test_src,
|
||||
deps=json.dumps(deps["extra_deps"] + [":" + test_library]),
|
||||
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
|
||||
)
|
||||
else:
|
||||
if with_faiss:
|
||||
BUCK.register_test(
|
||||
test_target_name,
|
||||
test_src,
|
||||
deps=json.dumps(deps["extra_deps"] + [":rocksdb_with_faiss_test_lib"]),
|
||||
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
|
||||
)
|
||||
else:
|
||||
BUCK.register_test(
|
||||
test_target_name,
|
||||
test_src,
|
||||
deps=json.dumps(deps["extra_deps"] + [":rocksdb_test_lib"]),
|
||||
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
|
||||
)
|
||||
BUCK.export_file("tools/db_crashtest.py")
|
||||
TARGETS.register_test(
|
||||
test_target_name,
|
||||
test_src,
|
||||
deps=json.dumps(deps["extra_deps"] + [":rocksdb_test_lib"]),
|
||||
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
|
||||
)
|
||||
TARGETS.export_file("tools/db_crashtest.py")
|
||||
|
||||
print(ColorString.info("Generated BUCK Summary:"))
|
||||
print(ColorString.info("- %d libs" % BUCK.total_lib))
|
||||
print(ColorString.info("- %d binarys" % BUCK.total_bin))
|
||||
print(ColorString.info("- %d tests" % BUCK.total_test))
|
||||
print(ColorString.info("Generated TARGETS Summary:"))
|
||||
print(ColorString.info("- %d libs" % TARGETS.total_lib))
|
||||
print(ColorString.info("- %d binarys" % TARGETS.total_bin))
|
||||
print(ColorString.info("- %d tests" % TARGETS.total_test))
|
||||
return True
|
||||
|
||||
|
||||
@@ -369,10 +328,10 @@ def exit_with_error(msg):
|
||||
|
||||
def main():
|
||||
deps_map = get_dependencies()
|
||||
# Generate BUCK file for buck
|
||||
ok = generate_buck(get_rocksdb_path(), deps_map)
|
||||
# Generate TARGETS file for buck
|
||||
ok = generate_targets(get_rocksdb_path(), deps_map)
|
||||
if not ok:
|
||||
exit_with_error("Failed to generate BUCK files")
|
||||
exit_with_error("Failed to generate TARGETS files")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,44 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# If clang_format_diff.py command is not specfied, we assume we are able to
|
||||
# access directly without any path.
|
||||
|
||||
if [[ ! -f "BUCK" ]]
|
||||
then
|
||||
echo "BUCK file is missing!"
|
||||
echo "Please do not remove / rename BUCK file in your commit(s)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TGT_DIFF=`git diff BUCK | head -n 1`
|
||||
TGT_DIFF=`git diff TARGETS | head -n 1`
|
||||
|
||||
if [ ! -z "$TGT_DIFF" ]
|
||||
then
|
||||
echo "BUCK file has uncommitted changes. Skip this check."
|
||||
echo "TARGETS file has uncommitted changes. Skip this check."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo Backup original BUCK file.
|
||||
echo Backup original TARGETS file.
|
||||
|
||||
cp BUCK BUCK.bkp
|
||||
cp TARGETS TARGETS.bkp
|
||||
|
||||
${PYTHON:-python3} buckifier/buckify_rocksdb.py
|
||||
|
||||
if [[ ! -f "BUCK" ]]
|
||||
then
|
||||
echo "BUCK file went missing after running buckifier/buckify_rocksdb.py!"
|
||||
echo "Please do not remove the BUCK file."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TGT_DIFF=`git diff BUCK | head -n 1`
|
||||
TGT_DIFF=`git diff TARGETS | head -n 1`
|
||||
|
||||
if [ -z "$TGT_DIFF" ]
|
||||
then
|
||||
mv BUCK.bkp BUCK
|
||||
mv TARGETS.bkp TARGETS
|
||||
exit 0
|
||||
else
|
||||
echo "Please run '${PYTHON:-python3} buckifier/buckify_rocksdb.py' to update BUCK file."
|
||||
echo "Do not manually update BUCK file."
|
||||
echo "Please run '${PYTHON:-python3} buckifier/buckify_rocksdb.py' to update TARGETS file."
|
||||
echo "Do not manually update TARGETS file."
|
||||
${PYTHON:-python3} --version
|
||||
mv BUCK.bkp BUCK
|
||||
mv TARGETS.bkp TARGETS
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
try:
|
||||
from builtins import object, str
|
||||
@@ -8,28 +9,17 @@ import pprint
|
||||
|
||||
import targets_cfg
|
||||
|
||||
class LiteralValue:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return str(self.value)
|
||||
|
||||
def smart_quote_value(val):
|
||||
if isinstance(val, LiteralValue):
|
||||
return str(val)
|
||||
return '"%s"' % val
|
||||
|
||||
def pretty_list(lst, indent=8):
|
||||
if lst is None or len(lst) == 0:
|
||||
return ""
|
||||
|
||||
if len(lst) == 1:
|
||||
return smart_quote_value(lst[0])
|
||||
return '"%s"' % lst[0]
|
||||
|
||||
separator = ',\n%s' % (" " * indent)
|
||||
res = separator.join(sorted(map(smart_quote_value, lst)))
|
||||
res = "\n" + (" " * indent) + res + ',\n' + (" " * (indent - 4))
|
||||
separator = '",\n%s"' % (" " * indent)
|
||||
res = separator.join(sorted(lst))
|
||||
res = "\n" + (" " * indent) + '"' + res + '",\n' + (" " * (indent - 4))
|
||||
return res
|
||||
|
||||
|
||||
@@ -58,12 +48,7 @@ class TARGETSBuilder:
|
||||
extra_test_libs=False,
|
||||
):
|
||||
if headers is not None:
|
||||
if isinstance(headers, LiteralValue):
|
||||
headers = str(headers)
|
||||
else:
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
else:
|
||||
headers = "[]"
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.library_template.format(
|
||||
@@ -80,7 +65,8 @@ class TARGETSBuilder:
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_rocksdb_library(self, name, srcs, headers=None, external_dependencies=None):
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
if headers is not None:
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.rocksdb_library_template.format(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
rocksdb_target_header_template = """# This file \100generated by:
|
||||
#$ python3 buckifier/buckify_rocksdb.py{extra_argv}
|
||||
@@ -6,7 +7,6 @@ rocksdb_target_header_template = """# This file \100generated by:
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
load("//rocks/buckifier:defs.bzl", "cpp_library_wrapper","rocks_cpp_library_wrapper","cpp_binary_wrapper","cpp_unittest_wrapper","fancy_bench_wrapper","add_c_test_wrapper")
|
||||
load("@fbcode_macros//build_defs:export_files.bzl", "export_file")
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"""
|
||||
This module keeps commonly used components.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
try:
|
||||
from builtins import object
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#
|
||||
# The solution is to move the include out of the #ifdef.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import re
|
||||
@@ -61,7 +62,7 @@ def expand_include(
|
||||
|
||||
included.add(include_path)
|
||||
with open(include_path) as f:
|
||||
print(f'#line 1 "{include_path}"', file=source_out)
|
||||
print('#line 1 "{}"'.format(include_path), file=source_out)
|
||||
process_file(
|
||||
f, include_path, source_out, header_out, include_paths, public_include_paths
|
||||
)
|
||||
@@ -117,7 +118,7 @@ def process_file(
|
||||
)
|
||||
|
||||
if expanded:
|
||||
print(f'#line {line + 1} "{abs_path}"', file=source_out)
|
||||
print('#line {} "{}"'.format(line + 1, abs_path), file=source_out)
|
||||
elif text != "#pragma once\n":
|
||||
source_out.write(text)
|
||||
|
||||
@@ -156,8 +157,8 @@ def main():
|
||||
with open(filename) as f, open(args.source_out, "w") as source_out, open(
|
||||
args.header_out, "w"
|
||||
) as header_out:
|
||||
print(f'#line 1 "{filename}"', file=source_out)
|
||||
print(f'#include "{header_out.name}"', file=source_out)
|
||||
print('#line 1 "{}"'.format(filename), file=source_out)
|
||||
print('#include "{}"'.format(header_out.name), file=source_out)
|
||||
process_file(
|
||||
f, abs_path, source_out, header_out, include_paths, public_include_paths
|
||||
)
|
||||
|
||||
@@ -102,7 +102,7 @@ class BenchmarkUtils:
|
||||
|
||||
|
||||
class ResultParser:
|
||||
def __init__(self, field=r"(\w|[+-:.%])+", intrafield=r"(\s)+", separator="\t"):
|
||||
def __init__(self, field="(\w|[+-:.%])+", intrafield="(\s)+", separator="\t"):
|
||||
self.field = re.compile(field)
|
||||
self.intra = re.compile(intrafield)
|
||||
self.sep = re.compile(separator)
|
||||
@@ -159,7 +159,7 @@ class ResultParser:
|
||||
|
||||
|
||||
def load_report_from_tsv(filename: str):
|
||||
file = open(filename)
|
||||
file = open(filename, "r")
|
||||
contents = file.readlines()
|
||||
file.close()
|
||||
parser = ResultParser()
|
||||
|
||||
@@ -360,13 +360,9 @@ EOF
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZSTD; then
|
||||
# Test whether zstd library is installed with minimum version
|
||||
# (Keep in sync with compression.h)
|
||||
# Test whether zstd library is installed
|
||||
$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
|
||||
@@ -589,7 +585,7 @@ EOF
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lbenchmark"
|
||||
fi
|
||||
fi
|
||||
if test $USE_FOLLY || test $USE_FOLLY_LITE; then
|
||||
if test $USE_FOLLY; then
|
||||
# Test whether libfolly library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <folly/synchronization/DistributedMutex.h>
|
||||
@@ -755,11 +751,6 @@ if [ "$USE_FOLLY" ]; then
|
||||
FOLLY_PATH=`cd $FOLLY_DIR && $PYTHON build/fbcode_builder/getdeps.py show-inst-dir folly`
|
||||
fi
|
||||
fi
|
||||
if [ "$USE_FOLLY_LITE" ]; then
|
||||
if [ "$FOLLY_DIR" ]; then
|
||||
BOOST_SOURCE_PATH=`cd $FOLLY_DIR && $PYTHON build/fbcode_builder/getdeps.py show-source-dir boost`
|
||||
fi
|
||||
fi
|
||||
|
||||
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
|
||||
@@ -801,7 +792,6 @@ echo "PROFILING_FLAGS=$PROFILING_FLAGS" >> "$OUTPUT"
|
||||
echo "FIND=$FIND" >> "$OUTPUT"
|
||||
echo "WATCH=$WATCH" >> "$OUTPUT"
|
||||
echo "FOLLY_PATH=$FOLLY_PATH" >> "$OUTPUT"
|
||||
echo "BOOST_SOURCE_PATH=$BOOST_SOURCE_PATH" >> "$OUTPUT"
|
||||
|
||||
# This will enable some related identifiers for the preprocessor
|
||||
if test -n "$JEMALLOC"; then
|
||||
|
||||
+11
-10
@@ -9,6 +9,7 @@
|
||||
- Prints those error messages to stdout
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import re
|
||||
import sys
|
||||
@@ -42,7 +43,7 @@ class GTestErrorParser(ErrorParserBase):
|
||||
return None
|
||||
gtest_fail_match = self._GTEST_FAIL_PATTERN.match(line)
|
||||
if gtest_fail_match:
|
||||
return "{} failed: {}".format(self._last_gtest_name, gtest_fail_match.group(1))
|
||||
return "%s failed: %s" % (self._last_gtest_name, gtest_fail_match.group(1))
|
||||
return None
|
||||
|
||||
|
||||
@@ -65,52 +66,52 @@ class CompilerErrorParser(MatchErrorParser):
|
||||
# format (link error):
|
||||
# '<filename>:<line #>: error: <error msg>'
|
||||
# The below regex catches both
|
||||
super().__init__(r"\S+:\d+: error:")
|
||||
super(CompilerErrorParser, self).__init__(r"\S+:\d+: error:")
|
||||
|
||||
|
||||
class ScanBuildErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"scan-build: \d+ bugs found.$")
|
||||
super(ScanBuildErrorParser, self).__init__(r"scan-build: \d+ bugs found.$")
|
||||
|
||||
|
||||
class DbCrashErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"\*\*\*.*\^$|TEST FAILED.")
|
||||
super(DbCrashErrorParser, self).__init__(r"\*\*\*.*\^$|TEST FAILED.")
|
||||
|
||||
|
||||
class WriteStressErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
super(WriteStressErrorParser, self).__init__(
|
||||
r"ERROR: write_stress died with exitcode=\d+"
|
||||
)
|
||||
|
||||
|
||||
class AsanErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"==\d+==ERROR: AddressSanitizer:")
|
||||
super(AsanErrorParser, self).__init__(r"==\d+==ERROR: AddressSanitizer:")
|
||||
|
||||
|
||||
class UbsanErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
# format: '<filename>:<line #>:<column #>: runtime error: <error msg>'
|
||||
super().__init__(r"\S+:\d+:\d+: runtime error:")
|
||||
super(UbsanErrorParser, self).__init__(r"\S+:\d+:\d+: runtime error:")
|
||||
|
||||
|
||||
class ValgrindErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
# just grab the summary, valgrind doesn't clearly distinguish errors
|
||||
# from other log messages.
|
||||
super().__init__(r"==\d+== ERROR SUMMARY:")
|
||||
super(ValgrindErrorParser, self).__init__(r"==\d+== ERROR SUMMARY:")
|
||||
|
||||
|
||||
class CompatErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"==== .*[Ee]rror.* ====$")
|
||||
super(CompatErrorParser, self).__init__(r"==== .*[Ee]rror.* ====$")
|
||||
|
||||
|
||||
class TsanErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"WARNING: ThreadSanitizer:")
|
||||
super(TsanErrorParser, self).__init__(r"WARNING: ThreadSanitizer:")
|
||||
|
||||
|
||||
_TEST_NAME_TO_PARSERS = {
|
||||
|
||||
@@ -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 -DZSTD_STATIC_LINKING_ONLY"
|
||||
CFLAGS+=" -DZSTD"
|
||||
fi
|
||||
|
||||
# location of gflags headers and libraries
|
||||
|
||||
Vendored
+13
-18
@@ -118,6 +118,7 @@ Status SecondaryCache::CreateFromString(
|
||||
sec_cache = NewCompressedSecondaryCache(sec_cache_opts);
|
||||
}
|
||||
|
||||
|
||||
if (status.ok()) {
|
||||
result->swap(sec_cache);
|
||||
}
|
||||
@@ -132,25 +133,19 @@ Status Cache::CreateFromString(const ConfigOptions& config_options,
|
||||
std::shared_ptr<Cache>* result) {
|
||||
Status status;
|
||||
std::shared_ptr<Cache> cache;
|
||||
if (StartsWith(value, "null")) {
|
||||
cache = nullptr;
|
||||
} else if (value.find("://") == std::string::npos) {
|
||||
if (value.find('=') == std::string::npos) {
|
||||
cache = NewLRUCache(ParseSizeT(value));
|
||||
} else {
|
||||
LRUCacheOptions cache_opts;
|
||||
status = OptionTypeInfo::ParseStruct(config_options, "",
|
||||
&lru_cache_options_type_info, "",
|
||||
value, &cache_opts);
|
||||
if (status.ok()) {
|
||||
cache = NewLRUCache(cache_opts);
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
result->swap(cache);
|
||||
}
|
||||
if (value.find('=') == std::string::npos) {
|
||||
cache = NewLRUCache(ParseSizeT(value));
|
||||
} else {
|
||||
status = LoadSharedObject<Cache>(config_options, value, result);
|
||||
LRUCacheOptions cache_opts;
|
||||
status = OptionTypeInfo::ParseStruct(config_options, "",
|
||||
&lru_cache_options_type_info, "",
|
||||
value, &cache_opts);
|
||||
if (status.ok()) {
|
||||
cache = NewLRUCache(cache_opts);
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
result->swap(cache);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Vendored
+2
-1
@@ -191,7 +191,8 @@ namespace {
|
||||
class SharedState {
|
||||
public:
|
||||
explicit SharedState(CacheBench* cache_bench)
|
||||
: cv_(&mu_), cache_bench_(cache_bench) {}
|
||||
: cv_(&mu_),
|
||||
cache_bench_(cache_bench) {}
|
||||
|
||||
~SharedState() = default;
|
||||
|
||||
|
||||
Vendored
-84
@@ -18,7 +18,6 @@
|
||||
#include "cache/lru_cache.h"
|
||||
#include "cache/typed_cache.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "table/block_based/block_cache.h"
|
||||
#include "test_util/secondary_cache_test_util.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/coding.h"
|
||||
@@ -886,32 +885,6 @@ TEST_P(CacheTest, ApplyToAllEntriesDuringResize) {
|
||||
ASSERT_EQ(special_count, kSpecialCount);
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, ApplyToHandleTest) {
|
||||
std::string callback_state;
|
||||
const auto callback = [&](const Slice& key, Cache::ObjectPtr value,
|
||||
size_t charge,
|
||||
const Cache::CacheItemHelper* helper) {
|
||||
callback_state = std::to_string(DecodeKey(key)) + "," +
|
||||
std::to_string(DecodeValue(value)) + "," +
|
||||
std::to_string(charge);
|
||||
assert(helper == &CacheTest::kHelper);
|
||||
};
|
||||
|
||||
std::vector<std::string> inserted;
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Insert(i, i * 2, i + 1);
|
||||
inserted.push_back(std::to_string(i) + "," + std::to_string(i * 2) + "," +
|
||||
std::to_string(i + 1));
|
||||
}
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Cache::Handle* handle = cache_->Lookup(EncodeKey(i));
|
||||
cache_->ApplyToHandle(cache_.get(), handle, callback);
|
||||
EXPECT_EQ(inserted[i], callback_state);
|
||||
cache_->Release(handle);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, DefaultShardBits) {
|
||||
// Prevent excessive allocation (to save time & space)
|
||||
estimated_value_size_ = 100000;
|
||||
@@ -1044,63 +1017,6 @@ INSTANTIATE_TEST_CASE_P(CacheTestInstance, CacheTest,
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, LRUCacheTest,
|
||||
testing::Values(secondary_cache_test_util::kLRU));
|
||||
|
||||
TEST(MiscBlockCacheTest, UncacheAggressivenessAdvisor) {
|
||||
// Aggressiveness to a sequence of Report() calls (as string of 0s and 1s)
|
||||
// exactly until the first ShouldContinue() == false.
|
||||
const std::vector<std::pair<uint32_t, Slice>> expectedTraces{
|
||||
// Aggressiveness 1 aborts on first unsuccessful erasure.
|
||||
{1, "0"},
|
||||
{1, "11111111111111111111110"},
|
||||
// For sufficient evidence, aggressiveness 2 requires a minimum of two
|
||||
// unsuccessful erasures.
|
||||
{2, "00"},
|
||||
{2, "0110"},
|
||||
{2, "1100"},
|
||||
{2, "011111111111111111111111111111111111111111111111111111111111111100"},
|
||||
{2, "0111111111111111111111111111111111110"},
|
||||
// For sufficient evidence, aggressiveness 3 and higher require a minimum
|
||||
// of three unsuccessful erasures.
|
||||
{3, "000"},
|
||||
{3, "01010"},
|
||||
{3, "111000"},
|
||||
{3, "00111111111111111111111111111111111100"},
|
||||
{3, "00111111111111111111110"},
|
||||
|
||||
{4, "000"},
|
||||
{4, "01010"},
|
||||
{4, "111000"},
|
||||
{4, "001111111111111111111100"},
|
||||
{4, "0011111111111110"},
|
||||
|
||||
{6, "000"},
|
||||
{6, "01010"},
|
||||
{6, "111000"},
|
||||
{6, "00111111111111100"},
|
||||
{6, "0011111110"},
|
||||
|
||||
// 69 -> 50% threshold, now up to minimum of 4
|
||||
{69, "0000"},
|
||||
{69, "010000"},
|
||||
{69, "01010000"},
|
||||
{69, "101010100010101000"},
|
||||
|
||||
// 230 -> 10% threshold, appropriately higher minimum
|
||||
{230, "000000000000"},
|
||||
{230, "0000000000010000000000"},
|
||||
{230, "00000000000100000000010000000000"}};
|
||||
for (const auto& [aggressiveness, t] : expectedTraces) {
|
||||
SCOPED_TRACE("aggressiveness=" + std::to_string(aggressiveness) + " with " +
|
||||
t.ToString());
|
||||
UncacheAggressivenessAdvisor uaa(aggressiveness);
|
||||
for (size_t i = 0; i < t.size(); ++i) {
|
||||
SCOPED_TRACE("i=" + std::to_string(i));
|
||||
ASSERT_TRUE(uaa.ShouldContinue());
|
||||
uaa.Report(t[i] & 1);
|
||||
}
|
||||
ASSERT_FALSE(uaa.ShouldContinue());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
Vendored
-16
@@ -1444,22 +1444,6 @@ const Cache::CacheItemHelper* BaseHyperClockCache<Table>::GetCacheItemHelper(
|
||||
return h->helper;
|
||||
}
|
||||
|
||||
template <class Table>
|
||||
void BaseHyperClockCache<Table>::ApplyToHandle(
|
||||
Cache* cache, Handle* handle,
|
||||
const std::function<void(const Slice& key, Cache::ObjectPtr value,
|
||||
size_t charge, const CacheItemHelper* helper)>&
|
||||
callback) {
|
||||
BaseHyperClockCache<Table>* cache_ptr =
|
||||
static_cast<BaseHyperClockCache<Table>*>(cache);
|
||||
auto h = static_cast<const typename Table::HandleImpl*>(handle);
|
||||
UniqueId64x2 unhashed;
|
||||
auto hash_seed = cache_ptr->GetShard(h->GetHash()).GetTable().GetHashSeed();
|
||||
callback(
|
||||
ClockCacheShard<Table>::ReverseHash(h->hashed_key, &unhashed, hash_seed),
|
||||
h->value, h->GetTotalCharge(), h->helper);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// For each cache shard, estimate what the table load factor would be if
|
||||
|
||||
Vendored
-6
@@ -1128,12 +1128,6 @@ class BaseHyperClockCache : public ShardedCache<ClockCacheShard<Table>> {
|
||||
|
||||
const CacheItemHelper* GetCacheItemHelper(Handle* handle) const override;
|
||||
|
||||
void ApplyToHandle(
|
||||
Cache* cache, Handle* handle,
|
||||
const std::function<void(const Slice& key, Cache::ObjectPtr obj,
|
||||
size_t charge, const CacheItemHelper* helper)>&
|
||||
callback) override;
|
||||
|
||||
void ReportProblems(
|
||||
const std::shared_ptr<Logger>& /*info_log*/) const override;
|
||||
};
|
||||
|
||||
Vendored
+8
-38
@@ -79,11 +79,7 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
data_ptr = GetVarint32Ptr(data_ptr, data_ptr + 1,
|
||||
static_cast<uint32_t*>(&source_32));
|
||||
source = static_cast<CacheTier>(source_32);
|
||||
uint64_t data_size = 0;
|
||||
data_ptr = GetVarint64Ptr(data_ptr, ptr->get() + handle_value_charge,
|
||||
static_cast<uint64_t*>(&data_size));
|
||||
assert(handle_value_charge > data_size);
|
||||
handle_value_charge = data_size;
|
||||
handle_value_charge -= (data_ptr - ptr->get());
|
||||
}
|
||||
MemoryAllocator* allocator = cache_options_.memory_allocator.get();
|
||||
|
||||
@@ -173,15 +169,13 @@ Status CompressedSecondaryCache::InsertInternal(
|
||||
}
|
||||
|
||||
auto internal_helper = GetHelper(cache_options_.enable_custom_split_merge);
|
||||
char header[20];
|
||||
char header[10];
|
||||
char* payload = header;
|
||||
payload = EncodeVarint32(payload, static_cast<uint32_t>(type));
|
||||
payload = EncodeVarint32(payload, static_cast<uint32_t>(source));
|
||||
size_t data_size = (*helper->size_cb)(value);
|
||||
char* data_size_ptr = payload;
|
||||
payload = EncodeVarint64(payload, data_size);
|
||||
|
||||
size_t header_size = payload - header;
|
||||
size_t data_size = (*helper->size_cb)(value);
|
||||
size_t total_size = data_size + header_size;
|
||||
CacheAllocationPtr ptr =
|
||||
AllocateBlock(total_size, cache_options_.memory_allocator.get());
|
||||
@@ -216,8 +210,6 @@ Status CompressedSecondaryCache::InsertInternal(
|
||||
|
||||
val = Slice(compressed_val);
|
||||
data_size = compressed_val.size();
|
||||
payload = EncodeVarint64(data_size_ptr, data_size);
|
||||
header_size = payload - header;
|
||||
total_size = header_size + data_size;
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_compressed_bytes, data_size);
|
||||
|
||||
@@ -230,21 +222,14 @@ Status CompressedSecondaryCache::InsertInternal(
|
||||
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_insert_real_count, 1);
|
||||
if (cache_options_.enable_custom_split_merge) {
|
||||
size_t split_charge{0};
|
||||
CacheValueChunk* value_chunks_head = SplitValueIntoChunks(
|
||||
val, cache_options_.compression_type, split_charge);
|
||||
return cache_->Insert(key, value_chunks_head, internal_helper,
|
||||
split_charge);
|
||||
size_t charge{0};
|
||||
CacheValueChunk* value_chunks_head =
|
||||
SplitValueIntoChunks(val, cache_options_.compression_type, charge);
|
||||
return cache_->Insert(key, value_chunks_head, internal_helper, charge);
|
||||
} else {
|
||||
#ifdef ROCKSDB_MALLOC_USABLE_SIZE
|
||||
size_t charge = malloc_usable_size(ptr.get());
|
||||
#else
|
||||
size_t charge = total_size;
|
||||
#endif
|
||||
std::memcpy(ptr.get(), header, header_size);
|
||||
CacheAllocationPtr* buf = new CacheAllocationPtr(std::move(ptr));
|
||||
charge += sizeof(CacheAllocationPtr);
|
||||
return cache_->Insert(key, buf, internal_helper, charge);
|
||||
return cache_->Insert(key, buf, internal_helper, total_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,21 +398,6 @@ const Cache::CacheItemHelper* CompressedSecondaryCache::GetHelper(
|
||||
}
|
||||
}
|
||||
|
||||
size_t CompressedSecondaryCache::TEST_GetCharge(const Slice& key) {
|
||||
Cache::Handle* lru_handle = cache_->Lookup(key);
|
||||
if (lru_handle == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t charge = cache_->GetCharge(lru_handle);
|
||||
if (cache_->Value(lru_handle) != nullptr &&
|
||||
!cache_options_.enable_custom_split_merge) {
|
||||
charge -= 10;
|
||||
}
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
|
||||
return charge;
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache>
|
||||
CompressedSecondaryCacheOptions::MakeSharedSecondaryCache() const {
|
||||
return std::make_shared<CompressedSecondaryCache>(*this);
|
||||
|
||||
Vendored
-2
@@ -139,8 +139,6 @@ class CompressedSecondaryCache : public SecondaryCache {
|
||||
const Cache::CacheItemHelper* helper,
|
||||
CompressionType type, CacheTier source);
|
||||
|
||||
size_t TEST_GetCharge(const Slice& key);
|
||||
|
||||
// TODO: clean up to use cleaner interfaces in typed_cache.h
|
||||
const Cache::CacheItemHelper* GetHelper(bool enable_custom_split_merge) const;
|
||||
std::shared_ptr<Cache> cache_;
|
||||
|
||||
+1
-4
@@ -39,8 +39,6 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
protected:
|
||||
void BasicTestHelper(std::shared_ptr<SecondaryCache> sec_cache,
|
||||
bool sec_cache_is_compressed) {
|
||||
CompressedSecondaryCache* comp_sec_cache =
|
||||
static_cast<CompressedSecondaryCache*>(sec_cache.get());
|
||||
get_perf_context()->Reset();
|
||||
bool kept_in_sec_cache{true};
|
||||
// Lookup an non-existent key.
|
||||
@@ -68,8 +66,6 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 1);
|
||||
|
||||
ASSERT_GT(comp_sec_cache->TEST_GetCharge(key1), 1000);
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1_2 =
|
||||
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/true,
|
||||
/*stats=*/nullptr, kept_in_sec_cache);
|
||||
@@ -845,6 +841,7 @@ TEST_P(CompressedSecondaryCacheTestWithCompressionParam,
|
||||
BasicTestHelper(sec_cache, sec_cache_is_compressed_);
|
||||
}
|
||||
|
||||
|
||||
TEST_P(CompressedSecondaryCacheTestWithCompressionParam, FailsTest) {
|
||||
FailsTest(sec_cache_is_compressed_);
|
||||
}
|
||||
|
||||
Vendored
+3
-13
@@ -93,8 +93,9 @@ 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];
|
||||
@@ -676,17 +677,6 @@ const Cache::CacheItemHelper* LRUCache::GetCacheItemHelper(
|
||||
return h->helper;
|
||||
}
|
||||
|
||||
void LRUCache::ApplyToHandle(
|
||||
Cache* cache, Handle* handle,
|
||||
const std::function<void(const Slice& key, ObjectPtr value, size_t charge,
|
||||
const CacheItemHelper* helper)>& callback) {
|
||||
auto cache_ptr = static_cast<LRUCache*>(cache);
|
||||
auto h = static_cast<const LRUHandle*>(handle);
|
||||
callback(h->key(), h->value,
|
||||
h->GetCharge(cache_ptr->GetShard(0).metadata_charge_policy_),
|
||||
h->helper);
|
||||
}
|
||||
|
||||
size_t LRUCache::TEST_GetLRUSize() {
|
||||
return SumOverShards([](LRUCacheShard& cs) { return cs.TEST_GetLRUSize(); });
|
||||
}
|
||||
|
||||
Vendored
-6
@@ -452,12 +452,6 @@ class LRUCache
|
||||
size_t GetCharge(Handle* handle) const override;
|
||||
const CacheItemHelper* GetCacheItemHelper(Handle* handle) const override;
|
||||
|
||||
void ApplyToHandle(
|
||||
Cache* cache, Handle* handle,
|
||||
const std::function<void(const Slice& key, ObjectPtr obj, size_t charge,
|
||||
const CacheItemHelper* helper)>& callback)
|
||||
override;
|
||||
|
||||
// Retrieves number of elements in LRU, for unit test purpose only.
|
||||
size_t TEST_GetLRUSize();
|
||||
// Retrieves high pri pool ratio.
|
||||
|
||||
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
+3
-1
@@ -7,4 +7,6 @@
|
||||
|
||||
#include "cache/cache_entry_roles.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {} // namespace ROCKSDB_NAMESPACE
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+1
-2
@@ -271,8 +271,7 @@ Status CacheWithSecondaryAdapter::Insert(const Slice& key, ObjectPtr value,
|
||||
// Warm up the secondary cache with the compressed block. The secondary
|
||||
// cache may choose to ignore it based on the admission policy.
|
||||
if (value != nullptr && !compressed_value.empty() &&
|
||||
adm_policy_ == TieredAdmissionPolicy::kAdmPolicyThreeQueue &&
|
||||
helper->IsSecondaryCacheCompatible()) {
|
||||
adm_policy_ == TieredAdmissionPolicy::kAdmPolicyThreeQueue) {
|
||||
Status status = secondary_cache_->InsertSaved(key, compressed_value, type);
|
||||
assert(status.ok() || status.IsNotSupported());
|
||||
}
|
||||
|
||||
Vendored
+5
-69
@@ -253,7 +253,6 @@ TEST_F(DBTieredSecondaryCacheTest, BasicTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
// Disable paranoid_file_checks so that flush will not read back the newly
|
||||
@@ -365,7 +364,6 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
@@ -508,7 +506,6 @@ TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
@@ -609,7 +606,6 @@ TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
@@ -721,7 +717,6 @@ TEST_F(DBTieredSecondaryCacheTest, IterateTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
@@ -765,54 +760,6 @@ TEST_F(DBTieredSecondaryCacheTest, IterateTest) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBTieredSecondaryCacheTest, VolatileTierTest) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
// We want a block cache of size 5KB, and a compressed secondary cache of
|
||||
// size 5KB. However, we specify a block cache size of 256KB here in order
|
||||
// to take into account the cache reservation in the block cache on
|
||||
// behalf of the compressed cache. The unit of cache reservation is 256KB.
|
||||
// The effective block cache capacity will be calculated as 256 + 5 = 261KB,
|
||||
// and 256KB will be reserved for the compressed cache, leaving 5KB for
|
||||
// the primary block cache. We only have to worry about this here because
|
||||
// the cache size is so small.
|
||||
table_options.block_cache = NewCache(256 * 1024, 5 * 1024, 256 * 1024);
|
||||
table_options.block_size = 4 * 1024;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
// Disable paranoid_file_checks so that flush will not read back the newly
|
||||
// written file
|
||||
options.paranoid_file_checks = false;
|
||||
options.lowest_used_cache_tier = CacheTier::kVolatileTier;
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
const int N = 256;
|
||||
for (int i = 0; i < N; i++) {
|
||||
std::string p_v;
|
||||
test::CompressibleString(&rnd, 0.5, 1007, &p_v);
|
||||
ASSERT_OK(Put(Key(i), p_v));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Since lowest_used_cache_tier is the volatile tier, nothing should be
|
||||
// inserted in the secondary cache.
|
||||
std::string v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 0u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 0u);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
class DBTieredAdmPolicyTest
|
||||
: public DBTieredSecondaryCacheTest,
|
||||
public testing::WithParamInterface<TieredAdmissionPolicy> {};
|
||||
@@ -837,7 +784,6 @@ TEST_P(DBTieredAdmPolicyTest, CompressedOnlyTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
size_t comp_cache_usage = compressed_secondary_cache()->TEST_GetUsage();
|
||||
@@ -890,7 +836,6 @@ TEST_P(DBTieredAdmPolicyTest, CompressedCacheAdmission) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
size_t comp_cache_usage = compressed_secondary_cache()->TEST_GetUsage();
|
||||
@@ -951,20 +896,12 @@ TEST_F(DBTieredSecondaryCacheTest, FSBufferTest) {
|
||||
IODebugContext* dbg) override {
|
||||
for (size_t i = 0; i < num_reqs; ++i) {
|
||||
FSReadRequest& req = reqs[i];
|
||||
// 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,
|
||||
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);
|
||||
FSAllocationPtr buffer(new char[req.len], [](void* ptr) {
|
||||
delete[] static_cast<char*>(ptr);
|
||||
});
|
||||
req.fs_scratch = std::move(internalPtr);
|
||||
req.fs_scratch = std::move(buffer);
|
||||
req.status = Read(req.offset, req.len, options, &req.result,
|
||||
static_cast<char*>(req.fs_scratch.get()), dbg);
|
||||
}
|
||||
return IOStatus::OK();
|
||||
}
|
||||
@@ -1000,7 +937,6 @@ TEST_F(DBTieredSecondaryCacheTest, FSBufferTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.env = wrap_env.get();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import optparse
|
||||
import re
|
||||
@@ -108,11 +109,11 @@ def report_coverage():
|
||||
|
||||
# Check if we need to display coverage info for interested files.
|
||||
if len(interested_files):
|
||||
per_file_coverage = {
|
||||
fname: per_file_coverage[fname]
|
||||
per_file_coverage = dict(
|
||||
(fname, per_file_coverage[fname])
|
||||
for fname in interested_files
|
||||
if fname in per_file_coverage
|
||||
}
|
||||
)
|
||||
# If we only interested in several files, it makes no sense to report
|
||||
# the total_coverage
|
||||
total_coverage = None
|
||||
|
||||
+38
-128
@@ -45,137 +45,24 @@ void ArenaWrappedDBIter::Init(
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iteration,
|
||||
uint64_t version_number, ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh, bool expose_blob_index, bool allow_refresh) {
|
||||
auto mem = arena_.AllocateAligned(sizeof(DBIter));
|
||||
db_iter_ = new (mem) DBIter(
|
||||
env, read_options, ioptions, mutable_cf_options, ioptions.user_comparator,
|
||||
/* iter */ nullptr, version, sequence, true,
|
||||
max_sequential_skip_in_iteration, read_callback, cfh, expose_blob_index);
|
||||
sv_number_ = version_number;
|
||||
read_options_ = read_options;
|
||||
allow_refresh_ = allow_refresh;
|
||||
memtable_range_tombstone_iter_ = nullptr;
|
||||
|
||||
if (!CheckFSFeatureSupport(env->GetFileSystem().get(),
|
||||
FSSupportedOps::kAsyncIO)) {
|
||||
read_options_.async_io = false;
|
||||
}
|
||||
read_options_.total_order_seek |= ioptions.prefix_seek_opt_in_only;
|
||||
|
||||
auto mem = arena_.AllocateAligned(sizeof(DBIter));
|
||||
db_iter_ = new (mem) DBIter(env, read_options_, ioptions, mutable_cf_options,
|
||||
ioptions.user_comparator,
|
||||
/* iter */ nullptr, version, sequence, true,
|
||||
max_sequential_skip_in_iteration, read_callback,
|
||||
cfh, expose_blob_index);
|
||||
|
||||
sv_number_ = version_number;
|
||||
allow_refresh_ = allow_refresh;
|
||||
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.");
|
||||
@@ -195,9 +82,32 @@ 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) {
|
||||
DoRefresh(snapshot, cur_sv_number);
|
||||
reinit_internal_iter();
|
||||
break;
|
||||
} else {
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
|
||||
@@ -225,13 +135,13 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
// The memtable under DBIter did not have range tombstone before
|
||||
// refresh.
|
||||
DoRefresh(snapshot, cur_sv_number);
|
||||
reinit_internal_iter();
|
||||
break;
|
||||
} else {
|
||||
*memtable_range_tombstone_iter_ =
|
||||
std::make_unique<TruncatedRangeDelIterator>(
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
|
||||
&cfd->internal_comparator(), nullptr, nullptr);
|
||||
delete *memtable_range_tombstone_iter_;
|
||||
*memtable_range_tombstone_iter_ = new TruncatedRangeDelIterator(
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
|
||||
&cfd->internal_comparator(), nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
|
||||
@@ -55,8 +55,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
db_iter_->SetIter(iter);
|
||||
}
|
||||
|
||||
void SetMemtableRangetombstoneIter(
|
||||
std::unique_ptr<TruncatedRangeDelIterator>* iter) {
|
||||
void SetMemtableRangetombstoneIter(TruncatedRangeDelIterator** iter) {
|
||||
memtable_range_tombstone_iter_ = iter;
|
||||
}
|
||||
|
||||
@@ -65,26 +64,12 @@ 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 {
|
||||
MaybeAutoRefresh(true /* is_seek */, DBIter::kForward);
|
||||
db_iter_->Seek(target);
|
||||
}
|
||||
|
||||
void Seek(const Slice& target) override { 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();
|
||||
MaybeAutoRefresh(false /* is_seek */, DBIter::kForward);
|
||||
}
|
||||
|
||||
void Prev() override {
|
||||
db_iter_->Prev();
|
||||
MaybeAutoRefresh(false /* is_seek */, DBIter::kReverse);
|
||||
}
|
||||
|
||||
void Next() override { db_iter_->Next(); }
|
||||
void Prev() override { db_iter_->Prev(); }
|
||||
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(); }
|
||||
@@ -97,8 +82,6 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
Status Refresh() override;
|
||||
Status Refresh(const Snapshot*) override;
|
||||
|
||||
bool PrepareValue() override { return db_iter_->PrepareValue(); }
|
||||
|
||||
void Init(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
@@ -117,9 +100,6 @@ 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_;
|
||||
@@ -130,8 +110,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
bool allow_refresh_ = true;
|
||||
// If this is nullptr, it means the mutable memtable does not contain range
|
||||
// tombstone when added under this DBIter.
|
||||
std::unique_ptr<TruncatedRangeDelIterator>* memtable_range_tombstone_iter_ =
|
||||
nullptr;
|
||||
TruncatedRangeDelIterator** memtable_range_tombstone_iter_ = nullptr;
|
||||
};
|
||||
|
||||
// Generate the arena wrapped iterator class.
|
||||
|
||||
@@ -13,11 +13,14 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class AttributeGroupIteratorImpl : public AttributeGroupIterator {
|
||||
public:
|
||||
AttributeGroupIteratorImpl(
|
||||
const ReadOptions& read_options, const Comparator* comparator,
|
||||
std::vector<std::pair<ColumnFamilyHandle*, std::unique_ptr<Iterator>>>&&
|
||||
cfh_iter_pairs)
|
||||
: impl_(read_options, comparator, std::move(cfh_iter_pairs),
|
||||
ResetFunc(this), PopulateFunc(this)) {}
|
||||
const Comparator* comparator,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
const std::vector<Iterator*>& child_iterators)
|
||||
: impl_(
|
||||
comparator, column_families, child_iterators, [this]() { Reset(); },
|
||||
[this](const autovector<MultiCfIteratorInfo>& items) {
|
||||
AddToAttributeGroups(items);
|
||||
}) {}
|
||||
~AttributeGroupIteratorImpl() override {}
|
||||
|
||||
// No copy allowed
|
||||
@@ -42,36 +45,8 @@ class AttributeGroupIteratorImpl : public AttributeGroupIterator {
|
||||
|
||||
void Reset() { attribute_groups_.clear(); }
|
||||
|
||||
bool PrepareValue() override { return impl_.PrepareValue(); }
|
||||
|
||||
private:
|
||||
class ResetFunc {
|
||||
public:
|
||||
explicit ResetFunc(AttributeGroupIteratorImpl* iter) : iter_(iter) {}
|
||||
|
||||
void operator()() const {
|
||||
assert(iter_);
|
||||
iter_->Reset();
|
||||
}
|
||||
|
||||
private:
|
||||
AttributeGroupIteratorImpl* iter_;
|
||||
};
|
||||
|
||||
class PopulateFunc {
|
||||
public:
|
||||
explicit PopulateFunc(AttributeGroupIteratorImpl* iter) : iter_(iter) {}
|
||||
|
||||
void operator()(const autovector<MultiCfIteratorInfo>& items) const {
|
||||
assert(iter_);
|
||||
iter_->AddToAttributeGroups(items);
|
||||
}
|
||||
|
||||
private:
|
||||
AttributeGroupIteratorImpl* iter_;
|
||||
};
|
||||
|
||||
MultiCfIteratorImpl<ResetFunc, PopulateFunc> impl_;
|
||||
MultiCfIteratorImpl impl_;
|
||||
IteratorAttributeGroups attribute_groups_;
|
||||
void AddToAttributeGroups(const autovector<MultiCfIteratorInfo>& items);
|
||||
};
|
||||
|
||||
@@ -188,12 +188,10 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
|
||||
}
|
||||
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
FileOptions fo_copy;
|
||||
|
||||
{
|
||||
assert(file_options_);
|
||||
fo_copy = *file_options_;
|
||||
fo_copy.write_hint = write_hint_;
|
||||
Status s = NewWritableFile(fs_, blob_file_path, &file, fo_copy);
|
||||
Status s = NewWritableFile(fs_, blob_file_path, &file, *file_options_);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"BlobFileBuilder::OpenBlobFileIfNeeded:NewWritableFile", &s);
|
||||
@@ -211,9 +209,7 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
|
||||
|
||||
assert(file);
|
||||
file->SetIOPriority(write_options_->rate_limiter_priority);
|
||||
// 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->SetWriteLifeTimeHint(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(
|
||||
|
||||
@@ -42,7 +42,6 @@ Status BlobFileCache::GetBlobFileReader(
|
||||
assert(blob_file_reader);
|
||||
assert(blob_file_reader->IsEmpty());
|
||||
|
||||
// NOTE: sharing same Cache with table_cache
|
||||
const Slice key = GetSliceForKey(&blob_file_number);
|
||||
|
||||
assert(cache_);
|
||||
@@ -99,13 +98,4 @@ Status BlobFileCache::GetBlobFileReader(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void BlobFileCache::Evict(uint64_t blob_file_number) {
|
||||
// NOTE: sharing same Cache with table_cache
|
||||
const Slice key = GetSliceForKey(&blob_file_number);
|
||||
|
||||
assert(cache_);
|
||||
|
||||
cache_.get()->Erase(key);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -36,15 +36,6 @@ class BlobFileCache {
|
||||
uint64_t blob_file_number,
|
||||
CacheHandleGuard<BlobFileReader>* blob_file_reader);
|
||||
|
||||
// Called when a blob file is obsolete to ensure it is removed from the cache
|
||||
// to avoid effectively leaking the open file and assicated memory
|
||||
void Evict(uint64_t blob_file_number);
|
||||
|
||||
// Used to identify cache entries for blob files (not normally useful)
|
||||
static const Cache::CacheItemHelper* GetHelper() {
|
||||
return CacheInterface::GetBasicHelper();
|
||||
}
|
||||
|
||||
private:
|
||||
using CacheInterface =
|
||||
BasicTypedCacheInterface<BlobFileReader, CacheEntryRole::kMisc>;
|
||||
|
||||
@@ -20,24 +20,23 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
BlobSource::BlobSource(const ImmutableOptions& immutable_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
BlobSource::BlobSource(const ImmutableOptions* immutable_options,
|
||||
const std::string& db_id,
|
||||
const std::string& db_session_id,
|
||||
BlobFileCache* blob_file_cache)
|
||||
: db_id_(db_id),
|
||||
db_session_id_(db_session_id),
|
||||
statistics_(immutable_options.statistics.get()),
|
||||
statistics_(immutable_options->statistics.get()),
|
||||
blob_file_cache_(blob_file_cache),
|
||||
blob_cache_(immutable_options.blob_cache),
|
||||
lowest_used_cache_tier_(immutable_options.lowest_used_cache_tier) {
|
||||
blob_cache_(immutable_options->blob_cache),
|
||||
lowest_used_cache_tier_(immutable_options->lowest_used_cache_tier) {
|
||||
auto bbto =
|
||||
mutable_cf_options.table_factory->GetOptions<BlockBasedTableOptions>();
|
||||
immutable_options->table_factory->GetOptions<BlockBasedTableOptions>();
|
||||
if (bbto &&
|
||||
bbto->cache_usage_options.options_overrides.at(CacheEntryRole::kBlobCache)
|
||||
.charged == CacheEntryRoleOptions::Decision::kEnabled) {
|
||||
blob_cache_ = SharedCacheInterface{std::make_shared<ChargedCache>(
|
||||
immutable_options.blob_cache, bbto->block_cache)};
|
||||
immutable_options->blob_cache, bbto->block_cache)};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,8 +81,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();
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
struct ImmutableOptions;
|
||||
struct MutableCFOptions;
|
||||
class Status;
|
||||
class FilePrefetchBuffer;
|
||||
class Slice;
|
||||
@@ -32,10 +31,7 @@ class Slice;
|
||||
// storage with minimal cost.
|
||||
class BlobSource {
|
||||
public:
|
||||
// NOTE: db_id, db_session_id, and blob_file_cache are saved by reference or
|
||||
// pointer.
|
||||
BlobSource(const ImmutableOptions& immutable_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
BlobSource(const ImmutableOptions* immutable_options,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
BlobFileCache* blob_file_cache);
|
||||
|
||||
|
||||
+14
-22
@@ -148,7 +148,6 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -194,8 +193,8 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
|
||||
backing_cache.get(), &immutable_options, &file_options,
|
||||
column_family_id, blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
@@ -465,7 +464,6 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -500,8 +498,8 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
|
||||
backing_cache.get(), &immutable_options, &file_options,
|
||||
column_family_id, nullptr /*HistogramImpl*/, nullptr /*IOTracer*/);
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
@@ -591,7 +589,6 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -647,8 +644,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
|
||||
backing_cache.get(), &immutable_options, &file_options,
|
||||
column_family_id, blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
@@ -785,7 +782,6 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -831,8 +827,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
|
||||
backing_cache.get(), &immutable_options, &file_options,
|
||||
column_family_id, blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
@@ -1109,7 +1105,6 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -1142,8 +1137,8 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
backing_cache.get(), &immutable_options, &file_options, column_family_id,
|
||||
blob_file_read_hist, nullptr /*IOTracer*/));
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
CacheHandleGuard<BlobFileReader> file_reader;
|
||||
ReadOptions read_options;
|
||||
@@ -1410,7 +1405,6 @@ TEST_F(BlobSourceCacheReservationTest, SimpleCacheReservation) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr ExpirationRange expiration_range;
|
||||
|
||||
@@ -1432,8 +1426,8 @@ TEST_F(BlobSourceCacheReservationTest, SimpleCacheReservation) {
|
||||
backing_cache.get(), &immutable_options, &file_options,
|
||||
kColumnFamilyId, blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
ConcurrentCacheReservationManager* cache_res_mgr =
|
||||
static_cast<ChargedCache*>(blob_source.GetBlobCache())
|
||||
@@ -1525,8 +1519,6 @@ TEST_F(BlobSourceCacheReservationTest, IncreaseCacheReservation) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr size_t blob_size = 24 << 10; // 24KB
|
||||
for (size_t i = 0; i < kNumBlobs; ++i) {
|
||||
blob_file_size_ -= blobs_[i].size(); // old blob size
|
||||
@@ -1554,8 +1546,8 @@ TEST_F(BlobSourceCacheReservationTest, IncreaseCacheReservation) {
|
||||
backing_cache.get(), &immutable_options, &file_options,
|
||||
kColumnFamilyId, blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
ConcurrentCacheReservationManager* cache_res_mgr =
|
||||
static_cast<ChargedCache*>(blob_source.GetBlobCache())
|
||||
|
||||
@@ -374,115 +374,6 @@ TEST_F(DBBlobBasicTest, IterateBlobsFromCachePinning) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, IterateBlobsAllowUnpreparedValue) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr size_t num_blobs = 5;
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> blobs;
|
||||
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
keys.emplace_back("key" + std::to_string(i));
|
||||
blobs.emplace_back("blob" + std::to_string(i));
|
||||
ASSERT_OK(Put(keys[i], blobs[i]));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.allow_unprepared_value = true;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_EQ(iter->key(), keys[i]);
|
||||
ASSERT_TRUE(iter->value().empty());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_TRUE(iter->PrepareValue());
|
||||
|
||||
ASSERT_EQ(iter->key(), keys[i]);
|
||||
ASSERT_EQ(iter->value(), blobs[i]);
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(i, num_blobs);
|
||||
}
|
||||
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
|
||||
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
|
||||
ASSERT_TRUE(iter->value().empty());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_TRUE(iter->PrepareValue());
|
||||
|
||||
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
|
||||
ASSERT_EQ(iter->value(), blobs[num_blobs - 1 - i]);
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(i, num_blobs);
|
||||
}
|
||||
|
||||
{
|
||||
size_t i = 1;
|
||||
|
||||
for (iter->Seek(keys[i]); iter->Valid(); iter->Next()) {
|
||||
ASSERT_EQ(iter->key(), keys[i]);
|
||||
ASSERT_TRUE(iter->value().empty());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_TRUE(iter->PrepareValue());
|
||||
|
||||
ASSERT_EQ(iter->key(), keys[i]);
|
||||
ASSERT_EQ(iter->value(), blobs[i]);
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(i, num_blobs);
|
||||
}
|
||||
|
||||
{
|
||||
size_t i = 1;
|
||||
|
||||
for (iter->SeekForPrev(keys[num_blobs - 1 - i]); iter->Valid();
|
||||
iter->Prev()) {
|
||||
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
|
||||
ASSERT_TRUE(iter->value().empty());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_TRUE(iter->PrepareValue());
|
||||
|
||||
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
|
||||
ASSERT_EQ(iter->value(), blobs[num_blobs - 1 - i]);
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(i, num_blobs);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, MultiGetBlobs) {
|
||||
constexpr size_t min_blob_size = 6;
|
||||
|
||||
@@ -1764,46 +1655,6 @@ TEST_P(DBBlobBasicIOErrorTest, CompactionFilterReadBlob_IOError) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(DBBlobBasicIOErrorTest, IterateBlobsAllowUnpreparedValue_IOError) {
|
||||
Options options;
|
||||
options.env = fault_injection_env_.get();
|
||||
options.enable_blob_files = true;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char key[] = "key";
|
||||
constexpr char blob_value[] = "blob_value";
|
||||
|
||||
ASSERT_OK(Put(key, blob_value));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
|
||||
fault_injection_env_->SetFilesystemActive(false,
|
||||
Status::IOError(sync_point_));
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.allow_unprepared_value = true;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
iter->SeekToFirst();
|
||||
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), key);
|
||||
ASSERT_TRUE(iter->value().empty());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_FALSE(iter->PrepareValue());
|
||||
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsIOError());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, WarmCacheWithBlobsDuringFlush) {
|
||||
Options options = GetDefaultOptions();
|
||||
|
||||
|
||||
+28
-21
@@ -53,7 +53,7 @@ TableBuilder* NewTableBuilder(const TableBuilderOptions& tboptions,
|
||||
assert((tboptions.column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
tboptions.column_family_name.empty());
|
||||
return tboptions.moptions.table_factory->NewTableBuilder(tboptions, file);
|
||||
return tboptions.ioptions.table_factory->NewTableBuilder(tboptions, file);
|
||||
}
|
||||
|
||||
Status BuildTable(
|
||||
@@ -64,7 +64,7 @@ Status BuildTable(
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
|
||||
std::vector<SequenceNumber> snapshots, SequenceNumber earliest_snapshot,
|
||||
std::vector<SequenceNumber> snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, SnapshotChecker* snapshot_checker,
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
@@ -145,9 +145,7 @@ Status BuildTable(
|
||||
bool use_direct_writes = file_options.use_direct_writes;
|
||||
TEST_SYNC_POINT_CALLBACK("BuildTable:create_file", &use_direct_writes);
|
||||
#endif // !NDEBUG
|
||||
FileOptions fo_copy = file_options;
|
||||
fo_copy.write_hint = write_hint;
|
||||
IOStatus io_s = NewWritableFile(fs, fname, &file, fo_copy);
|
||||
IOStatus io_s = NewWritableFile(fs, fname, &file, file_options);
|
||||
assert(s.ok());
|
||||
s = io_s;
|
||||
if (io_status->ok()) {
|
||||
@@ -165,9 +163,7 @@ Status BuildTable(
|
||||
table_file_created = true;
|
||||
FileTypeSet tmp_set = ioptions.checksum_handoff_file_types;
|
||||
file->SetIOPriority(tboptions.write_options.rate_limiter_priority);
|
||||
// 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->SetWriteLifeTimeHint(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,
|
||||
@@ -199,7 +195,7 @@ Status BuildTable(
|
||||
|
||||
const std::atomic<bool> kManualCompactionCanceledFalse{false};
|
||||
CompactionIterator c_iter(
|
||||
iter, ucmp, &merge, kMaxSequenceNumber, &snapshots, earliest_snapshot,
|
||||
iter, ucmp, &merge, kMaxSequenceNumber, &snapshots,
|
||||
earliest_write_conflict_snapshot, job_snapshot, snapshot_checker, env,
|
||||
ShouldReportDetailedTime(env, ioptions.stats),
|
||||
true /* internal key corruption is not ok */, range_del_agg.get(),
|
||||
@@ -210,6 +206,10 @@ Status BuildTable(
|
||||
/*compaction=*/nullptr, compaction_filter.get(),
|
||||
/*shutting_down=*/nullptr, db_options.info_log, full_history_ts_low);
|
||||
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
const bool logical_strip_timestamp =
|
||||
ts_sz > 0 && !ioptions.persist_user_defined_timestamps;
|
||||
|
||||
SequenceNumber smallest_preferred_seqno = kMaxSequenceNumber;
|
||||
std::string key_after_flush_buf;
|
||||
std::string value_buf;
|
||||
@@ -222,6 +222,16 @@ Status BuildTable(
|
||||
Slice key_after_flush = key_after_flush_buf;
|
||||
Slice value_after_flush = value;
|
||||
|
||||
// If user defined timestamps will be stripped from user key after flush,
|
||||
// the in memory version of the key act logically the same as one with a
|
||||
// minimum timestamp. We update the timestamp here so file boundary and
|
||||
// output validator, block builder all see the effect of the stripping.
|
||||
if (logical_strip_timestamp) {
|
||||
key_after_flush_buf.clear();
|
||||
ReplaceInternalKeyWithMinTimestamp(&key_after_flush_buf, key, ts_sz);
|
||||
key_after_flush = key_after_flush_buf;
|
||||
}
|
||||
|
||||
if (ikey.type == kTypeValuePreferredSeqno) {
|
||||
auto [unpacked_value, unix_write_time] =
|
||||
ParsePackedValueWithWriteTime(value);
|
||||
@@ -281,7 +291,11 @@ Status BuildTable(
|
||||
Slice last_tombstone_start_user_key{};
|
||||
for (range_del_it->SeekToFirst(); range_del_it->Valid();
|
||||
range_del_it->Next()) {
|
||||
auto tombstone = range_del_it->Tombstone();
|
||||
// When user timestamp should not be persisted, we logically strip a
|
||||
// range tombstone's start and end key's timestamp (replace it with min
|
||||
// timestamp) before passing them along to table builder and to update
|
||||
// file boundaries.
|
||||
auto tombstone = range_del_it->Tombstone(logical_strip_timestamp);
|
||||
std::pair<InternalKey, Slice> kv = tombstone.Serialize();
|
||||
builder->Add(kv.first.Encode(), kv.second);
|
||||
InternalKey tombstone_end = tombstone.SerializeEndKey();
|
||||
@@ -424,7 +438,8 @@ Status BuildTable(
|
||||
// the goal is to cache it here for further user reads.
|
||||
std::unique_ptr<InternalIterator> it(table_cache->NewIterator(
|
||||
tboptions.read_options, file_options, tboptions.internal_comparator,
|
||||
*meta, nullptr /* range_del_agg */, mutable_cf_options, nullptr,
|
||||
*meta, nullptr /* range_del_agg */,
|
||||
mutable_cf_options.prefix_extractor, nullptr,
|
||||
(internal_stats == nullptr) ? nullptr
|
||||
: internal_stats->GetFileReadHist(0),
|
||||
TableReaderCaller::kFlush, /*arena=*/nullptr,
|
||||
@@ -432,7 +447,8 @@ Status BuildTable(
|
||||
MaxFileSizeForL0MetaPin(mutable_cf_options),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key*/ nullptr,
|
||||
/*allow_unprepared_value*/ false));
|
||||
/*allow_unprepared_value*/ false,
|
||||
mutable_cf_options.block_protection_bytes_per_key));
|
||||
s = it->status();
|
||||
if (s.ok() && paranoid_file_checks) {
|
||||
OutputValidator file_validator(tboptions.internal_comparator,
|
||||
@@ -464,18 +480,9 @@ Status BuildTable(
|
||||
Status prepare =
|
||||
WritableFileWriter::PrepareIOOptions(tboptions.write_options, opts);
|
||||
if (prepare.ok()) {
|
||||
// FIXME: track file for "slow" deletion, e.g. into the
|
||||
// VersionSet::obsolete_files_ pipeline
|
||||
Status ignored = fs->DeleteFile(fname, opts, dbg);
|
||||
ignored.PermitUncheckedError();
|
||||
}
|
||||
// Ensure we don't leak table cache entries when throwing away output
|
||||
// files. (The usual logic in PurgeObsoleteFiles is not applicable because
|
||||
// this function deletes the obsolete file itself, while they should
|
||||
// probably go into the VersionSet::obsolete_files_ pipeline.)
|
||||
TableCache::ReleaseObsolete(table_cache->get_cache().get(),
|
||||
meta->fd.GetNumber(), nullptr /*handle*/,
|
||||
mutable_cf_options.uncache_aggressiveness);
|
||||
}
|
||||
|
||||
assert(blob_file_additions || blob_file_paths.empty());
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ Status BuildTable(
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
|
||||
std::vector<SequenceNumber> snapshots, SequenceNumber earliest_snapshot,
|
||||
std::vector<SequenceNumber> snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, SnapshotChecker* snapshot_checker,
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
|
||||
@@ -231,7 +231,6 @@ struct rocksdb_livefiles_t {
|
||||
};
|
||||
struct rocksdb_column_family_handle_t {
|
||||
ColumnFamilyHandle* rep;
|
||||
bool immortal; /* only true for default cf */
|
||||
};
|
||||
struct rocksdb_column_family_metadata_t {
|
||||
ColumnFamilyMetaData rep;
|
||||
@@ -532,42 +531,6 @@ struct rocksdb_universal_compaction_options_t {
|
||||
ROCKSDB_NAMESPACE::CompactionOptionsUniversal* rep;
|
||||
};
|
||||
|
||||
struct rocksdb_callback_logger_t : public Logger {
|
||||
static const ssize_t STACK_BUFSZ = 512;
|
||||
rocksdb_callback_logger_t(InfoLogLevel log_level,
|
||||
void (*logv_cb)(void*, unsigned, char*, size_t),
|
||||
void* priv)
|
||||
: Logger(log_level), logv_cb_(logv_cb), priv_(priv) {}
|
||||
|
||||
using Logger::Logv;
|
||||
void Logv(const InfoLogLevel level, const char* fmt, va_list ap0) override {
|
||||
char stack_buf[STACK_BUFSZ];
|
||||
char* alloc_buf = nullptr;
|
||||
char* buf = stack_buf;
|
||||
int len = 0;
|
||||
va_list ap1;
|
||||
if (!logv_cb_) return;
|
||||
va_copy(ap1, ap0);
|
||||
len = vsnprintf(buf, STACK_BUFSZ, fmt, ap0);
|
||||
if (len <= 0)
|
||||
goto cleanup;
|
||||
else if (len >= STACK_BUFSZ) {
|
||||
buf = alloc_buf = reinterpret_cast<char*>(malloc(len + 1));
|
||||
if (!buf) goto cleanup;
|
||||
len = vsnprintf(buf, len + 1, fmt, ap1);
|
||||
if (len <= 0) goto cleanup;
|
||||
}
|
||||
logv_cb_(priv_, unsigned(level), buf, size_t(len));
|
||||
cleanup:
|
||||
va_end(ap1);
|
||||
free(alloc_buf);
|
||||
}
|
||||
|
||||
private:
|
||||
void (*logv_cb_)(void*, unsigned, char*, size_t) = nullptr;
|
||||
void* priv_ = nullptr;
|
||||
};
|
||||
|
||||
static bool SaveError(char** errptr, const Status& s) {
|
||||
assert(errptr != nullptr);
|
||||
if (s.ok()) {
|
||||
@@ -583,7 +546,6 @@ static bool SaveError(char** errptr, const Status& s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copies str to a new malloc()-ed buffer. The buffer is not NUL terminated.
|
||||
static char* CopyString(const std::string& str) {
|
||||
char* result = reinterpret_cast<char*>(malloc(sizeof(char) * str.size()));
|
||||
memcpy(result, str.data(), sizeof(char) * str.size());
|
||||
@@ -944,7 +906,6 @@ rocksdb_t* rocksdb_open_and_trim_history(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -975,7 +936,6 @@ rocksdb_t* rocksdb_open_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -1011,7 +971,6 @@ rocksdb_t* rocksdb_open_column_families_with_ttl(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -1045,7 +1004,6 @@ rocksdb_t* rocksdb_open_for_read_only_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -1077,7 +1035,6 @@ rocksdb_t* rocksdb_open_as_secondary_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -1115,7 +1072,6 @@ rocksdb_column_family_handle_t* rocksdb_create_column_family(
|
||||
SaveError(errptr, db->rep->CreateColumnFamily(
|
||||
ColumnFamilyOptions(column_family_options->rep),
|
||||
std::string(column_family_name), &(handle->rep)));
|
||||
handle->immortal = false;
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -1139,7 +1095,6 @@ rocksdb_column_family_handle_t** rocksdb_create_column_families(
|
||||
for (size_t i = 0; i != handles.size(); ++i) {
|
||||
c_handles[i] = new rocksdb_column_family_handle_t;
|
||||
c_handles[i]->rep = handles[i];
|
||||
c_handles[i]->immortal = false;
|
||||
}
|
||||
|
||||
return c_handles;
|
||||
@@ -1159,7 +1114,6 @@ rocksdb_column_family_handle_t* rocksdb_create_column_family_with_ttl(
|
||||
SaveError(errptr, db_with_ttl->CreateColumnFamilyWithTtl(
|
||||
ColumnFamilyOptions(column_family_options->rep),
|
||||
std::string(column_family_name), &(handle->rep), ttl));
|
||||
handle->immortal = false;
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -1181,19 +1135,9 @@ char* rocksdb_column_family_handle_get_name(
|
||||
return CopyString(name);
|
||||
}
|
||||
|
||||
rocksdb_column_family_handle_t* rocksdb_get_default_column_family_handle(
|
||||
rocksdb_t* db) {
|
||||
rocksdb_column_family_handle_t* handle = new rocksdb_column_family_handle_t;
|
||||
handle->rep = db->rep->DefaultColumnFamily();
|
||||
handle->immortal = true;
|
||||
return handle;
|
||||
}
|
||||
|
||||
void rocksdb_column_family_handle_destroy(
|
||||
rocksdb_column_family_handle_t* handle) {
|
||||
if (!handle->immortal) {
|
||||
delete handle->rep;
|
||||
}
|
||||
delete handle->rep;
|
||||
delete handle;
|
||||
}
|
||||
|
||||
@@ -1429,18 +1373,6 @@ char* rocksdb_get_cf_with_ts(rocksdb_t* db,
|
||||
return result;
|
||||
}
|
||||
|
||||
char* rocksdb_get_db_identity(rocksdb_t* db, size_t* id_len) {
|
||||
std::string identity_tmp;
|
||||
Status s = db->rep->GetDbIdentity(identity_tmp);
|
||||
if (!s.ok()) {
|
||||
*id_len = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*id_len = identity_tmp.size();
|
||||
return CopyString(identity_tmp);
|
||||
}
|
||||
|
||||
void rocksdb_multi_get(rocksdb_t* db, const rocksdb_readoptions_t* options,
|
||||
size_t num_keys, const char* const* keys_list,
|
||||
const size_t* keys_list_sizes, char** values_list,
|
||||
@@ -1838,24 +1770,8 @@ void rocksdb_approximate_sizes_cf(
|
||||
delete[] ranges;
|
||||
}
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_approximate_sizes_cf_with_flags(
|
||||
rocksdb_t* db, rocksdb_column_family_handle_t* column_family,
|
||||
int num_ranges, const char* const* range_start_key,
|
||||
const size_t* range_start_key_len, const char* const* range_limit_key,
|
||||
const size_t* range_limit_key_len, uint8_t include_flags, uint64_t* sizes,
|
||||
char** errptr) {
|
||||
Range* ranges = new Range[num_ranges];
|
||||
for (int i = 0; i < num_ranges; i++) {
|
||||
ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]);
|
||||
ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]);
|
||||
}
|
||||
Status s = db->rep->GetApproximateSizes(
|
||||
column_family->rep, ranges, num_ranges, sizes,
|
||||
static_cast<DB::SizeApproximationFlags>(include_flags));
|
||||
if (!s.ok()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
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) {
|
||||
@@ -2031,10 +1947,6 @@ void rocksdb_iter_get_error(const rocksdb_iterator_t* iter, char** errptr) {
|
||||
SaveError(errptr, iter->rep->status());
|
||||
}
|
||||
|
||||
void rocksdb_iter_refresh(const rocksdb_iterator_t* iter, char** errptr) {
|
||||
SaveError(errptr, iter->rep->Refresh());
|
||||
}
|
||||
|
||||
rocksdb_writebatch_t* rocksdb_writebatch_create() {
|
||||
return new rocksdb_writebatch_t;
|
||||
}
|
||||
@@ -2046,15 +1958,6 @@ rocksdb_writebatch_t* rocksdb_writebatch_create_from(const char* rep,
|
||||
return b;
|
||||
}
|
||||
|
||||
rocksdb_writebatch_t* rocksdb_writebatch_create_with_params(
|
||||
size_t reserved_bytes, size_t max_bytes, size_t protection_bytes_per_key,
|
||||
size_t default_cf_ts_sz) {
|
||||
rocksdb_writebatch_t* b = new rocksdb_writebatch_t;
|
||||
b->rep = WriteBatch(reserved_bytes, max_bytes, protection_bytes_per_key,
|
||||
default_cf_ts_sz);
|
||||
return b;
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_destroy(rocksdb_writebatch_t* b) { delete b; }
|
||||
|
||||
void rocksdb_writebatch_clear(rocksdb_writebatch_t* b) { b->rep.Clear(); }
|
||||
@@ -2282,32 +2185,6 @@ class H : public WriteBatch::Handler {
|
||||
}
|
||||
};
|
||||
|
||||
class HCF : public WriteBatch::Handler {
|
||||
public:
|
||||
void* state_;
|
||||
void (*put_cf_)(void*, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen);
|
||||
void (*deleted_cf_)(void*, uint32_t cfid, const char* k, size_t klen);
|
||||
void (*merge_cf_)(void*, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen);
|
||||
Status PutCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) override {
|
||||
(*put_cf_)(state_, column_family_id, key.data(), key.size(), value.data(),
|
||||
value.size());
|
||||
return Status::OK();
|
||||
}
|
||||
Status DeleteCF(uint32_t column_family_id, const Slice& key) override {
|
||||
(*deleted_cf_)(state_, column_family_id, key.data(), key.size());
|
||||
return Status::OK();
|
||||
}
|
||||
Status MergeCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) override {
|
||||
(*merge_cf_)(state_, column_family_id, key.data(), key.size(), value.data(),
|
||||
value.size());
|
||||
return Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
void rocksdb_writebatch_iterate(rocksdb_writebatch_t* b, void* state,
|
||||
void (*put)(void*, const char* k, size_t klen,
|
||||
const char* v, size_t vlen),
|
||||
@@ -2320,21 +2197,6 @@ void rocksdb_writebatch_iterate(rocksdb_writebatch_t* b, void* state,
|
||||
b->rep.Iterate(&handler);
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_iterate_cf(
|
||||
rocksdb_writebatch_t* b, void* state,
|
||||
void (*put_cf)(void*, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen),
|
||||
void (*deleted_cf)(void*, uint32_t cfid, const char* k, size_t klen),
|
||||
void (*merge_cf)(void*, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen)) {
|
||||
HCF handler;
|
||||
handler.state_ = state;
|
||||
handler.put_cf_ = put_cf;
|
||||
handler.deleted_cf_ = deleted_cf;
|
||||
handler.merge_cf_ = merge_cf;
|
||||
b->rep.Iterate(&handler);
|
||||
}
|
||||
|
||||
const char* rocksdb_writebatch_data(rocksdb_writebatch_t* b, size_t* size) {
|
||||
*size = b->rep.GetDataSize();
|
||||
return b->rep.Data().c_str();
|
||||
@@ -2361,35 +2223,6 @@ rocksdb_writebatch_wi_t* rocksdb_writebatch_wi_create(
|
||||
return b;
|
||||
}
|
||||
|
||||
rocksdb_writebatch_wi_t* rocksdb_writebatch_wi_create_with_params(
|
||||
rocksdb_comparator_t* backup_index_comparator, size_t reserved_bytes,
|
||||
unsigned char overwrite_key, size_t max_bytes,
|
||||
size_t protection_bytes_per_key) {
|
||||
rocksdb_writebatch_wi_t* b = new rocksdb_writebatch_wi_t;
|
||||
b->rep = new WriteBatchWithIndex(backup_index_comparator, reserved_bytes,
|
||||
overwrite_key, max_bytes,
|
||||
protection_bytes_per_key);
|
||||
return b;
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_update_timestamps(
|
||||
rocksdb_writebatch_t* wb, const char* ts, size_t tslen, void* state,
|
||||
size_t (*get_ts_size)(void*, uint32_t), char** errptr) {
|
||||
SaveError(errptr, wb->rep.UpdateTimestamps(
|
||||
Slice(ts, tslen), [&get_ts_size, &state](uint32_t cf) {
|
||||
return (*get_ts_size)(state, cf);
|
||||
}));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_update_timestamps(
|
||||
rocksdb_writebatch_wi_t* wb, const char* ts, size_t tslen, void* state,
|
||||
size_t (*get_ts_size)(void*, uint32_t), char** errptr) {
|
||||
SaveError(errptr, wb->rep->GetWriteBatch()->UpdateTimestamps(
|
||||
Slice(ts, tslen), [&get_ts_size, &state](uint32_t cf) {
|
||||
return (*get_ts_size)(state, cf);
|
||||
}));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_destroy(rocksdb_writebatch_wi_t* b) {
|
||||
if (b->rep) {
|
||||
delete b->rep;
|
||||
@@ -2912,24 +2745,6 @@ void rocksdb_options_set_block_based_table_factory(
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_top_level_index_pinning_tier(
|
||||
rocksdb_block_based_table_options_t* options, int v) {
|
||||
options->rep.metadata_cache_options.top_level_index_pinning =
|
||||
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_partition_pinning_tier(
|
||||
rocksdb_block_based_table_options_t* options, int v) {
|
||||
options->rep.metadata_cache_options.partition_pinning =
|
||||
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_unpartitioned_pinning_tier(
|
||||
rocksdb_block_based_table_options_t* options, int v) {
|
||||
options->rep.metadata_cache_options.unpartitioned_pinning =
|
||||
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
|
||||
}
|
||||
|
||||
rocksdb_cuckoo_table_options_t* rocksdb_cuckoo_options_create() {
|
||||
return new rocksdb_cuckoo_table_options_t;
|
||||
}
|
||||
@@ -3130,15 +2945,6 @@ rocksdb_logger_t* rocksdb_logger_create_stderr_logger(int log_level,
|
||||
return logger;
|
||||
}
|
||||
|
||||
rocksdb_logger_t* rocksdb_logger_create_callback_logger(
|
||||
int log_level, void (*callback)(void*, unsigned, char*, size_t),
|
||||
void* priv) {
|
||||
rocksdb_logger_t* logger = new rocksdb_logger_t;
|
||||
logger->rep = std::make_shared<rocksdb_callback_logger_t>(
|
||||
static_cast<InfoLogLevel>(log_level), callback, priv);
|
||||
return logger;
|
||||
}
|
||||
|
||||
void rocksdb_logger_destroy(rocksdb_logger_t* logger) { delete logger; }
|
||||
|
||||
void rocksdb_options_set_env(rocksdb_options_t* opt, rocksdb_env_t* env) {
|
||||
@@ -4061,36 +3867,6 @@ void rocksdb_options_set_plain_table_factory(
|
||||
opt->rep.table_factory.reset(factory);
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_write_dbid_to_manifest(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.write_dbid_to_manifest;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_write_dbid_to_manifest(
|
||||
rocksdb_options_t* opt, unsigned char write_dbid_to_manifest) {
|
||||
opt->rep.write_dbid_to_manifest = write_dbid_to_manifest;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_write_identity_file(rocksdb_options_t* opt) {
|
||||
return opt->rep.write_identity_file;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_write_identity_file(
|
||||
rocksdb_options_t* opt, unsigned char write_identity_file) {
|
||||
opt->rep.write_identity_file = write_identity_file;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_track_and_verify_wals_in_manifest(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.track_and_verify_wals_in_manifest;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_track_and_verify_wals_in_manifest(
|
||||
rocksdb_options_t* opt, unsigned char track_and_verify_wals_in_manifest) {
|
||||
opt->rep.track_and_verify_wals_in_manifest =
|
||||
track_and_verify_wals_in_manifest;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_successive_merges(rocksdb_options_t* opt,
|
||||
size_t v) {
|
||||
opt->rep.max_successive_merges = v;
|
||||
@@ -4154,14 +3930,6 @@ void rocksdb_options_set_fifo_compaction_options(
|
||||
opt->rep.compaction_options_fifo = fifo->rep;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compaction_pri(rocksdb_options_t* opt, int pri) {
|
||||
opt->rep.compaction_pri = static_cast<ROCKSDB_NAMESPACE::CompactionPri>(pri);
|
||||
}
|
||||
|
||||
int rocksdb_options_get_compaction_pri(rocksdb_options_t* opt) {
|
||||
return opt->rep.compaction_pri;
|
||||
}
|
||||
|
||||
char* rocksdb_options_statistics_get_string(rocksdb_options_t* opt) {
|
||||
ROCKSDB_NAMESPACE::Statistics* statistics = opt->rep.statistics.get();
|
||||
if (statistics) {
|
||||
@@ -4470,8 +4238,13 @@ void rocksdb_perfcontext_destroy(rocksdb_perfcontext_t* context) {
|
||||
|
||||
/*
|
||||
TODO:
|
||||
DB::OpenForReadOnly
|
||||
DB::KeyMayExist
|
||||
DB::GetOptions
|
||||
DB::GetSortedWalFiles
|
||||
DB::GetLatestSequenceNumber
|
||||
DB::GetUpdatesSince
|
||||
DB::GetDbIdentity
|
||||
DB::RunManualCompaction
|
||||
custom cache
|
||||
table_properties_collectors
|
||||
@@ -4996,35 +4769,6 @@ 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) {
|
||||
@@ -5977,7 +5721,6 @@ rocksdb_column_family_handle_t* rocksdb_transactiondb_create_column_family(
|
||||
SaveError(errptr, txn_db->rep->CreateColumnFamily(
|
||||
ColumnFamilyOptions(column_family_options->rep),
|
||||
std::string(column_family_name), &(handle->rep)));
|
||||
handle->immortal = false;
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -6020,7 +5763,6 @@ rocksdb_transactiondb_t* rocksdb_transactiondb_open_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_transactiondb_t* result = new rocksdb_transactiondb_t;
|
||||
@@ -6815,7 +6557,6 @@ rocksdb_optimistictransactiondb_open_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_optimistictransactiondb_t* result =
|
||||
|
||||
+2
-220
@@ -199,79 +199,6 @@ static void CheckDel(void* ptr, const char* k, size_t klen) {
|
||||
(*state)++;
|
||||
}
|
||||
|
||||
// Callback from rocksdb_writebatch_iterate_cf()
|
||||
static void CheckPutCF(void* ptr, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen) {
|
||||
int* state = (int*)ptr;
|
||||
switch (*state) {
|
||||
case 0:
|
||||
CheckEqual("bar", k, klen);
|
||||
CheckEqual("b", v, vlen);
|
||||
CheckCondition(cfid == 1);
|
||||
break;
|
||||
case 1:
|
||||
CheckEqual("box", k, klen);
|
||||
CheckEqual("c", v, vlen);
|
||||
CheckCondition(cfid == 1);
|
||||
break;
|
||||
case 4:
|
||||
CheckEqual("foo", k, klen);
|
||||
CheckEqual("f", v, vlen);
|
||||
CheckCondition(cfid == 0);
|
||||
break;
|
||||
case 6:
|
||||
CheckEqual("baz", k, klen);
|
||||
CheckEqual("a", v, vlen);
|
||||
CheckCondition(cfid == 0);
|
||||
break;
|
||||
default:
|
||||
CheckCondition(false);
|
||||
break;
|
||||
}
|
||||
(*state)++;
|
||||
}
|
||||
|
||||
// Callback from rocksdb_writebatch_iterate_cf()
|
||||
static void CheckDelCF(void* ptr, uint32_t cfid, const char* k, size_t klen) {
|
||||
int* state = (int*)ptr;
|
||||
switch (*state) {
|
||||
case 2:
|
||||
CheckEqual("bar", k, klen);
|
||||
CheckCondition(cfid == 1);
|
||||
break;
|
||||
case 5:
|
||||
CheckEqual("foo", k, klen);
|
||||
CheckCondition(cfid == 0);
|
||||
break;
|
||||
default:
|
||||
CheckCondition(false);
|
||||
break;
|
||||
}
|
||||
(*state)++;
|
||||
}
|
||||
|
||||
// Callback from rocksdb_writebatch_iterate_cf()
|
||||
static void CheckMergeCF(void* ptr, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen) {
|
||||
int* state = (int*)ptr;
|
||||
switch (*state) {
|
||||
case 3:
|
||||
CheckEqual("box", k, klen);
|
||||
CheckEqual("cc", v, vlen);
|
||||
CheckCondition(cfid == 1);
|
||||
break;
|
||||
case 7:
|
||||
CheckEqual("baz", k, klen);
|
||||
CheckEqual("aa", v, vlen);
|
||||
CheckCondition(cfid == 0);
|
||||
break;
|
||||
default:
|
||||
CheckCondition(false);
|
||||
break;
|
||||
}
|
||||
(*state)++;
|
||||
}
|
||||
|
||||
static void CmpDestroy(void* arg) { (void)arg; }
|
||||
|
||||
static int CmpCompare(void* arg, const char* a, size_t alen, const char* b,
|
||||
@@ -772,17 +699,11 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_write_buffer_size(options, 100000);
|
||||
rocksdb_options_set_paranoid_checks(options, 1);
|
||||
rocksdb_options_set_max_open_files(options, 10);
|
||||
/* Compatibility with how test was written */
|
||||
rocksdb_options_set_write_dbid_to_manifest(options, 0);
|
||||
|
||||
table_options = rocksdb_block_based_options_create();
|
||||
rocksdb_block_based_options_set_block_cache(table_options, cache);
|
||||
rocksdb_block_based_options_set_data_block_index_type(table_options, 1);
|
||||
rocksdb_block_based_options_set_data_block_hash_ratio(table_options, 0.75);
|
||||
rocksdb_block_based_options_set_top_level_index_pinning_tier(table_options,
|
||||
1);
|
||||
rocksdb_block_based_options_set_partition_pinning_tier(table_options, 2);
|
||||
rocksdb_block_based_options_set_unpartitioned_pinning_tier(table_options, 3);
|
||||
rocksdb_options_set_block_based_table_factory(options, table_options);
|
||||
|
||||
rocksdb_options_set_compression(options, rocksdb_no_compression);
|
||||
@@ -873,11 +794,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_delete(db, woptions, "foo", 3, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// get the identity before the backup
|
||||
size_t before_db_id_len = 0;
|
||||
char* before_db_id = rocksdb_get_db_identity(db, &before_db_id_len);
|
||||
CheckCondition(before_db_id_len == 36);
|
||||
|
||||
rocksdb_close(db);
|
||||
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
@@ -898,14 +814,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
|
||||
// the db_identity after the backup is different
|
||||
size_t after_db_id_len = 0;
|
||||
char* after_db_id = rocksdb_get_db_identity(db, &after_db_id_len);
|
||||
CheckCondition(after_db_id_len == 36);
|
||||
CheckCondition(memcmp(after_db_id, before_db_id, after_db_id_len) != 0);
|
||||
Free(&before_db_id);
|
||||
Free(&after_db_id);
|
||||
|
||||
rocksdb_backup_engine_close(be);
|
||||
}
|
||||
|
||||
@@ -921,14 +829,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_checkpoint_create(checkpoint, dbcheckpointname, 0, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_object_destroy(checkpoint);
|
||||
checkpoint = NULL;
|
||||
|
||||
// get the identity before the checkpoint
|
||||
size_t before_db_id_len = 0;
|
||||
char* before_db_id = rocksdb_get_db_identity(db, &before_db_id_len);
|
||||
CheckCondition(before_db_id_len == 36);
|
||||
|
||||
// start a new database from the checkpoint
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
@@ -937,13 +837,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
|
||||
// the db_identity after the checkpoint is different
|
||||
size_t after_db_id_len = 0;
|
||||
char* after_db_id = rocksdb_get_db_identity(db, &after_db_id_len);
|
||||
CheckCondition(after_db_id_len == 36);
|
||||
CheckCondition(memcmp(after_db_id, before_db_id, after_db_id_len) != 0);
|
||||
Free(&before_db_id);
|
||||
Free(&after_db_id);
|
||||
rocksdb_checkpoint_object_destroy(checkpoint);
|
||||
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbcheckpointname, &err);
|
||||
@@ -954,82 +848,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_error_if_exists(options, 1);
|
||||
}
|
||||
|
||||
StartPhase("checkpoint_db_id_in_manifest");
|
||||
{
|
||||
// create new DB with set_write_dbid_to_manifest=true
|
||||
// db_identity is now the same across checkpoints
|
||||
rocksdb_close(db);
|
||||
db = NULL;
|
||||
|
||||
rocksdb_options_t* options_dbid_in_manifest = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(options_dbid_in_manifest, 1);
|
||||
|
||||
rocksdb_options_set_write_dbid_to_manifest(options_dbid_in_manifest, false);
|
||||
unsigned char write_to_manifest =
|
||||
rocksdb_options_get_write_dbid_to_manifest(options_dbid_in_manifest);
|
||||
CheckCondition(!write_to_manifest);
|
||||
rocksdb_options_set_write_dbid_to_manifest(options_dbid_in_manifest, true);
|
||||
write_to_manifest =
|
||||
rocksdb_options_get_write_dbid_to_manifest(options_dbid_in_manifest);
|
||||
CheckCondition(write_to_manifest);
|
||||
|
||||
rocksdb_options_set_write_identity_file(options_dbid_in_manifest, true);
|
||||
unsigned char write_identity_file =
|
||||
rocksdb_options_get_write_identity_file(options_dbid_in_manifest);
|
||||
CheckCondition(write_identity_file);
|
||||
rocksdb_options_set_write_identity_file(options_dbid_in_manifest, false);
|
||||
write_identity_file =
|
||||
rocksdb_options_get_write_identity_file(options_dbid_in_manifest);
|
||||
CheckCondition(!write_identity_file);
|
||||
|
||||
db = rocksdb_open(options_dbid_in_manifest, dbbackupname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_t* checkpoint =
|
||||
rocksdb_checkpoint_object_create(db, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_create(checkpoint, dbcheckpointname, 0, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_object_destroy(checkpoint);
|
||||
checkpoint = NULL;
|
||||
|
||||
// get the identity before the backup
|
||||
size_t before_db_id_len = 0;
|
||||
char* before_db_id = rocksdb_get_db_identity(db, &before_db_id_len);
|
||||
CheckCondition(before_db_id_len == 36);
|
||||
|
||||
// open the checkpoint
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options_dbid_in_manifest, dbbackupname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_set_error_if_exists(options_dbid_in_manifest, 0);
|
||||
db = rocksdb_open(options_dbid_in_manifest, dbcheckpointname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// the db_identity after the checkpoint is the same
|
||||
size_t after_db_id_len = 0;
|
||||
char* after_db_id = rocksdb_get_db_identity(db, &after_db_id_len);
|
||||
CheckCondition(after_db_id_len == 36);
|
||||
CheckCondition(memcmp(after_db_id, before_db_id, after_db_id_len) == 0);
|
||||
Free(&before_db_id);
|
||||
Free(&after_db_id);
|
||||
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options_dbid_in_manifest, dbcheckpointname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_options_destroy(options_dbid_in_manifest);
|
||||
options_dbid_in_manifest = NULL;
|
||||
|
||||
// re-open the default database
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_options_set_error_if_exists(options, 1);
|
||||
}
|
||||
|
||||
StartPhase("compactall");
|
||||
rocksdb_compact_range(db, NULL, 0, NULL, 0);
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
@@ -1772,21 +1590,6 @@ int main(int argc, char** argv) {
|
||||
CheckPinGetCF(db, roptions, handles[1], "bar", NULL);
|
||||
CheckPinGetCF(db, roptions, handles[1], "box", "c");
|
||||
CheckPinGetCF(db, roptions, handles[1], "buff", "rocksdb");
|
||||
rocksdb_writebatch_clear(wb);
|
||||
// Test WriteBatch iteration with Column Family
|
||||
int pos = 0;
|
||||
rocksdb_writebatch_put_cf(wb, handles[1], "bar", 3, "b", 1);
|
||||
rocksdb_writebatch_put_cf(wb, handles[1], "box", 3, "c", 1);
|
||||
rocksdb_writebatch_delete_cf(wb, handles[1], "bar", 3);
|
||||
rocksdb_writebatch_merge_cf(wb, handles[1], "box", 3, "cc", 2);
|
||||
rocksdb_writebatch_put(wb, "foo", 3, "f", 1);
|
||||
rocksdb_writebatch_delete(wb, "foo", 3);
|
||||
rocksdb_writebatch_put(wb, "baz", 3, "a", 1);
|
||||
rocksdb_writebatch_merge(wb, "baz", 3, "aa", 2);
|
||||
rocksdb_writebatch_iterate_cf(wb, &pos, CheckPutCF, CheckDelCF,
|
||||
CheckMergeCF);
|
||||
CheckCondition(pos == 8);
|
||||
rocksdb_writebatch_clear(wb);
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
|
||||
rocksdb_flush_wal(db, 1, &err);
|
||||
@@ -2238,6 +2041,7 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_advise_random_on_open(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_advise_random_on_open(o));
|
||||
|
||||
|
||||
rocksdb_options_set_use_adaptive_mutex(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_use_adaptive_mutex(o));
|
||||
|
||||
@@ -2308,9 +2112,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_compaction_style(o, 2);
|
||||
CheckCondition(2 == rocksdb_options_get_compaction_style(o));
|
||||
|
||||
rocksdb_options_set_compaction_pri(o, 4);
|
||||
CheckCondition(4 == rocksdb_options_get_compaction_pri(o));
|
||||
|
||||
rocksdb_options_set_atomic_flush(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_atomic_flush(o));
|
||||
|
||||
@@ -2333,12 +2134,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(rocksdb_statistics_level_all ==
|
||||
rocksdb_options_get_statistics_level(o));
|
||||
|
||||
CheckCondition(0 ==
|
||||
rocksdb_options_get_track_and_verify_wals_in_manifest(o));
|
||||
rocksdb_options_set_track_and_verify_wals_in_manifest(o, 42);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_options_get_track_and_verify_wals_in_manifest(o));
|
||||
|
||||
/* Blob Options */
|
||||
rocksdb_options_set_enable_blob_files(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_enable_blob_files(o));
|
||||
@@ -2821,10 +2616,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(1 == rocksdb_options_get_compaction_style(copy));
|
||||
CheckCondition(2 == rocksdb_options_get_compaction_style(o));
|
||||
|
||||
rocksdb_options_set_compaction_pri(copy, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_compaction_pri(copy));
|
||||
CheckCondition(4 == rocksdb_options_get_compaction_pri(o));
|
||||
|
||||
rocksdb_options_set_atomic_flush(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_atomic_flush(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_atomic_flush(o));
|
||||
@@ -2938,15 +2729,6 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
+10
-35
@@ -9,14 +9,17 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// EXPERIMENTAL
|
||||
class CoalescingIterator : public Iterator {
|
||||
public:
|
||||
CoalescingIterator(
|
||||
const ReadOptions& read_options, const Comparator* comparator,
|
||||
std::vector<std::pair<ColumnFamilyHandle*, std::unique_ptr<Iterator>>>&&
|
||||
cfh_iter_pairs)
|
||||
: impl_(read_options, comparator, std::move(cfh_iter_pairs),
|
||||
ResetFunc(this), PopulateFunc(this)) {}
|
||||
CoalescingIterator(const Comparator* comparator,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
const std::vector<Iterator*>& child_iterators)
|
||||
: impl_(
|
||||
comparator, column_families, child_iterators, [this]() { Reset(); },
|
||||
[this](const autovector<MultiCfIteratorInfo>& items) {
|
||||
Coalesce(items);
|
||||
}) {}
|
||||
~CoalescingIterator() override {}
|
||||
|
||||
// No copy allowed
|
||||
@@ -47,36 +50,8 @@ class CoalescingIterator : public Iterator {
|
||||
wide_columns_.clear();
|
||||
}
|
||||
|
||||
bool PrepareValue() override { return impl_.PrepareValue(); }
|
||||
|
||||
private:
|
||||
class ResetFunc {
|
||||
public:
|
||||
explicit ResetFunc(CoalescingIterator* iter) : iter_(iter) {}
|
||||
|
||||
void operator()() const {
|
||||
assert(iter_);
|
||||
iter_->Reset();
|
||||
}
|
||||
|
||||
private:
|
||||
CoalescingIterator* iter_;
|
||||
};
|
||||
|
||||
class PopulateFunc {
|
||||
public:
|
||||
explicit PopulateFunc(CoalescingIterator* iter) : iter_(iter) {}
|
||||
|
||||
void operator()(const autovector<MultiCfIteratorInfo>& items) const {
|
||||
assert(iter_);
|
||||
iter_->Coalesce(items);
|
||||
}
|
||||
|
||||
private:
|
||||
CoalescingIterator* iter_;
|
||||
};
|
||||
|
||||
MultiCfIteratorImpl<ResetFunc, PopulateFunc> impl_;
|
||||
MultiCfIteratorImpl impl_;
|
||||
Slice value_;
|
||||
WideColumns wide_columns_;
|
||||
|
||||
|
||||
+64
-112
@@ -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,8 +168,7 @@ Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options) {
|
||||
}
|
||||
if (!cf_options.memtable_factory->IsInsertConcurrentlySupported()) {
|
||||
return Status::InvalidArgument(
|
||||
"Memtable doesn't allow concurrent writes "
|
||||
"(allow_concurrent_memtable_write)");
|
||||
"Memtable doesn't concurrent writes (allow_concurrent_memtable_write)");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -200,9 +199,8 @@ const uint64_t kDefaultTtl = 0xfffffffffffffffe;
|
||||
const uint64_t kDefaultPeriodicCompSecs = 0xfffffffffffffffe;
|
||||
} // anonymous namespace
|
||||
|
||||
ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
|
||||
bool read_only,
|
||||
const ColumnFamilyOptions& src) {
|
||||
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src) {
|
||||
ColumnFamilyOptions result = src;
|
||||
size_t clamp_max = std::conditional<
|
||||
sizeof(size_t) == 4, std::integral_constant<size_t, 0xffffffff>,
|
||||
@@ -241,10 +239,6 @@ ColumnFamilyOptions SanitizeCfOptions(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;
|
||||
@@ -441,18 +435,6 @@ ColumnFamilyOptions SanitizeCfOptions(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;
|
||||
}
|
||||
|
||||
@@ -484,7 +466,7 @@ void SuperVersion::Cleanup() {
|
||||
// decrement reference to the immutable MemtableList
|
||||
// this SV object was pointing to.
|
||||
imm->Unref(&to_delete);
|
||||
ReadOnlyMemTable* m = mem->Unref();
|
||||
MemTable* m = mem->Unref();
|
||||
if (m != nullptr) {
|
||||
auto* memory_usage = current->cfd()->imm()->current_memory_usage();
|
||||
assert(*memory_usage >= m->ApproximateMemoryUsage());
|
||||
@@ -510,17 +492,6 @@ 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 {
|
||||
@@ -559,7 +530,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, bool read_only)
|
||||
const std::string& db_session_id)
|
||||
: id_(id),
|
||||
name_(name),
|
||||
dummy_versions_(_dummy_versions),
|
||||
@@ -567,9 +538,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
refs_(0),
|
||||
initialized_(false),
|
||||
dropped_(false),
|
||||
flush_skip_reschedule_(false),
|
||||
internal_comparator_(cf_options.comparator),
|
||||
initial_cf_options_(SanitizeCfOptions(db_options, read_only, cf_options)),
|
||||
initial_cf_options_(SanitizeOptions(db_options, cf_options)),
|
||||
ioptions_(db_options, initial_cf_options_),
|
||||
mutable_cf_options_(initial_cf_options_),
|
||||
is_delete_range_supported_(
|
||||
@@ -622,10 +592,10 @@ 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()));
|
||||
blob_source_.reset(new BlobSource(ioptions(), db_id, db_session_id,
|
||||
blob_file_cache_.get()));
|
||||
|
||||
if (ioptions_.compaction_style == kCompactionStyleLevel) {
|
||||
compaction_picker_.reset(
|
||||
@@ -722,9 +692,9 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
if (mem_ != nullptr) {
|
||||
delete mem_->Unref();
|
||||
}
|
||||
autovector<ReadOnlyMemTable*> to_delete;
|
||||
autovector<MemTable*> to_delete;
|
||||
imm_.current()->Unref(&to_delete);
|
||||
for (auto* m : to_delete) {
|
||||
for (MemTable* m : to_delete) {
|
||||
delete m;
|
||||
}
|
||||
|
||||
@@ -930,11 +900,7 @@ uint64_t GetPendingCompactionBytesForCompactionSpeedup(
|
||||
return slowdown_threshold;
|
||||
}
|
||||
|
||||
// Prevent a small CF from triggering parallel compactions for other CFs.
|
||||
// Require compaction debt to be more than a full L0 to Lbase compaction.
|
||||
const uint64_t kMinDebtSize = 2 * mutable_cf_options.max_bytes_for_level_base;
|
||||
uint64_t size_threshold =
|
||||
std::max(bottommost_files_size / kBottommostSizeDivisor, kMinDebtSize);
|
||||
uint64_t size_threshold = bottommost_files_size / kBottommostSizeDivisor;
|
||||
return std::min(size_threshold, slowdown_threshold);
|
||||
}
|
||||
|
||||
@@ -997,7 +963,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;
|
||||
|
||||
@@ -1189,13 +1155,12 @@ MemTable* ColumnFamilyData::ConstructNewMemtable(
|
||||
write_buffer_manager_, earliest_seq, id_);
|
||||
}
|
||||
|
||||
void ColumnFamilyData::CreateNewMemtable(SequenceNumber earliest_seq) {
|
||||
void ColumnFamilyData::CreateNewMemtable(
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq) {
|
||||
if (mem_ != nullptr) {
|
||||
delete mem_->Unref();
|
||||
}
|
||||
// NOTE: db mutex must be locked for SetMemtable, so safe for
|
||||
// GetLatestMutableCFOptions
|
||||
SetMemtable(ConstructNewMemtable(GetLatestMutableCFOptions(), earliest_seq));
|
||||
SetMemtable(ConstructNewMemtable(mutable_cf_options, earliest_seq));
|
||||
mem_->Ref();
|
||||
}
|
||||
|
||||
@@ -1206,12 +1171,10 @@ bool ColumnFamilyData::NeedsCompaction() const {
|
||||
|
||||
Compaction* ColumnFamilyData::PickCompaction(
|
||||
const MutableCFOptions& mutable_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, LogBuffer* log_buffer) {
|
||||
const MutableDBOptions& mutable_db_options, LogBuffer* log_buffer) {
|
||||
auto* result = compaction_picker_->PickCompaction(
|
||||
GetName(), mutable_options, mutable_db_options, existing_snapshots,
|
||||
snapshot_checker, current_->storage_info(), log_buffer);
|
||||
GetName(), mutable_options, mutable_db_options, current_->storage_info(),
|
||||
log_buffer);
|
||||
if (result != nullptr) {
|
||||
result->FinalizeInputInfo(current_);
|
||||
}
|
||||
@@ -1237,10 +1200,8 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
read_opts.total_order_seek = true;
|
||||
MergeIteratorBuilder merge_iter_builder(&internal_comparator_, &arena);
|
||||
merge_iter_builder.AddIterator(super_version->mem->NewIterator(
|
||||
read_opts, /*seqno_to_time_mapping=*/nullptr, &arena,
|
||||
/*prefix_extractor=*/nullptr, /*for_flush=*/false));
|
||||
read_opts, /*seqno_to_time_mapping=*/nullptr, &arena));
|
||||
super_version->imm->AddIterators(read_opts, /*seqno_to_time_mapping=*/nullptr,
|
||||
/*prefix_extractor=*/nullptr,
|
||||
&merge_iter_builder,
|
||||
false /* add_range_tombstone_iter */);
|
||||
ScopedArenaPtr<InternalIterator> memtable_iter(merge_iter_builder.Finish());
|
||||
@@ -1368,17 +1329,20 @@ bool ColumnFamilyData::ReturnThreadLocalSuperVersion(SuperVersion* sv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ColumnFamilyData::InstallSuperVersion(
|
||||
SuperVersionContext* sv_context, InstrumentedMutex* db_mutex,
|
||||
std::optional<std::shared_ptr<SeqnoToTimeMapping>>
|
||||
new_seqno_to_time_mapping) {
|
||||
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) {
|
||||
SuperVersion* new_superversion = sv_context->new_superversion.release();
|
||||
new_superversion->mutable_cf_options = GetLatestMutableCFOptions();
|
||||
new_superversion->mutable_cf_options = mutable_cf_options;
|
||||
new_superversion->Init(this, mem_, imm_.current(), current_,
|
||||
new_seqno_to_time_mapping.has_value()
|
||||
? std::move(new_seqno_to_time_mapping.value())
|
||||
sv_context->new_seqno_to_time_mapping
|
||||
? std::move(sv_context->new_seqno_to_time_mapping)
|
||||
: super_version_
|
||||
? super_version_->ShareSeqnoToTimeMapping()
|
||||
: nullptr);
|
||||
@@ -1391,7 +1355,7 @@ void ColumnFamilyData::InstallSuperVersion(
|
||||
// currently RecalculateWriteStallConditions() treats it as further slowing
|
||||
// down is needed.
|
||||
super_version_->write_stall_condition =
|
||||
RecalculateWriteStallConditions(new_superversion->mutable_cf_options);
|
||||
RecalculateWriteStallConditions(mutable_cf_options);
|
||||
} else {
|
||||
super_version_->write_stall_condition =
|
||||
old_superversion->write_stall_condition;
|
||||
@@ -1404,15 +1368,14 @@ void ColumnFamilyData::InstallSuperVersion(
|
||||
ResetThreadLocalSuperVersions();
|
||||
|
||||
if (old_superversion->mutable_cf_options.write_buffer_size !=
|
||||
new_superversion->mutable_cf_options.write_buffer_size) {
|
||||
mem_->UpdateWriteBufferSize(
|
||||
new_superversion->mutable_cf_options.write_buffer_size);
|
||||
mutable_cf_options.write_buffer_size) {
|
||||
mem_->UpdateWriteBufferSize(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();
|
||||
@@ -1565,20 +1528,6 @@ Status ColumnFamilyData::ValidateOptions(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cf_options.compaction_style == kCompactionStyleUniversal) {
|
||||
int max_read_amp = cf_options.compaction_options_universal.max_read_amp;
|
||||
if (max_read_amp < -1) {
|
||||
return Status::NotSupported(
|
||||
"CompactionOptionsUniversal::max_read_amp should be at least -1.");
|
||||
} else if (0 < max_read_amp &&
|
||||
max_read_amp < cf_options.level0_file_num_compaction_trigger) {
|
||||
return Status::NotSupported(
|
||||
"CompactionOptionsUniversal::max_read_amp limits the number of sorted"
|
||||
" runs but is smaller than the compaction trigger "
|
||||
"level0_file_num_compaction_trigger.");
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1589,11 +1538,6 @@ Status ColumnFamilyData::SetOptions(
|
||||
BuildColumnFamilyOptions(initial_cf_options_, mutable_cf_options_);
|
||||
ConfigOptions config_opts;
|
||||
config_opts.mutable_options_only = true;
|
||||
#ifndef NDEBUG
|
||||
if (TEST_allowSetOptionsImmutableInMutable) {
|
||||
config_opts.mutable_options_only = false;
|
||||
}
|
||||
#endif
|
||||
Status s = GetColumnFamilyOptionsFromMap(config_opts, cf_opts, options_map,
|
||||
&cf_opts);
|
||||
if (s.ok()) {
|
||||
@@ -1606,6 +1550,28 @@ Status ColumnFamilyData::SetOptions(
|
||||
return s;
|
||||
}
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
Env::WriteLifeTimeHint ColumnFamilyData::CalculateSSTWriteHint(int level) {
|
||||
if (initial_cf_options_.compaction_style != kCompactionStyleLevel) {
|
||||
return Env::WLTH_NOT_SET;
|
||||
}
|
||||
if (level == 0) {
|
||||
return Env::WLTH_MEDIUM;
|
||||
}
|
||||
int base_level = current_->storage_info()->base_level();
|
||||
|
||||
// L1: medium, L2: long, ...
|
||||
if (level - base_level >= 2) {
|
||||
return Env::WLTH_EXTREME;
|
||||
} else if (level < base_level) {
|
||||
// There is no restriction which prevents level passed in to be smaller
|
||||
// than base_level.
|
||||
return Env::WLTH_MEDIUM;
|
||||
}
|
||||
return static_cast<Env::WriteLifeTimeHint>(
|
||||
level - base_level + static_cast<int>(Env::WLTH_MEDIUM));
|
||||
}
|
||||
|
||||
Status ColumnFamilyData::AddDirectories(
|
||||
std::map<std::string, std::shared_ptr<FSDirectory>>* created_dirs) {
|
||||
Status s;
|
||||
@@ -1641,19 +1607,6 @@ FSDirectory* ColumnFamilyData::GetDataDir(size_t path_id) const {
|
||||
return data_dirs_[path_id].get();
|
||||
}
|
||||
|
||||
void ColumnFamilyData::SetFlushSkipReschedule() {
|
||||
const Comparator* ucmp = user_comparator();
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
if (ts_sz == 0 || ioptions_.persist_user_defined_timestamps) {
|
||||
return;
|
||||
}
|
||||
flush_skip_reschedule_.store(true);
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::GetAndClearFlushSkipReschedule() {
|
||||
return flush_skip_reschedule_.exchange(false);
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::ShouldPostponeFlushToRetainUDT(
|
||||
uint64_t max_memtable_id) {
|
||||
const Comparator* ucmp = user_comparator();
|
||||
@@ -1671,9 +1624,6 @@ bool ColumnFamilyData::ShouldPostponeFlushToRetainUDT(
|
||||
}
|
||||
for (const Slice& table_newest_udt :
|
||||
imm()->GetTablesNewestUDT(max_memtable_id)) {
|
||||
if (table_newest_udt.empty()) {
|
||||
continue;
|
||||
}
|
||||
assert(table_newest_udt.size() == full_history_ts_low.size());
|
||||
// Checking the newest UDT contained in MemTable with ascending ID up to
|
||||
// `max_memtable_id`. Return immediately on finding the first MemTable that
|
||||
@@ -1707,8 +1657,7 @@ 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,
|
||||
/*read_only*/ true)),
|
||||
block_cache_tracer, io_tracer, db_id, db_session_id)),
|
||||
default_cfd_cache_(nullptr),
|
||||
db_name_(dbname),
|
||||
db_options_(db_options),
|
||||
@@ -1780,12 +1729,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, bool read_only) {
|
||||
const ColumnFamilyOptions& options) {
|
||||
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_, read_only);
|
||||
db_id_, db_session_id_);
|
||||
column_families_.insert({name, id});
|
||||
column_family_data_.insert({id, new_cfd});
|
||||
auto ucmp = new_cfd->user_comparator();
|
||||
@@ -1873,7 +1822,10 @@ const ImmutableOptions& GetImmutableOptions(ColumnFamilyHandle* column_family) {
|
||||
const ColumnFamilyData* const cfd = handle->cfd();
|
||||
assert(cfd);
|
||||
|
||||
return cfd->ioptions();
|
||||
const ImmutableOptions* ioptions = cfd->ioptions();
|
||||
assert(ioptions);
|
||||
|
||||
return *ioptions;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+45
-73
@@ -16,7 +16,6 @@
|
||||
|
||||
#include "cache/cache_reservation_manager.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
@@ -207,10 +206,9 @@ struct SuperVersion {
|
||||
// Accessing members of this class is not thread-safe and requires external
|
||||
// synchronization (ie db mutex held or on write thread).
|
||||
ColumnFamilyData* cfd;
|
||||
ReadOnlyMemTable* mem;
|
||||
MemTable* 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;
|
||||
@@ -222,8 +220,7 @@ struct SuperVersion {
|
||||
// enable UDT feature, this is an empty string.
|
||||
std::string full_history_ts_low;
|
||||
|
||||
// An immutable snapshot of the DB's seqno to time mapping, usually shared
|
||||
// between SuperVersions.
|
||||
// A shared copy of the DB's seqno to time mapping.
|
||||
std::shared_ptr<const SeqnoToTimeMapping> seqno_to_time_mapping{nullptr};
|
||||
|
||||
// should be called outside the mutex
|
||||
@@ -271,7 +268,7 @@ struct SuperVersion {
|
||||
// We need to_delete because during Cleanup(), imm->Unref() returns
|
||||
// all memtables that we need to free through this vector. We then
|
||||
// delete all those memtables outside of mutex, during destruction
|
||||
autovector<ReadOnlyMemTable*> to_delete;
|
||||
autovector<MemTable*> to_delete;
|
||||
};
|
||||
|
||||
Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
|
||||
@@ -281,9 +278,8 @@ Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options);
|
||||
Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
|
||||
ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
|
||||
bool read_only,
|
||||
const ColumnFamilyOptions& src);
|
||||
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
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.
|
||||
@@ -333,10 +329,6 @@ class ColumnFamilyData {
|
||||
void SetDropped();
|
||||
bool IsDropped() const { return dropped_.load(std::memory_order_relaxed); }
|
||||
|
||||
void SetFlushSkipReschedule();
|
||||
|
||||
bool GetAndClearFlushSkipReschedule();
|
||||
|
||||
// thread-safe
|
||||
int NumberLevels() const { return ioptions_.num_levels; }
|
||||
|
||||
@@ -345,17 +337,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
|
||||
@@ -382,49 +374,38 @@ class ColumnFamilyData {
|
||||
return mem()->GetFirstSequenceNumber() == 0 && imm()->NumNotFlushed() == 0;
|
||||
}
|
||||
|
||||
Version* current() { return current_; }
|
||||
Version* dummy_versions() { return dummy_versions_; }
|
||||
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 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
|
||||
void SetMemtable(MemTable* new_mem) {
|
||||
AssignMemtableID(new_mem);
|
||||
uint64_t memtable_id = last_memtable_id_.fetch_add(1) + 1;
|
||||
new_mem->SetID(memtable_id);
|
||||
mem_ = new_mem;
|
||||
if (ioptions_.disallow_memtable_writes) {
|
||||
mem_->MarkImmutable();
|
||||
}
|
||||
}
|
||||
|
||||
void AssignMemtableID(ReadOnlyMemTable* new_imm) {
|
||||
new_imm->SetID(++last_memtable_id_);
|
||||
}
|
||||
|
||||
// calculate the oldest log needed for the durability of this column family
|
||||
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(SequenceNumber earliest_seq);
|
||||
void CreateNewMemtable(const MutableCFOptions& mutable_cf_options,
|
||||
SequenceNumber earliest_seq);
|
||||
|
||||
TableCache* table_cache() const { return table_cache_.get(); }
|
||||
BlobFileCache* blob_file_cache() const { return blob_file_cache_.get(); }
|
||||
BlobSource* blob_source() const { return blob_source_.get(); }
|
||||
|
||||
// See documentation in compaction_picker.h
|
||||
// REQUIRES: DB mutex held
|
||||
bool NeedsCompaction() const;
|
||||
// REQUIRES: DB mutex held
|
||||
Compaction* PickCompaction(
|
||||
const MutableCFOptions& mutable_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, LogBuffer* log_buffer);
|
||||
Compaction* PickCompaction(const MutableCFOptions& mutable_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Check if the passed range overlap with any running compactions.
|
||||
// REQUIRES: DB mutex held
|
||||
@@ -488,14 +469,15 @@ class ColumnFamilyData {
|
||||
uint64_t GetSuperVersionNumber() const {
|
||||
return super_version_number_.load();
|
||||
}
|
||||
uint64_t GetSuperVersionNumberRelaxed() const {
|
||||
return super_version_number_.load(std::memory_order_relaxed);
|
||||
}
|
||||
// Only intended for use by DBImpl::InstallSuperVersion() and variants
|
||||
// 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()
|
||||
void InstallSuperVersion(SuperVersionContext* sv_context,
|
||||
InstrumentedMutex* db_mutex,
|
||||
std::optional<std::shared_ptr<SeqnoToTimeMapping>>
|
||||
new_seqno_to_time_mapping = {});
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
void InstallSuperVersion(SuperVersionContext* sv_context,
|
||||
InstrumentedMutex* db_mutex);
|
||||
|
||||
void ResetThreadLocalSuperVersions();
|
||||
|
||||
@@ -525,6 +507,8 @@ class ColumnFamilyData {
|
||||
return initial_cf_options_;
|
||||
}
|
||||
|
||||
Env::WriteLifeTimeHint CalculateSSTWriteHint(int level);
|
||||
|
||||
// created_dirs remembers directory created, so that we don't need to call
|
||||
// the same data creation operation again.
|
||||
Status AddDirectories(
|
||||
@@ -584,20 +568,18 @@ class ColumnFamilyData {
|
||||
// of its files (if missing)
|
||||
void RecoverEpochNumbers();
|
||||
|
||||
int GetUnflushedMemTableCountForWriteStallCheck() const {
|
||||
return (mem_->IsEmpty() ? 0 : 1) + imm_.NumNotFlushed();
|
||||
}
|
||||
|
||||
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, bool read_only);
|
||||
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);
|
||||
|
||||
std::vector<std::string> GetDbPaths() const;
|
||||
|
||||
@@ -610,15 +592,6 @@ class ColumnFamilyData {
|
||||
std::atomic<bool> initialized_;
|
||||
std::atomic<bool> dropped_; // true if client dropped it
|
||||
|
||||
// When user-defined timestamps in memtable only feature is enabled, this
|
||||
// flag indicates a successfully requested flush that should
|
||||
// skip being rescheduled and haven't undergone the rescheduling check yet.
|
||||
// This flag is cleared when a check skips rescheduling a FlushRequest.
|
||||
// With this flag, automatic flushes in regular cases can continue to
|
||||
// retain UDTs by getting rescheduled as usual while manual flushes and
|
||||
// error recovery flushes will proceed without getting rescheduled.
|
||||
std::atomic<bool> flush_skip_reschedule_;
|
||||
|
||||
const InternalKeyComparator internal_comparator_;
|
||||
InternalTblPropCollFactories internal_tbl_prop_coll_factories_;
|
||||
|
||||
@@ -681,7 +654,7 @@ class ColumnFamilyData {
|
||||
bool allow_2pc_;
|
||||
|
||||
// Memtable id to track flush.
|
||||
uint64_t last_memtable_id_;
|
||||
std::atomic<uint64_t> last_memtable_id_;
|
||||
|
||||
// Directories corresponding to cf_paths.
|
||||
std::vector<std::shared_ptr<FSDirectory>> data_dirs_;
|
||||
@@ -759,8 +732,7 @@ class ColumnFamilySet {
|
||||
|
||||
ColumnFamilyData* CreateColumnFamily(const std::string& name, uint32_t id,
|
||||
Version* dummy_version,
|
||||
const ColumnFamilyOptions& options,
|
||||
bool read_only);
|
||||
const ColumnFamilyOptions& options);
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>& GetRunningColumnFamiliesTimestampSize()
|
||||
const {
|
||||
|
||||
+78
-352
@@ -23,7 +23,6 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/utilities/object_registry.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
@@ -35,13 +34,6 @@
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
std::string EncodeAsUint64(uint64_t number) {
|
||||
std::string result;
|
||||
PutFixed64(&result, number);
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
static const int kValueSize = 1000;
|
||||
|
||||
@@ -271,8 +263,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
// them.
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
|
||||
ConfigOptions(), desc.options,
|
||||
SanitizeCfOptions(dbfull()->immutable_db_options(),
|
||||
/*read_only*/ false, current_cf_opt)));
|
||||
SanitizeOptions(dbfull()->immutable_db_options(), current_cf_opt)));
|
||||
cfi++;
|
||||
}
|
||||
}
|
||||
@@ -2233,7 +2224,7 @@ TEST_P(ColumnFamilyTest, CreateMissingColumnFamilies) {
|
||||
ASSERT_EQ(my_fs->options_files_created.load(), 2);
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, SanitizeCfOptions) {
|
||||
TEST_P(ColumnFamilyTest, SanitizeOptions) {
|
||||
DBOptions db_options;
|
||||
for (int s = kCompactionStyleLevel; s <= kCompactionStyleUniversal; ++s) {
|
||||
for (int l = 0; l <= 2; l++) {
|
||||
@@ -2249,8 +2240,8 @@ TEST_P(ColumnFamilyTest, SanitizeCfOptions) {
|
||||
original.write_buffer_size =
|
||||
l * 4 * 1024 * 1024 + i * 1024 * 1024 + j * 1024 + k;
|
||||
|
||||
ColumnFamilyOptions result = SanitizeCfOptions(
|
||||
ImmutableDBOptions(db_options), /*read_only*/ false, original);
|
||||
ColumnFamilyOptions result =
|
||||
SanitizeOptions(ImmutableDBOptions(db_options), original);
|
||||
ASSERT_TRUE(result.level0_stop_writes_trigger >=
|
||||
result.level0_slowdown_writes_trigger);
|
||||
ASSERT_TRUE(result.level0_slowdown_writes_trigger >=
|
||||
@@ -3013,25 +3004,19 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForCompactionDebt) {
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
|
||||
{
|
||||
// 1MB debt is way bigger than bottommost data so definitely triggers
|
||||
// speedup.
|
||||
VersionStorageInfo* vstorage = cfd->current()->storage_info();
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */,
|
||||
dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
// Eight bytes is way smaller than bottommost data so definitely does not
|
||||
// trigger speedup.
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(8, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
// 1MB is much larger than bottommost level size. However, since it's too
|
||||
// small in terms of absolute size, it does not trigger parallel compaction
|
||||
// in this case (see GetPendingCompactionBytesForCompactionSpeedup()).
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */,
|
||||
dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(
|
||||
2 * mutable_cf_options.max_bytes_for_level_base, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3074,20 +3059,12 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForMarkedFiles) {
|
||||
WaitForCompaction();
|
||||
AssertFilesPerLevel("0,1", 0 /* cf */);
|
||||
|
||||
// We should calculate the limit by obtaining the number of env background
|
||||
// threads, because the current test case will share the same env
|
||||
// with another case that may have already increased the number of
|
||||
// background threads which is larger than kParallelismLimit
|
||||
const auto limit = env_->GetBackgroundThreads(Env::Priority::LOW);
|
||||
|
||||
// Block the compaction thread pool so marked files accumulate in L0.
|
||||
std::vector<std::shared_ptr<test::SleepingBackgroundTask>> sleeping_tasks;
|
||||
for (int i = 0; i < limit; i++) {
|
||||
sleeping_tasks.emplace_back(
|
||||
std::make_shared<test::SleepingBackgroundTask>());
|
||||
test::SleepingBackgroundTask sleeping_tasks[kParallelismLimit];
|
||||
for (int i = 0; i < kParallelismLimit; i++) {
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
sleeping_tasks[i].get(), Env::Priority::LOW);
|
||||
sleeping_tasks[i]->WaitUntilSleeping();
|
||||
&sleeping_tasks[i], Env::Priority::LOW);
|
||||
sleeping_tasks[i].WaitUntilSleeping();
|
||||
}
|
||||
|
||||
// Zero marked upper-level files. No speedup.
|
||||
@@ -3106,9 +3083,9 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForMarkedFiles) {
|
||||
ASSERT_EQ(kParallelismLimit, dbfull()->TEST_BGCompactionsAllowed());
|
||||
AssertFilesPerLevel("2,1", 0 /* cf */);
|
||||
|
||||
for (int i = 0; i < limit; i++) {
|
||||
sleeping_tasks[i]->WakeUp();
|
||||
sleeping_tasks[i]->WaitUntilDone();
|
||||
for (int i = 0; i < kParallelismLimit; i++) {
|
||||
sleeping_tasks[i].WakeUp();
|
||||
sleeping_tasks[i].WaitUntilDone();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3193,8 +3170,6 @@ TEST_P(ColumnFamilyTest, IteratorCloseWALFile1) {
|
||||
SpecialEnv env(Env::Default());
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
// When this option is removed, the test will need re-engineering
|
||||
db_options_.background_close_inactive_wals = true;
|
||||
column_family_options_.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(2));
|
||||
Open();
|
||||
@@ -3247,8 +3222,6 @@ TEST_P(ColumnFamilyTest, IteratorCloseWALFile2) {
|
||||
env.SetBackgroundThreads(2, Env::HIGH);
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
// When this option is removed, the test will need re-engineering
|
||||
db_options_.background_close_inactive_wals = true;
|
||||
column_family_options_.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(2));
|
||||
Open();
|
||||
@@ -3306,8 +3279,6 @@ TEST_P(ColumnFamilyTest, ForwardIteratorCloseWALFile) {
|
||||
env.SetBackgroundThreads(2, Env::HIGH);
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
// When this option is removed, the test will need re-engineering
|
||||
db_options_.background_close_inactive_wals = true;
|
||||
column_family_options_.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(3));
|
||||
column_family_options_.level0_file_num_compaction_trigger = 2;
|
||||
@@ -3632,9 +3603,7 @@ TEST(ColumnFamilyTest, ValidateMemtableKVChecksumOption) {
|
||||
}
|
||||
|
||||
// Tests the flushing behavior of a column family to retain user-defined
|
||||
// timestamp when `persist_user_defined_timestamp` is false. The behavior of
|
||||
// auto flush is it makes some effort to retain user-defined timestamps while
|
||||
// the behavior of manual flush is that it skips retaining UDTs.
|
||||
// timestamp when `persist_user_defined_timestamp` is false.
|
||||
class ColumnFamilyRetainUDTTest : public ColumnFamilyTestBase {
|
||||
public:
|
||||
ColumnFamilyRetainUDTTest() : ColumnFamilyTestBase(kLatestFormatVersion) {}
|
||||
@@ -3652,27 +3621,6 @@ class ColumnFamilyRetainUDTTest : public ColumnFamilyTestBase {
|
||||
return db_->Put(WriteOptions(), handles_[cf], Slice(key), Slice(ts),
|
||||
Slice(value));
|
||||
}
|
||||
|
||||
std::string Get(int cf, const std::string& key, const std::string& read_ts) {
|
||||
ReadOptions ropts;
|
||||
Slice timestamp = read_ts;
|
||||
ropts.timestamp = ×tamp;
|
||||
std::string value;
|
||||
Status s = db_->Get(ropts, handles_[cf], Slice(key), &value);
|
||||
if (s.IsNotFound()) {
|
||||
return "NOT_FOUND";
|
||||
} else if (s.ok()) {
|
||||
return value;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void CheckEffectiveCutoffTime(uint64_t expected_cutoff) {
|
||||
std::string effective_full_history_ts_low;
|
||||
EXPECT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
EXPECT_EQ(EncodeAsUint64(expected_cutoff), effective_full_history_ts_low);
|
||||
}
|
||||
};
|
||||
|
||||
class TestTsComparator : public Comparator {
|
||||
@@ -3716,9 +3664,7 @@ TEST_F(ColumnFamilyRetainUDTTest, SanityCheck) {
|
||||
Close();
|
||||
}
|
||||
|
||||
class AutoFlushRetainUDTTest : public ColumnFamilyRetainUDTTest {};
|
||||
|
||||
TEST_F(AutoFlushRetainUDTTest, FullHistoryTsLowNotSet) {
|
||||
TEST_F(ColumnFamilyRetainUDTTest, FullHistoryTsLowNotSet) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
@@ -3728,22 +3674,28 @@ TEST_F(AutoFlushRetainUDTTest, FullHistoryTsLowNotSet) {
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Open();
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
// No `full_history_ts_low` explicitly set by user, auto flush is continued
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
|
||||
// No `full_history_ts_low` explicitly set by user, flush is continued
|
||||
// without checking if its UDTs expired.
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_OK(Flush(0));
|
||||
|
||||
// After flush, `full_history_ts_low` should be automatically advanced to
|
||||
// the effective cutoff timestamp: write_ts + 1
|
||||
CheckEffectiveCutoffTime(2);
|
||||
std::string cutoff_ts;
|
||||
PutFixed64(&cutoff_ts, 2);
|
||||
std::string effective_full_history_ts_low;
|
||||
ASSERT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(AutoFlushRetainUDTTest, AllKeysExpired) {
|
||||
TEST_F(ColumnFamilyRetainUDTTest, AllKeysExpired) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
@@ -3753,22 +3705,27 @@ TEST_F(AutoFlushRetainUDTTest, AllKeysExpired) {
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Open();
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(3)));
|
||||
// All keys expired w.r.t the configured `full_history_ts_low`, auto flush
|
||||
// continue without the need for a re-schedule.
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
|
||||
std::string cutoff_ts;
|
||||
PutFixed64(&cutoff_ts, 3);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
|
||||
// All keys expired w.r.t the configured `full_history_ts_low`, flush continue
|
||||
// without the need for a re-schedule.
|
||||
ASSERT_OK(Flush(0));
|
||||
|
||||
// `full_history_ts_low` stays unchanged after flush.
|
||||
CheckEffectiveCutoffTime(3);
|
||||
std::string effective_full_history_ts_low;
|
||||
ASSERT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(AutoFlushRetainUDTTest, NotAllKeysExpiredFlushToAvoidWriteStall) {
|
||||
TEST_F(ColumnFamilyRetainUDTTest, NotAllKeysExpiredFlushToAvoidWriteStall) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
@@ -3778,303 +3735,72 @@ TEST_F(AutoFlushRetainUDTTest, NotAllKeysExpiredFlushToAvoidWriteStall) {
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Open();
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(1)));
|
||||
std::string cutoff_ts;
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
|
||||
PutFixed64(&cutoff_ts, 1);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
|
||||
ASSERT_OK(db_->SetOptions(handles_[0], {{"max_write_buffer_number", "1"}}));
|
||||
// Not all keys expired, but auto flush is continued without a re-schedule
|
||||
// because of risk of write stall.
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
// Not all keys expired, but flush is continued without a re-schedule because
|
||||
// of risk of write stall.
|
||||
ASSERT_OK(Flush(0));
|
||||
|
||||
// After flush, `full_history_ts_low` should be automatically advanced to
|
||||
// the effective cutoff timestamp: write_ts + 1
|
||||
CheckEffectiveCutoffTime(2);
|
||||
std::string effective_full_history_ts_low;
|
||||
ASSERT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
|
||||
cutoff_ts.clear();
|
||||
PutFixed64(&cutoff_ts, 2);
|
||||
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(AutoFlushRetainUDTTest, NotAllKeysExpiredFlushRescheduled) {
|
||||
std::atomic<int> local_counter{1};
|
||||
TEST_F(ColumnFamilyRetainUDTTest, NotAllKeysExpiredFlushRescheduled) {
|
||||
std::string cutoff_ts;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::AfterRetainUDTReschedule:cb", [&](void* /*arg*/) {
|
||||
// Increasing full_history_ts_low so all keys expired after the initial
|
||||
// FlushRequest is rescheduled
|
||||
ASSERT_OK(
|
||||
db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(3)));
|
||||
cutoff_ts.clear();
|
||||
PutFixed64(&cutoff_ts, 3);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto reschedule_count = *static_cast<int*>(arg);
|
||||
ASSERT_EQ(2, reschedule_count);
|
||||
local_counter.fetch_add(1);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Open();
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(1)));
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
|
||||
PutFixed64(&cutoff_ts, 1);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
|
||||
// Not all keys expired, and there is no risk of write stall. Flush is
|
||||
// rescheduled. The actual flush happens after `full_history_ts_low` is
|
||||
// increased to mark all keys expired.
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
// Make sure callback is not skipped.
|
||||
ASSERT_EQ(2, local_counter);
|
||||
ASSERT_OK(Flush(0));
|
||||
|
||||
CheckEffectiveCutoffTime(3);
|
||||
std::string effective_full_history_ts_low;
|
||||
ASSERT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
// `full_history_ts_low` stays unchanged.
|
||||
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
class ManualFlushSkipRetainUDTTest : public ColumnFamilyRetainUDTTest {
|
||||
public:
|
||||
// Write an entry with timestamp that is not expired w.r.t cutoff timestamp,
|
||||
// and make sure automatic flush would be rescheduled to retain UDT.
|
||||
void CheckAutomaticFlushRetainUDT(uint64_t write_ts) {
|
||||
std::atomic<int> local_counter{1};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::AfterRetainUDTReschedule:cb", [&](void* /*arg*/) {
|
||||
// Increasing full_history_ts_low so all keys expired after the
|
||||
// initial FlushRequest is rescheduled
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(
|
||||
handles_[0], EncodeAsUint64(write_ts + 1)));
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto reschedule_count = *static_cast<int*>(arg);
|
||||
ASSERT_EQ(2, reschedule_count);
|
||||
local_counter.fetch_add(1);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
EXPECT_OK(Put(0, "foo", EncodeAsUint64(write_ts),
|
||||
"foo" + std::to_string(write_ts)));
|
||||
EXPECT_OK(dbfull()->TEST_SwitchWAL());
|
||||
EXPECT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
// Make sure callback is not skipped.
|
||||
EXPECT_EQ(2, local_counter);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, ManualFlush) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
// Manual flush proceeds without trying to retain UDT.
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(Flush(0));
|
||||
CheckEffectiveCutoffTime(2);
|
||||
CheckAutomaticFlushRetainUDT(3);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, FlushRemovesStaleEntries) {
|
||||
column_family_options_.max_write_buffer_number = 4;
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
ColumnFamilyHandle* cfh = db_->DefaultColumnFamily();
|
||||
ColumnFamilyData* cfd =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(cfh)->cfd();
|
||||
for (int version = 0; version < 100; version++) {
|
||||
if (version == 50) {
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db_)->TEST_SwitchMemtable(cfd));
|
||||
}
|
||||
ASSERT_OK(
|
||||
Put(0, "foo", EncodeAsUint64(version), "v" + std::to_string(version)));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush(0));
|
||||
TablePropertiesCollection tables_properties;
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(&tables_properties));
|
||||
ASSERT_EQ(1, tables_properties.size());
|
||||
std::shared_ptr<const TableProperties> table_properties =
|
||||
tables_properties.begin()->second;
|
||||
ASSERT_EQ(1, table_properties->num_entries);
|
||||
ASSERT_EQ(0, table_properties->num_deletions);
|
||||
ASSERT_EQ(0, table_properties->num_range_deletions);
|
||||
CheckEffectiveCutoffTime(100);
|
||||
CheckAutomaticFlushRetainUDT(101);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, RangeDeletionFlushRemovesStaleEntries) {
|
||||
column_family_options_.max_write_buffer_number = 4;
|
||||
Open();
|
||||
// TODO(yuzhangyu): a non 0 full history ts low is needed for this garbage
|
||||
// collection to kick in. This doesn't work well for the very first flush of
|
||||
// the column family. Not a big issue, but would be nice to improve this.
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(9)));
|
||||
|
||||
for (int i = 10; i < 100; i++) {
|
||||
ASSERT_OK(Put(0, "foo" + std::to_string(i), EncodeAsUint64(i),
|
||||
"val" + std::to_string(i)));
|
||||
if (i % 2 == 1) {
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), "foo" + std::to_string(i - 1),
|
||||
"foo" + std::to_string(i), EncodeAsUint64(i)));
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush(0));
|
||||
CheckEffectiveCutoffTime(100);
|
||||
std::string read_ts = EncodeAsUint64(100);
|
||||
std::string min_ts = EncodeAsUint64(0);
|
||||
ReadOptions ropts;
|
||||
Slice read_ts_slice = read_ts;
|
||||
std::string value;
|
||||
ropts.timestamp = &read_ts_slice;
|
||||
{
|
||||
Iterator* iter = db_->NewIterator(ropts);
|
||||
iter->SeekToFirst();
|
||||
int i = 11;
|
||||
while (iter->Valid()) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("foo" + std::to_string(i), iter->key());
|
||||
ASSERT_EQ("val" + std::to_string(i), iter->value());
|
||||
ASSERT_EQ(min_ts, iter->timestamp());
|
||||
iter->Next();
|
||||
i += 2;
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
delete iter;
|
||||
}
|
||||
TablePropertiesCollection tables_properties;
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(&tables_properties));
|
||||
ASSERT_EQ(1, tables_properties.size());
|
||||
std::shared_ptr<const TableProperties> table_properties =
|
||||
tables_properties.begin()->second;
|
||||
// 45 point data + 45 range deletions. 45 obsolete point data are garbage
|
||||
// collected.
|
||||
ASSERT_EQ(90, table_properties->num_entries);
|
||||
ASSERT_EQ(45, table_properties->num_deletions);
|
||||
ASSERT_EQ(45, table_properties->num_range_deletions);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, ManualCompaction) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
// Manual compaction proceeds without trying to retain UDT.
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v2"));
|
||||
ASSERT_OK(
|
||||
db_->CompactRange(CompactRangeOptions(), handles_[0], nullptr, nullptr));
|
||||
CheckEffectiveCutoffTime(2);
|
||||
CheckAutomaticFlushRetainUDT(3);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, BulkLoading) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
|
||||
// Test flush behavior in bulk loading scenarios.
|
||||
Options options(db_options_, column_family_options_);
|
||||
std::string sst_files_dir = dbname_ + "/sst_files/";
|
||||
ASSERT_OK(DestroyDir(env_, sst_files_dir));
|
||||
ASSERT_OK(env_->CreateDir(sst_files_dir));
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
std::string file1 = sst_files_dir + "file1.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
ASSERT_OK(sst_file_writer.Put("foo", EncodeAsUint64(0), "v2"));
|
||||
ExternalSstFileInfo file1_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file1_info));
|
||||
|
||||
// Bulk loading in UDT mode doesn't support external file key range overlap
|
||||
// with DB key range.
|
||||
ASSERT_TRUE(db_->IngestExternalFile({file1}, IngestExternalFileOptions())
|
||||
.IsInvalidArgument());
|
||||
|
||||
std::string file2 = sst_files_dir + "file2.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file2));
|
||||
ASSERT_OK(sst_file_writer.Put("bar", EncodeAsUint64(0), "val"));
|
||||
ExternalSstFileInfo file2_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file2_info));
|
||||
// A successful bulk loading, and it doesn't trigger any flush. As a result
|
||||
// the effective cutoff timestamp is also unchanged.
|
||||
ASSERT_OK(db_->IngestExternalFile({file2}, IngestExternalFileOptions()));
|
||||
|
||||
ASSERT_EQ(Get(0, "foo", EncodeAsUint64(1)), "v1");
|
||||
ASSERT_EQ(Get(0, "bar", EncodeAsUint64(0)), "val");
|
||||
CheckEffectiveCutoffTime(0);
|
||||
CheckAutomaticFlushRetainUDT(1);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, AutomaticFlushQueued) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
CheckEffectiveCutoffTime(0);
|
||||
|
||||
// Default `max_write_buffer_number=2` used, writing another memtable can get
|
||||
// automatic flush to proceed because of memory pressure. Not doing that so
|
||||
// we can test automatic flush gets to proceed because of an ongoing manual
|
||||
// flush attempt.
|
||||
ASSERT_OK(Flush(0));
|
||||
CheckEffectiveCutoffTime(2);
|
||||
CheckAutomaticFlushRetainUDT(3);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, ConcurrentManualFlushes) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
std::vector<ROCKSDB_NAMESPACE::port::Thread> manual_flush_tds;
|
||||
std::atomic<int> next_ts{0};
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
|
||||
auto manual_flush = [&](int write_ts) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
cv.wait(lock,
|
||||
[&write_ts, &next_ts] { return write_ts == next_ts.load(); });
|
||||
ASSERT_OK(Put(0, "foo" + std::to_string(write_ts),
|
||||
EncodeAsUint64(write_ts),
|
||||
"val_" + std::to_string(write_ts)));
|
||||
next_ts.fetch_add(1);
|
||||
cv.notify_all();
|
||||
}
|
||||
if (write_ts % 2 == 0) {
|
||||
ASSERT_OK(Flush(0));
|
||||
} else {
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), handles_[0], nullptr,
|
||||
nullptr));
|
||||
}
|
||||
};
|
||||
|
||||
for (int write_ts = 0; write_ts < 10; write_ts++) {
|
||||
manual_flush_tds.emplace_back(manual_flush, write_ts);
|
||||
}
|
||||
|
||||
for (auto& td : manual_flush_tds) {
|
||||
td.join();
|
||||
}
|
||||
|
||||
CheckEffectiveCutoffTime(10);
|
||||
CheckAutomaticFlushRetainUDT(11);
|
||||
Close();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// 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>
|
||||
|
||||
+88
-229
@@ -63,9 +63,7 @@ uint64_t TotalFileSize(const std::vector<FileMetaData*>& files) {
|
||||
return sum;
|
||||
}
|
||||
|
||||
// TODO(hx235): consider making this function part of the construction so we
|
||||
// don't forget to call it
|
||||
void Compaction::FinalizeInputInfo(Version* _input_version) {
|
||||
void Compaction::SetInputVersion(Version* _input_version) {
|
||||
input_version_ = _input_version;
|
||||
cfd_ = input_version_->cfd();
|
||||
|
||||
@@ -283,10 +281,9 @@ Compaction::Compaction(
|
||||
uint32_t _output_path_id, CompressionType _compression,
|
||||
CompressionOptions _compression_opts, Temperature _output_temperature,
|
||||
uint32_t _max_subcompactions, std::vector<FileMetaData*> _grandparents,
|
||||
std::optional<SequenceNumber> _earliest_snapshot,
|
||||
const SnapshotChecker* _snapshot_checker, bool _manual_compaction,
|
||||
const std::string& _trim_ts, double _score, bool _deletion_compaction,
|
||||
bool l0_files_might_overlap, CompactionReason _compaction_reason,
|
||||
bool _manual_compaction, const std::string& _trim_ts, double _score,
|
||||
bool _deletion_compaction, bool l0_files_might_overlap,
|
||||
CompactionReason _compaction_reason,
|
||||
BlobGarbageCollectionPolicy _blob_garbage_collection_policy,
|
||||
double _blob_garbage_collection_age_cutoff)
|
||||
: input_vstorage_(vstorage),
|
||||
@@ -308,8 +305,6 @@ Compaction::Compaction(
|
||||
l0_files_might_overlap_(l0_files_might_overlap),
|
||||
inputs_(PopulateWithAtomicBoundaries(vstorage, std::move(_inputs))),
|
||||
grandparents_(std::move(_grandparents)),
|
||||
earliest_snapshot_(_earliest_snapshot),
|
||||
snapshot_checker_(_snapshot_checker),
|
||||
score_(_score),
|
||||
bottommost_level_(
|
||||
// For simplicity, we don't support the concept of "bottommost level"
|
||||
@@ -332,22 +327,21 @@ 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),
|
||||
proximal_level_(
|
||||
// For simplicity, we don't support the concept of "proximal level"
|
||||
penultimate_level_(
|
||||
// For simplicity, we don't support the concept of "penultimate level"
|
||||
// with `CompactionReason::kExternalSstIngestion` and
|
||||
// `CompactionReason::kRefitLevel`
|
||||
_compaction_reason == CompactionReason::kExternalSstIngestion ||
|
||||
_compaction_reason == CompactionReason::kRefitLevel
|
||||
? Compaction::kInvalidLevel
|
||||
: EvaluateProximalLevel(vstorage, mutable_cf_options_,
|
||||
immutable_options_, start_level_,
|
||||
output_level_)) {
|
||||
: EvaluatePenultimateLevel(vstorage, immutable_options_,
|
||||
start_level_, output_level_)) {
|
||||
MarkFilesBeingCompacted(true);
|
||||
if (is_manual_compaction_) {
|
||||
compaction_reason_ = CompactionReason::kManualCompaction;
|
||||
@@ -368,17 +362,12 @@ Compaction::Compaction(
|
||||
}
|
||||
#endif
|
||||
|
||||
// setup input_levels_ and filtered_input_levels_
|
||||
// setup input_levels_
|
||||
{
|
||||
input_levels_.resize(num_input_levels());
|
||||
filtered_input_levels_.resize(num_input_levels());
|
||||
if (earliest_snapshot_.has_value()) {
|
||||
FilterInputsForCompactionIterator();
|
||||
} else {
|
||||
for (size_t which = 0; which < num_input_levels(); which++) {
|
||||
DoGenerateLevelFilesBrief(&input_levels_[which], inputs_[which].files,
|
||||
&arena_);
|
||||
}
|
||||
for (size_t which = 0; which < num_input_levels(); which++) {
|
||||
DoGenerateLevelFilesBrief(&input_levels_[which], inputs_[which].files,
|
||||
&arena_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,71 +394,53 @@ Compaction::Compaction(
|
||||
}
|
||||
}
|
||||
|
||||
PopulateProximalLevelOutputRange();
|
||||
PopulatePenultimateLevelOutputRange();
|
||||
}
|
||||
|
||||
void Compaction::PopulateProximalLevelOutputRange() {
|
||||
void Compaction::PopulatePenultimateLevelOutputRange() {
|
||||
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;
|
||||
proximal_output_range_type_ = ProximalOutputRangeType::kNonLastRange;
|
||||
penultimate_output_range_type_ = PenultimateOutputRangeType::kNonLastRange;
|
||||
|
||||
// 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).
|
||||
// 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).
|
||||
if (immutable_options_.compaction_style == kCompactionStyleUniversal) {
|
||||
exclude_level = kInvalidLevel;
|
||||
proximal_output_range_type_ = ProximalOutputRangeType::kFullRange;
|
||||
std::set<uint64_t> proximal_inputs;
|
||||
penultimate_output_range_type_ = PenultimateOutputRangeType::kFullRange;
|
||||
std::set<uint64_t> penultimate_inputs;
|
||||
for (const auto& input_lvl : inputs_) {
|
||||
if (input_lvl.level == proximal_level_) {
|
||||
if (input_lvl.level == penultimate_level_) {
|
||||
for (const auto& file : input_lvl.files) {
|
||||
proximal_inputs.emplace(file->fd.GetNumber());
|
||||
penultimate_inputs.emplace(file->fd.GetNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
auto proximal_files = input_vstorage_->LevelFiles(proximal_level_);
|
||||
for (const auto& file : proximal_files) {
|
||||
if (proximal_inputs.find(file->fd.GetNumber()) == proximal_inputs.end()) {
|
||||
auto penultimate_files = input_vstorage_->LevelFiles(penultimate_level_);
|
||||
for (const auto& file : penultimate_files) {
|
||||
if (penultimate_inputs.find(file->fd.GetNumber()) ==
|
||||
penultimate_inputs.end()) {
|
||||
exclude_level = number_levels_ - 1;
|
||||
proximal_output_range_type_ = ProximalOutputRangeType::kNonLastRange;
|
||||
penultimate_output_range_type_ =
|
||||
PenultimateOutputRangeType::kNonLastRange;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: should make use of `proximal_output_range_type_`.
|
||||
// FIXME: should make use of `penultimate_output_range_type_`.
|
||||
// FIXME: when last level's input range does not overlap with
|
||||
// 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;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
Compaction::~Compaction() {
|
||||
@@ -482,23 +453,23 @@ Compaction::~Compaction() {
|
||||
}
|
||||
|
||||
bool Compaction::SupportsPerKeyPlacement() const {
|
||||
return proximal_level_ != kInvalidLevel;
|
||||
return penultimate_level_ != kInvalidLevel;
|
||||
}
|
||||
|
||||
int Compaction::GetProximalLevel() const { return proximal_level_; }
|
||||
int Compaction::GetPenultimateLevel() const { return penultimate_level_; }
|
||||
|
||||
// smallest_key and largest_key include timestamps if user-defined timestamp is
|
||||
// enabled.
|
||||
bool Compaction::OverlapProximalLevelOutputRange(
|
||||
bool Compaction::OverlapPenultimateLevelOutputRange(
|
||||
const Slice& smallest_key, const Slice& largest_key) const {
|
||||
if (!SupportsPerKeyPlacement()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// See FIXME in Compaction::PopulateProximalLevelOutputRange().
|
||||
// See FIXME in Compaction::PopulatePenultimateLevelOutputRange().
|
||||
// We do not compact any key up in this case.
|
||||
if (proximal_level_smallest_.size() == 0 ||
|
||||
proximal_level_largest_.size() == 0) {
|
||||
if (penultimate_level_smallest_.size() == 0 ||
|
||||
penultimate_level_largest_.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -506,35 +477,28 @@ bool Compaction::OverlapProximalLevelOutputRange(
|
||||
input_vstorage_->InternalComparator()->user_comparator();
|
||||
|
||||
return ucmp->CompareWithoutTimestamp(
|
||||
smallest_key, proximal_level_largest_.user_key()) <= 0 &&
|
||||
smallest_key, penultimate_level_largest_.user_key()) <= 0 &&
|
||||
ucmp->CompareWithoutTimestamp(
|
||||
largest_key, proximal_level_smallest_.user_key()) >= 0;
|
||||
largest_key, penultimate_level_smallest_.user_key()) >= 0;
|
||||
}
|
||||
|
||||
// key includes timestamp if user-defined timestamp is enabled.
|
||||
void Compaction::TEST_AssertWithinProximalLevelOutputRange(
|
||||
const Slice& user_key, bool expect_failure) const {
|
||||
#ifdef NDEBUG
|
||||
(void)user_key;
|
||||
(void)expect_failure;
|
||||
#else
|
||||
assert(SupportsPerKeyPlacement());
|
||||
bool Compaction::WithinPenultimateLevelOutputRange(
|
||||
const ParsedInternalKey& ikey) const {
|
||||
if (!SupportsPerKeyPlacement()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(proximal_level_smallest_.size() > 0);
|
||||
assert(proximal_level_largest_.size() > 0);
|
||||
if (penultimate_level_smallest_.size() == 0 ||
|
||||
penultimate_level_largest_.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* cmp = input_vstorage_->user_comparator();
|
||||
const InternalKeyComparator* icmp = input_vstorage_->InternalComparator();
|
||||
|
||||
// op_type of a key can change during compaction, e.g. Merge -> Put.
|
||||
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
|
||||
return icmp->CompareKeySeq(ikey, penultimate_level_smallest_.Encode()) >= 0 &&
|
||||
icmp->CompareKeySeq(ikey, penultimate_level_largest_.Encode()) <= 0;
|
||||
}
|
||||
|
||||
bool Compaction::InputCompressionMatchesOutput() const {
|
||||
@@ -588,7 +552,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_;
|
||||
}
|
||||
|
||||
@@ -646,7 +610,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++) {
|
||||
@@ -687,7 +651,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 =
|
||||
@@ -720,11 +684,12 @@ bool Compaction::KeyRangeNotExistsBeyondOutputLevel(
|
||||
};
|
||||
|
||||
// Mark (or clear) each file that is being compacted
|
||||
void Compaction::MarkFilesBeingCompacted(bool being_compacted) const {
|
||||
void Compaction::MarkFilesBeingCompacted(bool mark_as_compacted) {
|
||||
for (size_t i = 0; i < num_input_levels(); i++) {
|
||||
for (size_t j = 0; j < inputs_[i].size(); j++) {
|
||||
assert(being_compacted != inputs_[i][j]->being_compacted);
|
||||
inputs_[i][j]->being_compacted = being_compacted;
|
||||
assert(mark_as_compacted ? !inputs_[i][j]->being_compacted
|
||||
: inputs_[i][j]->being_compacted);
|
||||
inputs_[i][j]->being_compacted = mark_as_compacted;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -768,7 +733,7 @@ uint64_t Compaction::CalculateTotalInputSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
void Compaction::ReleaseCompactionFiles(const Status& status) {
|
||||
void Compaction::ReleaseCompactionFiles(Status status) {
|
||||
MarkFilesBeingCompacted(false);
|
||||
cfd_->compaction_picker()->ReleaseCompactionFiles(this, status);
|
||||
}
|
||||
@@ -779,10 +744,8 @@ void Compaction::ResetNextCompactionIndex() {
|
||||
}
|
||||
|
||||
namespace {
|
||||
int InputSummary(const std::vector<FileMetaData*>& files,
|
||||
const std::vector<bool>& files_filtered, char* output,
|
||||
int InputSummary(const std::vector<FileMetaData*>& files, char* output,
|
||||
int len) {
|
||||
assert(files_filtered.empty() || (files.size() == files_filtered.size()));
|
||||
*output = '\0';
|
||||
int write = 0;
|
||||
for (size_t i = 0; i < files.size(); i++) {
|
||||
@@ -790,14 +753,8 @@ int InputSummary(const std::vector<FileMetaData*>& files,
|
||||
int ret;
|
||||
char sztxt[16];
|
||||
AppendHumanBytes(files.at(i)->fd.GetFileSize(), sztxt, 16);
|
||||
if (files_filtered.empty()) {
|
||||
ret = snprintf(output + write, sz, "%" PRIu64 "(%s) ",
|
||||
files.at(i)->fd.GetNumber(), sztxt);
|
||||
} else {
|
||||
ret = snprintf(output + write, sz, "%" PRIu64 "(%s filtered:%s) ",
|
||||
files.at(i)->fd.GetNumber(), sztxt,
|
||||
files_filtered.at(i) ? "true" : "false");
|
||||
}
|
||||
ret = snprintf(output + write, sz, "%" PRIu64 "(%s) ",
|
||||
files.at(i)->fd.GetNumber(), sztxt);
|
||||
if (ret < 0 || ret >= sz) {
|
||||
break;
|
||||
}
|
||||
@@ -823,15 +780,8 @@ void Compaction::Summary(char* output, int len) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(non_start_level_input_files_filtered_.empty() ||
|
||||
non_start_level_input_files_filtered_.size() == inputs_.size() - 1);
|
||||
write += InputSummary(
|
||||
inputs_[level_iter].files,
|
||||
(level_iter == 0 || non_start_level_input_files_filtered_.empty())
|
||||
? std::vector<bool>{}
|
||||
: non_start_level_input_files_filtered_[level_iter - 1],
|
||||
output + write, len - write);
|
||||
write +=
|
||||
InputSummary(inputs_[level_iter].files, output + write, len - write);
|
||||
if (write < 0 || write >= len) {
|
||||
return;
|
||||
}
|
||||
@@ -863,12 +813,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;
|
||||
}
|
||||
@@ -887,7 +837,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);
|
||||
}
|
||||
|
||||
@@ -914,15 +864,10 @@ bool Compaction::ShouldFormSubcompactions() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mutable_cf_options_.table_factory->Name() ==
|
||||
TableFactory::kPlainTableName()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -930,9 +875,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;
|
||||
@@ -962,25 +907,6 @@ bool Compaction::DoesInputReferenceBlobFiles() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t Compaction::MaxInputFileNewestKeyTime(const InternalKey* start,
|
||||
const InternalKey* end) const {
|
||||
uint64_t newest_key_time = kUnknownNewestKeyTime;
|
||||
const InternalKeyComparator& icmp =
|
||||
column_family_data()->internal_comparator();
|
||||
for (const auto& level_files : inputs_) {
|
||||
for (const auto& file : level_files.files) {
|
||||
if (start != nullptr && icmp.Compare(file->largest, *start) < 0) {
|
||||
continue;
|
||||
}
|
||||
if (end != nullptr && icmp.Compare(file->smallest, *end) > 0) {
|
||||
continue;
|
||||
}
|
||||
newest_key_time = std::max(newest_key_time, file->TryGetNewestKeyTime());
|
||||
}
|
||||
}
|
||||
return newest_key_time;
|
||||
}
|
||||
|
||||
uint64_t Compaction::MinInputFileOldestAncesterTime(
|
||||
const InternalKey* start, const InternalKey* end) const {
|
||||
uint64_t min_oldest_ancester_time = std::numeric_limits<uint64_t>::max();
|
||||
@@ -1014,9 +940,8 @@ uint64_t Compaction::MinInputFileEpochNumber() const {
|
||||
return min_epoch_number;
|
||||
}
|
||||
|
||||
int Compaction::EvaluateProximalLevel(
|
||||
int Compaction::EvaluatePenultimateLevel(
|
||||
const VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const ImmutableOptions& immutable_options, const int start_level,
|
||||
const int output_level) {
|
||||
// TODO: currently per_key_placement feature only support level and universal
|
||||
@@ -1029,26 +954,26 @@ int Compaction::EvaluateProximalLevel(
|
||||
return kInvalidLevel;
|
||||
}
|
||||
|
||||
int proximal_level = output_level - 1;
|
||||
assert(proximal_level < immutable_options.num_levels);
|
||||
if (proximal_level <= 0) {
|
||||
int penultimate_level = output_level - 1;
|
||||
assert(penultimate_level < immutable_options.num_levels);
|
||||
if (penultimate_level <= 0) {
|
||||
return kInvalidLevel;
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
// 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 proximal level.
|
||||
// file on the penultimate level.
|
||||
if (start_level == immutable_options.num_levels - 1 &&
|
||||
(immutable_options.compaction_style != kCompactionStyleUniversal ||
|
||||
!vstorage->LevelFiles(proximal_level).empty())) {
|
||||
!vstorage->LevelFiles(penultimate_level).empty())) {
|
||||
return kInvalidLevel;
|
||||
}
|
||||
|
||||
bool supports_per_key_placement =
|
||||
mutable_cf_options.preclude_last_level_data_seconds > 0;
|
||||
immutable_options.preclude_last_level_data_seconds > 0;
|
||||
|
||||
// it could be overridden by unittest
|
||||
TEST_SYNC_POINT_CALLBACK("Compaction::SupportsPerKeyPlacement:Enabled",
|
||||
@@ -1057,73 +982,7 @@ int Compaction::EvaluateProximalLevel(
|
||||
return kInvalidLevel;
|
||||
}
|
||||
|
||||
return proximal_level;
|
||||
}
|
||||
|
||||
void Compaction::FilterInputsForCompactionIterator() {
|
||||
assert(earliest_snapshot_.has_value());
|
||||
// cfd_ is not populated at Compaction construction time, get it from
|
||||
// VersionStorageInfo instead.
|
||||
assert(input_vstorage_);
|
||||
const auto* ucmp = input_vstorage_->user_comparator();
|
||||
assert(ucmp);
|
||||
// Simply comparing file boundaries when user-defined timestamp is defined
|
||||
// is not as safe because we need to also compare timestamp to know for
|
||||
// sure. Although entries with higher timestamp is also supposed to have
|
||||
// higher sequence number for the same user key (without timestamp).
|
||||
assert(ucmp->timestamp_size() == 0);
|
||||
size_t num_input_levels = inputs_.size();
|
||||
// TODO(yuzhangyu): filtering of older L0 file by new L0 file is not
|
||||
// supported yet.
|
||||
FileMetaData* rangedel_candidate = inputs_[0].level == 0
|
||||
? inputs_[0].files.back()
|
||||
: inputs_[0].files.front();
|
||||
assert(rangedel_candidate);
|
||||
if (!rangedel_candidate->FileIsStandAloneRangeTombstone() ||
|
||||
!DataIsDefinitelyInSnapshot(rangedel_candidate->fd.smallest_seqno,
|
||||
earliest_snapshot_.value(),
|
||||
snapshot_checker_)) {
|
||||
for (size_t level = 0; level < num_input_levels; level++) {
|
||||
DoGenerateLevelFilesBrief(&input_levels_[level], inputs_[level].files,
|
||||
&arena_);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Slice rangedel_start_ukey = rangedel_candidate->smallest.user_key();
|
||||
Slice rangedel_end_ukey = rangedel_candidate->largest.user_key();
|
||||
SequenceNumber rangedel_seqno = rangedel_candidate->fd.smallest_seqno;
|
||||
|
||||
std::vector<std::vector<FileMetaData*>> non_start_level_input_files;
|
||||
non_start_level_input_files.reserve(num_input_levels - 1);
|
||||
non_start_level_input_files_filtered_.reserve(num_input_levels - 1);
|
||||
for (size_t level = 1; level < num_input_levels; level++) {
|
||||
non_start_level_input_files.emplace_back();
|
||||
non_start_level_input_files_filtered_.emplace_back();
|
||||
for (FileMetaData* file : inputs_[level].files) {
|
||||
non_start_level_input_files_filtered_.back().push_back(false);
|
||||
// When range data and point data has the same sequence number, point
|
||||
// data wins. Range deletion end key is exclusive, so check it's bigger
|
||||
// than file right boundary user key.
|
||||
if (rangedel_seqno > file->fd.largest_seqno &&
|
||||
ucmp->CompareWithoutTimestamp(rangedel_start_ukey,
|
||||
file->smallest.user_key()) <= 0 &&
|
||||
ucmp->CompareWithoutTimestamp(rangedel_end_ukey,
|
||||
file->largest.user_key()) > 0) {
|
||||
non_start_level_input_files_filtered_.back().back() = true;
|
||||
filtered_input_levels_[level].push_back(file);
|
||||
} else {
|
||||
non_start_level_input_files.back().push_back(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DoGenerateLevelFilesBrief(&input_levels_[0], inputs_[0].files, &arena_);
|
||||
assert(non_start_level_input_files.size() == num_input_levels - 1);
|
||||
for (size_t level = 1; level < num_input_levels; level++) {
|
||||
DoGenerateLevelFilesBrief(&input_levels_[level],
|
||||
non_start_level_input_files[level - 1], &arena_);
|
||||
}
|
||||
return penultimate_level;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+64
-120
@@ -8,8 +8,6 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "db/version_set.h"
|
||||
#include "memory/arena.h"
|
||||
#include "options/cf_options.h"
|
||||
@@ -92,8 +90,6 @@ class Compaction {
|
||||
CompressionOptions compression_opts,
|
||||
Temperature output_temperature, uint32_t max_subcompactions,
|
||||
std::vector<FileMetaData*> grandparents,
|
||||
std::optional<SequenceNumber> earliest_snapshot,
|
||||
const SnapshotChecker* snapshot_checker,
|
||||
bool manual_compaction = false, const std::string& trim_ts = "",
|
||||
double score = -1, bool deletion_compaction = false,
|
||||
bool l0_files_might_overlap = true,
|
||||
@@ -102,13 +98,13 @@ class Compaction {
|
||||
BlobGarbageCollectionPolicy::kUseDefault,
|
||||
double blob_garbage_collection_age_cutoff = -1);
|
||||
|
||||
// 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
|
||||
// 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
|
||||
kNonLastRange, // only the keys within non_last_level compaction inputs can
|
||||
// be outputted to the proximal level
|
||||
kDisabled, // no data can be outputted to the proximal level
|
||||
// be outputted to the penultimate level
|
||||
kDisabled, // no data can be outputted to the penultimate level
|
||||
};
|
||||
|
||||
// No copying allowed
|
||||
@@ -184,16 +180,6 @@ class Compaction {
|
||||
return &input_levels_[compaction_input_level];
|
||||
}
|
||||
|
||||
// Returns the filtered input files of the specified compaction input level.
|
||||
// For now, only non start level is filtered.
|
||||
const std::vector<FileMetaData*>& filtered_input_levels(
|
||||
size_t compaction_input_level) const {
|
||||
const std::vector<FileMetaData*>& filtered_input_level =
|
||||
filtered_input_levels_[compaction_input_level];
|
||||
assert(compaction_input_level != 0 || filtered_input_level.size() == 0);
|
||||
return filtered_input_level;
|
||||
}
|
||||
|
||||
// Maximum size of files to build during this compaction.
|
||||
uint64_t max_output_file_size() const { return max_output_file_size_; }
|
||||
|
||||
@@ -244,7 +230,7 @@ class Compaction {
|
||||
// Delete this compaction from the list of running compactions.
|
||||
//
|
||||
// Requirement: DB mutex held
|
||||
void ReleaseCompactionFiles(const Status& status);
|
||||
void ReleaseCompactionFiles(Status status);
|
||||
|
||||
// Returns the summary of the compaction in "output" with maximum "len"
|
||||
// in bytes. The caller is responsible for the memory management of
|
||||
@@ -288,14 +274,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.
|
||||
@@ -303,7 +289,14 @@ class Compaction {
|
||||
// is the sum of all input file sizes.
|
||||
uint64_t OutputFilePreallocationSize() const;
|
||||
|
||||
void FinalizeInputInfo(Version* input_version);
|
||||
// TODO(hx235): eventually we should consider `InitInputTableProperties()`'s
|
||||
// status and fail the compaction if needed
|
||||
// TODO(hx235): consider making this function part of the construction so we
|
||||
// don't forget to call it
|
||||
void FinalizeInputInfo(Version* input_version) {
|
||||
SetInputVersion(input_version);
|
||||
InitInputTableProperties().PermitUncheckedError();
|
||||
}
|
||||
|
||||
struct InputLevelSummaryBuffer {
|
||||
char buffer[128];
|
||||
@@ -340,16 +333,6 @@ class Compaction {
|
||||
int output_level, VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs);
|
||||
|
||||
// TODO(hx235): eventually we should consider `InitInputTableProperties()`'s
|
||||
// status and fail the compaction if needed
|
||||
//
|
||||
// May open and read table files for table property.
|
||||
// Should not be called while holding mutex_.
|
||||
const TablePropertiesCollection& GetOrInitInputTableProperties() {
|
||||
InitInputTableProperties().PermitUncheckedError();
|
||||
return input_table_properties_;
|
||||
}
|
||||
|
||||
const TablePropertiesCollection& GetInputTableProperties() const {
|
||||
return input_table_properties_;
|
||||
}
|
||||
@@ -370,30 +353,30 @@ class Compaction {
|
||||
|
||||
Slice GetLargestUserKey() const { return largest_user_key_; }
|
||||
|
||||
ProximalOutputRangeType GetProximalOutputRangeType() const {
|
||||
return proximal_output_range_type_;
|
||||
PenultimateOutputRangeType GetPenultimateOutputRangeType() const {
|
||||
return penultimate_output_range_type_;
|
||||
}
|
||||
|
||||
// Return true if the compaction supports per_key_placement
|
||||
bool SupportsPerKeyPlacement() const;
|
||||
|
||||
// Get per_key_placement proximal output level, which is `last_level - 1`
|
||||
// Get per_key_placement penultimate output level, which is `last_level - 1`
|
||||
// if per_key_placement feature is supported. Otherwise, return -1.
|
||||
int GetProximalLevel() const;
|
||||
int GetPenultimateLevel() const;
|
||||
|
||||
// Return true if the given range is overlap with proximal level output
|
||||
// Return true if the given range is overlap with penultimate level output
|
||||
// range.
|
||||
// Both smallest_key and largest_key include timestamps if user-defined
|
||||
// timestamp is enabled.
|
||||
bool OverlapProximalLevelOutputRange(const Slice& smallest_key,
|
||||
const Slice& largest_key) const;
|
||||
bool OverlapPenultimateLevelOutputRange(const Slice& smallest_key,
|
||||
const Slice& largest_key) 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;
|
||||
// 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;
|
||||
|
||||
CompactionReason compaction_reason() const { return compaction_reason_; }
|
||||
|
||||
@@ -415,12 +398,6 @@ class Compaction {
|
||||
return blob_garbage_collection_age_cutoff_;
|
||||
}
|
||||
|
||||
// start and end are sub compact range. Null if no boundary.
|
||||
// This is used to calculate the newest_key_time table property after
|
||||
// compaction.
|
||||
uint64_t MaxInputFileNewestKeyTime(const InternalKey* start,
|
||||
const InternalKey* end) const;
|
||||
|
||||
// start and end are sub compact range. Null if no boundary.
|
||||
// This is used to filter out some input files' ancester's time range.
|
||||
uint64_t MinInputFileOldestAncesterTime(const InternalKey* start,
|
||||
@@ -441,34 +418,28 @@ class Compaction {
|
||||
|
||||
static constexpr int kInvalidLevel = -1;
|
||||
|
||||
// Evaluate proximal output level. If the compaction supports
|
||||
// per_key_placement feature, it returns the proximal level number.
|
||||
// Evaluate penultimate output level. If the compaction supports
|
||||
// per_key_placement feature, it returns the penultimate level number.
|
||||
// Otherwise, it's set to kInvalidLevel (-1), which means
|
||||
// output_to_proximal_level is not supported.
|
||||
// Note: even the proximal level output is supported (ProximalLevel !=
|
||||
// output_to_penultimate_level is not supported.
|
||||
// Note: even the penultimate level output is supported (PenultimateLevel !=
|
||||
// kInvalidLevel), some key range maybe unsafe to be outputted to the
|
||||
// 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;
|
||||
// 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 ImmutableOptions& immutable_options,
|
||||
const int start_level,
|
||||
const int output_level);
|
||||
|
||||
private:
|
||||
void SetInputVersion(Version* input_version);
|
||||
|
||||
Status InitInputTableProperties();
|
||||
|
||||
// mark (or clear) all files that are being compacted
|
||||
void MarkFilesBeingCompacted(bool mark_as_compacted);
|
||||
|
||||
// get the smallest and largest key present in files to be compacted
|
||||
static void GetBoundaryKeys(VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs,
|
||||
@@ -482,17 +453,10 @@ class Compaction {
|
||||
InternalKey* smallest_key, InternalKey* largest_key,
|
||||
int exclude_level = -1);
|
||||
|
||||
// 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:
|
||||
// When a standalone range deletion file on the start level is recognized and
|
||||
// can be determined to completely shadow some input files on non-start level.
|
||||
// These files will be filtered out and later not feed to compaction iterator.
|
||||
void FilterInputsForCompactionIterator();
|
||||
// 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();
|
||||
|
||||
// Get the atomic file boundaries for all files in the compaction. Necessary
|
||||
// in order to avoid the scenario described in
|
||||
@@ -544,30 +508,12 @@ class Compaction {
|
||||
// Compaction input files organized by level. Constant after construction
|
||||
const std::vector<CompactionInputFiles> inputs_;
|
||||
|
||||
// All files from inputs_ that are not filtered and will be fed to compaction
|
||||
// iterator, organized more closely in memory.
|
||||
// A copy of inputs_, organized more closely in memory
|
||||
autovector<LevelFilesBrief, 2> input_levels_;
|
||||
|
||||
// State used to check for number of overlapping grandparent files
|
||||
// (grandparent == "output_level_ + 1")
|
||||
std::vector<FileMetaData*> grandparents_;
|
||||
|
||||
// The earliest snapshot and snapshot checker at compaction picking time.
|
||||
// These fields are only set for deletion triggered compactions picked in
|
||||
// universal compaction. And when user-defined timestamp is not enabled.
|
||||
// It will be used to possibly filter out some non start level input files.
|
||||
std::optional<SequenceNumber> earliest_snapshot_;
|
||||
const SnapshotChecker* snapshot_checker_;
|
||||
|
||||
// Markers for which non start level input files are filtered out if
|
||||
// applicable. Only applicable if earliest_snapshot_ is provided and input
|
||||
// start level has a standalone range deletion file. Filtered files are
|
||||
// tracked in `filtered_input_levels_`.
|
||||
std::vector<std::vector<bool>> non_start_level_input_files_filtered_;
|
||||
|
||||
// All files from inputs_ that are filtered.
|
||||
std::vector<std::vector<FileMetaData*>> filtered_input_levels_;
|
||||
|
||||
const double score_; // score that was used to pick this compaction.
|
||||
|
||||
// Is this compaction creating a file in the bottom most level?
|
||||
@@ -612,24 +558,22 @@ 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 proximal_level_;
|
||||
const int penultimate_level_;
|
||||
|
||||
// Key range for proximal level output
|
||||
// Key range for penultimate level output
|
||||
// includes timestamp if user-defined timestamp is enabled.
|
||||
// proximal_output_range_type_ shows the range type
|
||||
InternalKey proximal_level_smallest_;
|
||||
InternalKey proximal_level_largest_;
|
||||
ProximalOutputRangeType proximal_output_range_type_ =
|
||||
ProximalOutputRangeType::kNotSupported;
|
||||
// penultimate_output_range_type_ shows the range type
|
||||
InternalKey penultimate_level_smallest_;
|
||||
InternalKey penultimate_level_largest_;
|
||||
PenultimateOutputRangeType penultimate_output_range_type_ =
|
||||
PenultimateOutputRangeType::kNotSupported;
|
||||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Helper struct only for tests, which contains the data to decide if a key
|
||||
// should be output to the proximal level.
|
||||
// should be output to the penultimate level.
|
||||
// TODO: remove this when the public feature knob is available
|
||||
struct PerKeyPlacementContext {
|
||||
const int level;
|
||||
@@ -637,16 +581,16 @@ struct PerKeyPlacementContext {
|
||||
const Slice value;
|
||||
const SequenceNumber seq_num;
|
||||
|
||||
bool& output_to_proximal_level;
|
||||
bool& output_to_penultimate_level;
|
||||
|
||||
PerKeyPlacementContext(int _level, Slice _key, Slice _value,
|
||||
SequenceNumber _seq_num,
|
||||
bool& _output_to_proximal_level)
|
||||
bool& _output_to_penultimate_level)
|
||||
: level(_level),
|
||||
key(_key),
|
||||
value(_value),
|
||||
seq_num(_seq_num),
|
||||
output_to_proximal_level(_output_to_proximal_level) {}
|
||||
output_to_penultimate_level(_output_to_penultimate_level) {}
|
||||
};
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace ROCKSDB_NAMESPACE {
|
||||
CompactionIterator::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,
|
||||
@@ -38,21 +37,23 @@ CompactionIterator::CompactionIterator(
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const std::shared_ptr<Logger> info_log,
|
||||
const std::string* full_history_ts_low,
|
||||
std::optional<SequenceNumber> preserve_seqno_min)
|
||||
const SequenceNumber preserve_time_min_seqno,
|
||||
const SequenceNumber preclude_last_level_min_seqno)
|
||||
: CompactionIterator(
|
||||
input, cmp, merge_helper, last_sequence, snapshots, earliest_snapshot,
|
||||
input, cmp, merge_helper, last_sequence, snapshots,
|
||||
earliest_write_conflict_snapshot, job_snapshot, snapshot_checker, env,
|
||||
report_detailed_time, expect_valid_internal_key, range_del_agg,
|
||||
blob_file_builder, allow_data_in_errors, enforce_single_del_contracts,
|
||||
manual_compaction_canceled,
|
||||
compaction ? std::make_unique<RealCompaction>(compaction) : nullptr,
|
||||
std::unique_ptr<CompactionProxy>(
|
||||
compaction ? new RealCompaction(compaction) : nullptr),
|
||||
must_count_input_entries, compaction_filter, shutting_down, info_log,
|
||||
full_history_ts_low, preserve_seqno_min) {}
|
||||
full_history_ts_low, preserve_time_min_seqno,
|
||||
preclude_last_level_min_seqno) {}
|
||||
|
||||
CompactionIterator::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,
|
||||
@@ -65,7 +66,8 @@ CompactionIterator::CompactionIterator(
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const std::shared_ptr<Logger> info_log,
|
||||
const std::string* full_history_ts_low,
|
||||
std::optional<SequenceNumber> preserve_seqno_min)
|
||||
const SequenceNumber preserve_time_min_seqno,
|
||||
const SequenceNumber preclude_last_level_min_seqno)
|
||||
: input_(input, cmp, must_count_input_entries),
|
||||
cmp_(cmp),
|
||||
merge_helper_(merge_helper),
|
||||
@@ -89,7 +91,9 @@ CompactionIterator::CompactionIterator(
|
||||
// snapshots_ cannot be nullptr, but we will assert later in the body of
|
||||
// the constructor.
|
||||
visible_at_tip_(snapshots_ ? snapshots_->empty() : false),
|
||||
earliest_snapshot_(earliest_snapshot),
|
||||
earliest_snapshot_(!snapshots_ || snapshots_->empty()
|
||||
? kMaxSequenceNumber
|
||||
: snapshots_->at(0)),
|
||||
info_log_(info_log),
|
||||
allow_data_in_errors_(allow_data_in_errors),
|
||||
enforce_single_del_contracts_(enforce_single_del_contracts),
|
||||
@@ -106,9 +110,10 @@ CompactionIterator::CompactionIterator(
|
||||
current_key_committed_(false),
|
||||
cmp_with_history_ts_low_(0),
|
||||
level_(compaction_ == nullptr ? 0 : compaction_->level()),
|
||||
preserve_seqno_after_(preserve_seqno_min.value_or(earliest_snapshot)) {
|
||||
preserve_time_min_seqno_(preserve_time_min_seqno),
|
||||
preclude_last_level_min_seqno_(preclude_last_level_min_seqno) {
|
||||
assert(snapshots_ != nullptr);
|
||||
assert(preserve_seqno_after_ <= earliest_snapshot_);
|
||||
assert(preserve_time_min_seqno_ <= preclude_last_level_min_seqno_);
|
||||
|
||||
if (compaction_ != nullptr) {
|
||||
level_ptrs_ = std::vector<size_t>(compaction_->number_levels(), 0);
|
||||
@@ -867,8 +872,8 @@ void CompactionIterator::NextFromInput() {
|
||||
if (Valid()) {
|
||||
at_next_ = true;
|
||||
}
|
||||
} else if (last_sequence != kMaxSequenceNumber &&
|
||||
(last_snapshot == current_user_key_snapshot_ ||
|
||||
} else if (last_snapshot == current_user_key_snapshot_ ||
|
||||
(last_snapshot > 0 &&
|
||||
last_snapshot < current_user_key_snapshot_)) {
|
||||
// If the earliest snapshot is which this key is visible in
|
||||
// is the same as the visibility of a previous instance of the
|
||||
@@ -1013,6 +1018,7 @@ 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;
|
||||
@@ -1253,6 +1259,71 @@ 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_)) {
|
||||
@@ -1261,6 +1332,13 @@ 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.
|
||||
@@ -1278,7 +1356,8 @@ void CompactionIterator::PrepareOutput() {
|
||||
!compaction_->allow_ingest_behind() && bottommost_level_ &&
|
||||
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
|
||||
ikey_.type != kTypeMerge && current_key_committed_ &&
|
||||
ikey_.sequence <= preserve_seqno_after_ && !is_range_del_) {
|
||||
!output_to_penultimate_level_ &&
|
||||
ikey_.sequence < preserve_time_min_seqno_ && !is_range_del_) {
|
||||
if (ikey_.type == kTypeDeletion ||
|
||||
(ikey_.type == kTypeSingleDeletion && timestamp_size_ == 0)) {
|
||||
ROCKS_LOG_FATAL(
|
||||
|
||||
@@ -117,6 +117,10 @@ 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 {
|
||||
@@ -124,6 +128,8 @@ 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(); }
|
||||
@@ -145,11 +151,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 {
|
||||
@@ -161,7 +167,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 {
|
||||
@@ -178,6 +184,14 @@ 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_;
|
||||
};
|
||||
@@ -189,7 +203,6 @@ class CompactionIterator {
|
||||
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,
|
||||
@@ -202,29 +215,28 @@ 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,
|
||||
std::optional<SequenceNumber> preserve_seqno_min = {});
|
||||
const SequenceNumber preserve_time_min_seqno = kMaxSequenceNumber,
|
||||
const SequenceNumber preclude_last_level_min_seqno = kMaxSequenceNumber);
|
||||
|
||||
// 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,
|
||||
std::optional<SequenceNumber> preserve_seqno_min = {});
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
|
||||
bool enforce_single_del_contracts,
|
||||
const std::atomic<bool>& manual_compaction_canceled,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
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();
|
||||
|
||||
@@ -255,6 +267,11 @@ 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_; }
|
||||
@@ -266,6 +283,10 @@ 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
|
||||
@@ -417,6 +438,16 @@ 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;
|
||||
@@ -474,8 +505,17 @@ class CompactionIterator {
|
||||
// just been zeroed out during bottommost compaction.
|
||||
bool last_key_seq_zeroed_{false};
|
||||
|
||||
// Max seqno that can be zeroed out at last level (various reasons)
|
||||
const SequenceNumber preserve_seqno_after_ = kMaxSequenceNumber;
|
||||
// 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;
|
||||
|
||||
void AdvanceInputIter() { input_.Next(); }
|
||||
|
||||
@@ -498,12 +538,18 @@ class CompactionIterator {
|
||||
|
||||
inline bool CompactionIterator::DefinitelyInSnapshot(SequenceNumber seq,
|
||||
SequenceNumber snapshot) {
|
||||
return DataIsDefinitelyInSnapshot(seq, snapshot, snapshot_checker_);
|
||||
return ((seq) <= (snapshot) &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) ==
|
||||
SnapshotCheckerResult::kInSnapshot)));
|
||||
}
|
||||
|
||||
inline bool CompactionIterator::DefinitelyNotInSnapshot(
|
||||
SequenceNumber seq, SequenceNumber snapshot) {
|
||||
return DataIsDefinitelyNotInSnapshot(seq, snapshot, snapshot_checker_);
|
||||
return ((seq) > (snapshot) ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
UNLIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) ==
|
||||
SnapshotCheckerResult::kNotInSnapshot)));
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -192,6 +192,11 @@ 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;
|
||||
@@ -291,7 +296,6 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
iter_->SeekToFirst();
|
||||
c_iter_.reset(new CompactionIterator(
|
||||
iter_.get(), cmp_, merge_helper_.get(), last_sequence, &snapshots_,
|
||||
snapshots_.empty() ? kMaxSequenceNumber : snapshots_.at(0),
|
||||
earliest_write_conflict_snapshot, kMaxSequenceNumber,
|
||||
snapshot_checker_.get(), Env::Default(),
|
||||
false /* report_detailed_time */, false, range_del_agg_.get(),
|
||||
@@ -828,15 +832,124 @@ TEST_P(CompactionIteratorTest, ConvertToPutAtBottom) {
|
||||
true /*bottomost_level*/);
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTest, ZeroSeqOfKeyAndSnapshot) {
|
||||
AddSnapshot(0);
|
||||
const std::vector<std::string> input_keys = {
|
||||
test::KeyStr("a", 0, kTypeValue), test::KeyStr("b", 0, kTypeValue)};
|
||||
const std::vector<std::string> input_values = {"a1", "b1"};
|
||||
RunTest(input_keys, input_values, input_keys, input_values);
|
||||
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();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CompactionIteratorTestInstance, CompactionIteratorTest,
|
||||
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.
|
||||
@@ -1732,22 +1845,6 @@ TEST_P(CompactionIteratorTsGcTest, SingleDeleteAllKeysOlderThanThreshold) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTsGcTest, ZeroSeqOfKeyAndSnapshot) {
|
||||
AddSnapshot(0);
|
||||
std::string full_history_ts_low;
|
||||
PutFixed64(&full_history_ts_low, std::numeric_limits<uint64_t>::max());
|
||||
const std::vector<std::string> input_keys = {
|
||||
test::KeyStr(101, "a", 0, kTypeValue),
|
||||
test::KeyStr(102, "b", 0, kTypeValue)};
|
||||
const std::vector<std::string> input_values = {"a1", "b1"};
|
||||
RunTest(input_keys, input_values, input_keys, input_values,
|
||||
/*last_committed_seq=*/kMaxSequenceNumber,
|
||||
/*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
|
||||
/*bottommost_level=*/false,
|
||||
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
|
||||
/*key_not_exists_beyond_output_level=*/false, &full_history_ts_low);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CompactionIteratorTsGcTestInstance,
|
||||
CompactionIteratorTsGcTest,
|
||||
testing::Values(true, false));
|
||||
|
||||
+254
-478
File diff suppressed because it is too large.
Load diff
+63
-105
@@ -67,7 +67,7 @@ class SubcompactionState;
|
||||
// if needed.
|
||||
//
|
||||
// CompactionJob has 2 main stats:
|
||||
// 1. CompactionJobStats job_stats_
|
||||
// 1. CompactionJobStats compaction_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 |
|
||||
// | job_stats +-----+ | |
|
||||
// | compaction_job_stats +-----+ | |
|
||||
// | | +--------->| compaction_job_stats |
|
||||
// | | | | |
|
||||
// +------------------------+ | +------------------------+
|
||||
@@ -98,13 +98,16 @@ class SubcompactionState;
|
||||
// +--------->+ |
|
||||
// +------------------------+
|
||||
//
|
||||
// 2. CompactionStatsFull internal_stats_
|
||||
// 2. CompactionStatsFull compaction_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, ordinary output level stats and the proximal level output
|
||||
// stats.
|
||||
// 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.
|
||||
// +---------------------------+
|
||||
// | SubcompactionState |
|
||||
// | |
|
||||
@@ -116,15 +119,15 @@ class SubcompactionState;
|
||||
// | | |
|
||||
// | | +----------------------+ |
|
||||
// +--------------------------------+ | | | CompactionOutputs | |
|
||||
// | CompactionJob | | | | (proximal_level) | |
|
||||
// | CompactionJob | | | | (penultimate_level) | |
|
||||
// | | +--------->| stats_ | |
|
||||
// | internal_stats_ | | | | +----------------------+ |
|
||||
// | compaction_stats_ | | | | +----------------------+ |
|
||||
// | +-------------------------+ | | | | |
|
||||
// | |output_level_stats |------|----+ +---------------------------+
|
||||
// | |stats (normal) |------|----+ +---------------------------+
|
||||
// | +-------------------------+ | | |
|
||||
// | | | |
|
||||
// | +-------------------------+ | | | +---------------------------+
|
||||
// | |proximal_level_stats |------+ | | SubcompactionState |
|
||||
// | |penultimate_level_stats +------+ | | SubcompactionState |
|
||||
// | +-------------------------+ | | | | |
|
||||
// | | | | | +----------------------+ |
|
||||
// | | | | | | CompactionOutputs | |
|
||||
@@ -134,7 +137,7 @@ class SubcompactionState;
|
||||
// | | |
|
||||
// | | +----------------------+ |
|
||||
// | | | CompactionOutputs | |
|
||||
// | | | (proximal_level) | |
|
||||
// | | | (penultimate_level) | |
|
||||
// +--------->| stats_ | |
|
||||
// | +----------------------+ |
|
||||
// | |
|
||||
@@ -173,13 +176,7 @@ class CompactionJob {
|
||||
|
||||
// REQUIRED: mutex held
|
||||
// Prepare for the compaction by setting up boundaries for each subcompaction
|
||||
// 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);
|
||||
|
||||
void Prepare();
|
||||
// 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
|
||||
@@ -190,24 +187,38 @@ 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(bool* compaction_released);
|
||||
Status Install(const MutableCFOptions& mutable_cf_options,
|
||||
bool* compaction_released);
|
||||
|
||||
// Return the IO status
|
||||
IOStatus io_status() const { return io_status_; }
|
||||
|
||||
protected:
|
||||
void UpdateCompactionJobOutputStats(
|
||||
const InternalStats::CompactionStatsFull& internal_stats) const;
|
||||
|
||||
// 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);
|
||||
void LogCompaction();
|
||||
virtual void RecordCompactionIOStats();
|
||||
void CleanupCompaction();
|
||||
|
||||
// Iterate through input and compact the kv-pairs.
|
||||
// Call compaction filter. Then iterate through input and compact the
|
||||
// kv-pairs
|
||||
void ProcessKeyValueCompaction(SubcompactionState* sub_compact);
|
||||
|
||||
CompactionState* compact_;
|
||||
InternalStats::CompactionStatsFull internal_stats_;
|
||||
InternalStats::CompactionStatsFull compaction_stats_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
const MutableDBOptions mutable_db_options_copy_;
|
||||
LogBuffer* log_buffer_;
|
||||
@@ -220,37 +231,11 @@ class CompactionJob {
|
||||
|
||||
IOStatus io_status_;
|
||||
|
||||
CompactionJobStats* job_stats_;
|
||||
CompactionJobStats* compaction_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
|
||||
@@ -290,10 +275,12 @@ class CompactionJob {
|
||||
const Slice& next_table_min_key,
|
||||
const Slice* comp_start_user_key,
|
||||
const Slice* comp_end_user_key);
|
||||
Status InstallCompactionResults(bool* compaction_released);
|
||||
Status InstallCompactionResults(const MutableCFOptions& mutable_cf_options,
|
||||
bool* compaction_released);
|
||||
Status OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
CompactionOutputs& outputs);
|
||||
|
||||
void UpdateCompactionJobStats(
|
||||
const InternalStats::CompactionStats& stats) const;
|
||||
void RecordDroppedKeys(const CompactionIterationStats& c_iter_stats,
|
||||
CompactionJobStats* compaction_job_stats = nullptr);
|
||||
|
||||
@@ -327,8 +314,6 @@ class CompactionJob {
|
||||
// deleted because that version is not visible in any snapshot.
|
||||
std::vector<SequenceNumber> existing_snapshots_;
|
||||
|
||||
SequenceNumber earliest_snapshot_;
|
||||
|
||||
// This is the earliest snapshot that could be used for write-conflict
|
||||
// checking by a transaction. For any user-key newer than this snapshot, we
|
||||
// should make sure not to remove evidence that a write occurred.
|
||||
@@ -366,18 +351,15 @@ class CompactionJob {
|
||||
// it also collects the smallest_seqno -> oldest_ancester_time from the SST.
|
||||
SeqnoToTimeMapping seqno_to_time_mapping_;
|
||||
|
||||
// Max seqno that can be zeroed out in last level, including for preserving
|
||||
// write times.
|
||||
SequenceNumber preserve_seqno_after_ = kMaxSequenceNumber;
|
||||
// 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;
|
||||
|
||||
// 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 proximal level).
|
||||
SequenceNumber proximal_after_seqno_ = kMaxSequenceNumber;
|
||||
|
||||
// Options File Number used for Remote Compaction
|
||||
// Setting this requires DBMutex.
|
||||
uint64_t options_file_number_ = 0;
|
||||
// the last level (output to penultimate level).
|
||||
SequenceNumber preclude_last_level_min_seqno_ = kMaxSequenceNumber;
|
||||
|
||||
// Get table file name in where it's outputting to, which should also be in
|
||||
// `output_directory_`.
|
||||
@@ -393,7 +375,9 @@ class CompactionJob {
|
||||
// doesn't contain the LSM tree information, which is passed though MANIFEST
|
||||
// file.
|
||||
struct CompactionServiceInput {
|
||||
std::string cf_name;
|
||||
ColumnFamilyDescriptor column_family;
|
||||
|
||||
DBOptions db_options;
|
||||
|
||||
std::vector<SequenceNumber> snapshots;
|
||||
|
||||
@@ -401,7 +385,7 @@ struct CompactionServiceInput {
|
||||
// files needed for this compaction, for both input level files and output
|
||||
// level files.
|
||||
std::vector<std::string> input_files;
|
||||
int output_level = 0;
|
||||
int output_level;
|
||||
|
||||
// db_id is used to generate unique id of sst on the remote compactor
|
||||
std::string db_id;
|
||||
@@ -412,12 +396,13 @@ struct CompactionServiceInput {
|
||||
bool has_end = false;
|
||||
std::string end;
|
||||
|
||||
uint64_t options_file_number = 0;
|
||||
|
||||
// serialization interface to read and write the object
|
||||
static Status Read(const std::string& data_str, CompactionServiceInput* obj);
|
||||
Status Write(std::string* output);
|
||||
|
||||
// Initialize a dummy ColumnFamilyDescriptor
|
||||
CompactionServiceInput() : column_family("", ColumnFamilyOptions()) {}
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool TEST_Equals(CompactionServiceInput* other);
|
||||
bool TEST_Equals(CompactionServiceInput* other, std::string* mismatch);
|
||||
@@ -431,28 +416,20 @@ struct CompactionServiceOutputFile {
|
||||
SequenceNumber largest_seqno;
|
||||
std::string smallest_internal_key;
|
||||
std::string largest_internal_key;
|
||||
uint64_t oldest_ancester_time = kUnknownOldestAncesterTime;
|
||||
uint64_t file_creation_time = kUnknownFileCreationTime;
|
||||
uint64_t epoch_number = kUnknownEpochNumber;
|
||||
std::string file_checksum = kUnknownFileChecksum;
|
||||
std::string file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
uint64_t oldest_ancester_time;
|
||||
uint64_t file_creation_time;
|
||||
uint64_t epoch_number;
|
||||
uint64_t paranoid_hash;
|
||||
bool marked_for_compaction;
|
||||
UniqueId64x2 unique_id{};
|
||||
TableProperties table_properties;
|
||||
bool is_proximal_level_output;
|
||||
Temperature file_temperature;
|
||||
UniqueId64x2 unique_id;
|
||||
|
||||
CompactionServiceOutputFile() = default;
|
||||
CompactionServiceOutputFile(
|
||||
const std::string& name, SequenceNumber smallest, SequenceNumber largest,
|
||||
std::string _smallest_internal_key, std::string _largest_internal_key,
|
||||
uint64_t _oldest_ancester_time, uint64_t _file_creation_time,
|
||||
uint64_t _epoch_number, const std::string& _file_checksum,
|
||||
const std::string& _file_checksum_func_name, uint64_t _paranoid_hash,
|
||||
bool _marked_for_compaction, UniqueId64x2 _unique_id,
|
||||
const TableProperties& _table_properties, bool _is_proximal_level_output,
|
||||
Temperature _file_temperature)
|
||||
uint64_t _epoch_number, uint64_t _paranoid_hash,
|
||||
bool _marked_for_compaction, UniqueId64x2 _unique_id)
|
||||
: file_name(name),
|
||||
smallest_seqno(smallest),
|
||||
largest_seqno(largest),
|
||||
@@ -461,14 +438,9 @@ struct CompactionServiceOutputFile {
|
||||
oldest_ancester_time(_oldest_ancester_time),
|
||||
file_creation_time(_file_creation_time),
|
||||
epoch_number(_epoch_number),
|
||||
file_checksum(_file_checksum),
|
||||
file_checksum_func_name(_file_checksum_func_name),
|
||||
paranoid_hash(_paranoid_hash),
|
||||
marked_for_compaction(_marked_for_compaction),
|
||||
unique_id(std::move(_unique_id)),
|
||||
table_properties(_table_properties),
|
||||
is_proximal_level_output(_is_proximal_level_output),
|
||||
file_temperature(_file_temperature) {}
|
||||
unique_id(std::move(_unique_id)) {}
|
||||
};
|
||||
|
||||
// CompactionServiceResult contains the compaction result from a different db
|
||||
@@ -477,28 +449,18 @@ struct CompactionServiceOutputFile {
|
||||
struct CompactionServiceResult {
|
||||
Status status;
|
||||
std::vector<CompactionServiceOutputFile> output_files;
|
||||
int output_level = 0;
|
||||
int output_level;
|
||||
|
||||
// location of the output files
|
||||
std::string output_path;
|
||||
|
||||
// some statistics about the compaction
|
||||
uint64_t num_output_records = 0;
|
||||
uint64_t total_bytes = 0;
|
||||
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);
|
||||
@@ -530,10 +492,6 @@ 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();
|
||||
|
||||
|
||||
@@ -959,6 +959,7 @@ int main(int argc, char** argv) {
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) { return 0; }
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// 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>
|
||||
@@ -49,8 +50,7 @@ void VerifyInitializationOfCompactionJobStats(
|
||||
ASSERT_EQ(compaction_job_stats.num_output_records, 0U);
|
||||
ASSERT_EQ(compaction_job_stats.num_output_files, 0U);
|
||||
|
||||
ASSERT_TRUE(compaction_job_stats.is_manual_compaction);
|
||||
ASSERT_FALSE(compaction_job_stats.is_remote_compaction);
|
||||
ASSERT_EQ(compaction_job_stats.is_manual_compaction, true);
|
||||
|
||||
ASSERT_EQ(compaction_job_stats.total_input_bytes, 0U);
|
||||
ASSERT_EQ(compaction_job_stats.total_output_bytes, 0U);
|
||||
@@ -232,11 +232,6 @@ class CompactionJobTestBase : public testing::Test {
|
||||
// set default for the tests
|
||||
mutable_cf_options_.target_file_size_base = 1024 * 1024;
|
||||
mutable_cf_options_.max_compaction_bytes = 10 * 1024 * 1024;
|
||||
|
||||
// Turn off compaction_verify_record_count MockTables
|
||||
if (table_type == TableTypeForTest::kMockTable) {
|
||||
db_options_.compaction_verify_record_count = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
@@ -254,7 +249,6 @@ class CompactionJobTestBase : public testing::Test {
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
mutable_cf_options_.table_factory = cf_options_.table_factory;
|
||||
}
|
||||
|
||||
std::string GenerateFileName(uint64_t file_number) {
|
||||
@@ -305,13 +299,13 @@ class CompactionJobTestBase : public testing::Test {
|
||||
const WriteOptions write_options;
|
||||
std::unique_ptr<TableBuilder> table_builder(
|
||||
cf_options_.table_factory->NewTableBuilder(
|
||||
TableBuilderOptions(
|
||||
cfd_->ioptions(), mutable_cf_options_, read_options,
|
||||
write_options, cfd_->internal_comparator(),
|
||||
cfd_->internal_tbl_prop_coll_factories(),
|
||||
CompressionType::kNoCompression, CompressionOptions(),
|
||||
0 /* column_family_id */, kDefaultColumnFamilyName,
|
||||
-1 /* level */, kUnknownNewestKeyTime),
|
||||
TableBuilderOptions(*cfd_->ioptions(), mutable_cf_options_,
|
||||
read_options, write_options,
|
||||
cfd_->internal_comparator(),
|
||||
cfd_->internal_tbl_prop_coll_factories(),
|
||||
CompressionType::kNoCompression,
|
||||
CompressionOptions(), 0 /* column_family_id */,
|
||||
kDefaultColumnFamilyName, -1 /* level */),
|
||||
file_writer.get()));
|
||||
// Build table.
|
||||
for (const auto& kv : contents) {
|
||||
@@ -402,8 +396,8 @@ class CompactionJobTestBase : public testing::Test {
|
||||
|
||||
mutex_.Lock();
|
||||
EXPECT_OK(versions_->LogAndApply(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), read_options_,
|
||||
write_options_, &edit, &mutex_, nullptr));
|
||||
versions_->GetColumnFamilySet()->GetDefault(), mutable_cf_options_,
|
||||
read_options_, write_options_, &edit, &mutex_, nullptr));
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
@@ -465,7 +459,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);
|
||||
@@ -551,14 +545,14 @@ class CompactionJobTestBase : public testing::Test {
|
||||
ASSERT_OK(s);
|
||||
db_options_.info_log = info_log;
|
||||
|
||||
versions_.reset(
|
||||
new VersionSet(dbname_, &db_options_, env_options_, table_cache_.get(),
|
||||
&write_buffer_manager_, &write_controller_,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
|
||||
test::kUnitTestDbId, /*db_session_id=*/"",
|
||||
/*daily_offpeak_time_utc=*/"",
|
||||
/*error_handler=*/nullptr, /*read_only=*/false));
|
||||
versions_.reset(new VersionSet(
|
||||
dbname_, &db_options_, env_options_, table_cache_.get(),
|
||||
&write_buffer_manager_, &write_controller_,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
|
||||
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
|
||||
/*error_handler=*/nullptr, /*read_only=*/false));
|
||||
compaction_job_stats_.Reset();
|
||||
ASSERT_OK(SetIdentityFile(WriteOptions(), env_, dbname_));
|
||||
|
||||
VersionEdit new_db;
|
||||
new_db.SetLogNumber(0);
|
||||
@@ -581,8 +575,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
// Make "CURRENT" file that points to the new manifest file.
|
||||
s = SetCurrentFile(WriteOptions(), fs_.get(), dbname_, 1,
|
||||
Temperature::kUnknown, nullptr);
|
||||
s = SetCurrentFile(WriteOptions(), fs_.get(), dbname_, 1, nullptr);
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
@@ -650,14 +643,13 @@ 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,
|
||||
Temperature::kUnknown, max_subcompactions, grandparents,
|
||||
/*earliest_snapshot*/ std::nullopt, /*snapshot_checker*/ nullptr, true);
|
||||
cfd->GetLatestMutableCFOptions()->compression_opts,
|
||||
Temperature::kUnknown, max_subcompactions, grandparents, true);
|
||||
compaction.FinalizeInputInfo(cfd->current());
|
||||
|
||||
assert(db_options_.info_log);
|
||||
@@ -682,14 +674,15 @@ class CompactionJobTestBase : public testing::Test {
|
||||
full_history_ts_low_);
|
||||
VerifyInitializationOfCompactionJobStats(compaction_job_stats_);
|
||||
|
||||
compaction_job.Prepare(std::nullopt /*subcompact to be computed*/);
|
||||
compaction_job.Prepare();
|
||||
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(&compaction_released));
|
||||
ASSERT_OK(compaction_job.Install(*cfd->GetLatestMutableCFOptions(),
|
||||
&compaction_released));
|
||||
ASSERT_OK(compaction_job.io_status());
|
||||
mutex_.Unlock();
|
||||
log_buffer.FlushBufferToLog();
|
||||
@@ -1479,7 +1472,7 @@ TEST_F(CompactionJobTest, OldestBlobFileNumber) {
|
||||
/* expected_oldest_blob_file_numbers */ {19});
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, VerifyProximalLevelOutput) {
|
||||
TEST_F(CompactionJobTest, VerifyPenultimateLevelOutput) {
|
||||
cf_options_.last_level_temperature = Temperature::kCold;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"Compaction::SupportsPerKeyPlacement:Enabled", [&](void* arg) {
|
||||
@@ -1492,7 +1485,8 @@ TEST_F(CompactionJobTest, VerifyProximalLevelOutput) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
|
||||
auto context = static_cast<PerKeyPlacementContext*>(arg);
|
||||
context->output_to_proximal_level = context->seq_num > latest_cold_seq;
|
||||
context->output_to_penultimate_level =
|
||||
context->seq_num > latest_cold_seq;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -1538,11 +1532,14 @@ TEST_F(CompactionJobTest, VerifyProximalLevelOutput) {
|
||||
/*verify_func=*/[&](Compaction& comp) {
|
||||
for (char c = 'a'; c <= 'z'; c++) {
|
||||
if (c == 'a') {
|
||||
comp.TEST_AssertWithinProximalLevelOutputRange(
|
||||
"a", true /*expect_failure*/);
|
||||
ParsedInternalKey pik("a", 0U, kTypeValue);
|
||||
ASSERT_FALSE(comp.WithinPenultimateLevelOutputRange(pik));
|
||||
} else {
|
||||
std::string c_str{c};
|
||||
comp.TEST_AssertWithinProximalLevelOutputRange(c_str);
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1570,7 +1567,17 @@ TEST_F(CompactionJobTest, InputSerialization) {
|
||||
const int kStrMaxLen = 1000;
|
||||
Random rnd(static_cast<uint32_t>(time(nullptr)));
|
||||
Random64 rnd64(time(nullptr));
|
||||
input.cf_name = rnd.RandomString(rnd.Uniform(kStrMaxLen));
|
||||
input.column_family.name = rnd.RandomString(rnd.Uniform(kStrMaxLen));
|
||||
input.column_family.options.comparator = ReverseBytewiseComparator();
|
||||
input.column_family.options.max_bytes_for_level_base =
|
||||
rnd64.Uniform(UINT64_MAX);
|
||||
input.column_family.options.disable_auto_compactions = rnd.OneIn(2);
|
||||
input.column_family.options.compression = kZSTD;
|
||||
input.column_family.options.compression_opts.level = 4;
|
||||
input.db_options.max_background_flushes = 10;
|
||||
input.db_options.paranoid_checks = rnd.OneIn(2);
|
||||
input.db_options.statistics = CreateDBStatistics();
|
||||
input.db_options.env = env_;
|
||||
while (!rnd.OneIn(10)) {
|
||||
input.snapshots.emplace_back(rnd64.Uniform(UINT64_MAX));
|
||||
}
|
||||
@@ -1598,10 +1605,10 @@ TEST_F(CompactionJobTest, InputSerialization) {
|
||||
ASSERT_TRUE(deserialized1.TEST_Equals(&input));
|
||||
|
||||
// Test mismatch
|
||||
deserialized1.output_level += 10;
|
||||
deserialized1.db_options.max_background_flushes += 10;
|
||||
std::string mismatch;
|
||||
ASSERT_FALSE(deserialized1.TEST_Equals(&input, &mismatch));
|
||||
ASSERT_EQ(mismatch, "output_level");
|
||||
ASSERT_EQ(mismatch, "db_options.max_background_flushes");
|
||||
|
||||
// Test unknown field
|
||||
CompactionServiceInput deserialized2;
|
||||
@@ -1657,41 +1664,20 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
};
|
||||
result.status =
|
||||
status_list.at(rnd.Uniform(static_cast<int>(status_list.size())));
|
||||
|
||||
std::string file_checksum = rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen));
|
||||
std::string file_checksum_func_name = "MyAwesomeChecksumGenerator";
|
||||
while (!rnd.OneIn(10)) {
|
||||
TableProperties tp;
|
||||
tp.user_collected_properties.emplace(
|
||||
"UCP_Key1", rnd.RandomString(rnd.Uniform(kStrMaxLen)));
|
||||
tp.user_collected_properties.emplace(
|
||||
"UCP_Key2", rnd.RandomString(rnd.Uniform(kStrMaxLen)));
|
||||
tp.readable_properties.emplace("RP_Key1",
|
||||
rnd.RandomString(rnd.Uniform(kStrMaxLen)));
|
||||
tp.readable_properties.emplace("RP_K2y2",
|
||||
rnd.RandomString(rnd.Uniform(kStrMaxLen)));
|
||||
|
||||
UniqueId64x2 id{rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX)};
|
||||
result.output_files.emplace_back(
|
||||
rnd.RandomString(rnd.Uniform(kStrMaxLen)) /* file_name */,
|
||||
rnd64.Uniform(UINT64_MAX) /* smallest_seqno */,
|
||||
rnd64.Uniform(UINT64_MAX) /* largest_seqno */,
|
||||
rnd.RandomBinaryString(
|
||||
rnd.Uniform(kStrMaxLen)) /* smallest_internal_key */,
|
||||
rnd.RandomBinaryString(
|
||||
rnd.Uniform(kStrMaxLen)) /* largest_internal_key */,
|
||||
rnd64.Uniform(UINT64_MAX) /* oldest_ancester_time */,
|
||||
rnd64.Uniform(UINT64_MAX) /* file_creation_time */,
|
||||
rnd64.Uniform(UINT64_MAX) /* epoch_number */,
|
||||
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,
|
||||
false /* is_proximal_level_output */, Temperature::kHot);
|
||||
rnd.RandomString(rnd.Uniform(kStrMaxLen)), rnd64.Uniform(UINT64_MAX),
|
||||
rnd64.Uniform(UINT64_MAX),
|
||||
rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen)),
|
||||
rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen)),
|
||||
rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX),
|
||||
rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX), rnd.OneIn(2), id);
|
||||
}
|
||||
result.output_level = rnd.Uniform(10);
|
||||
result.output_path = rnd.RandomString(rnd.Uniform(kStrMaxLen));
|
||||
result.stats.num_output_records = rnd64.Uniform(UINT64_MAX);
|
||||
result.num_output_records = rnd64.Uniform(UINT64_MAX);
|
||||
result.total_bytes = rnd64.Uniform(UINT64_MAX);
|
||||
result.bytes_read = 123;
|
||||
result.bytes_written = rnd64.Uniform(UINT64_MAX);
|
||||
result.stats.elapsed_micros = rnd64.Uniform(UINT64_MAX);
|
||||
@@ -1708,21 +1694,6 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
ASSERT_OK(CompactionServiceResult::Read(output, &deserialized1));
|
||||
ASSERT_TRUE(deserialized1.TEST_Equals(&result));
|
||||
|
||||
for (size_t i = 0; i < result.output_files.size(); i++) {
|
||||
for (const auto& prop :
|
||||
result.output_files[i].table_properties.user_collected_properties) {
|
||||
ASSERT_EQ(deserialized1.output_files[i]
|
||||
.table_properties.user_collected_properties[prop.first],
|
||||
prop.second);
|
||||
}
|
||||
for (const auto& prop :
|
||||
result.output_files[i].table_properties.readable_properties) {
|
||||
ASSERT_EQ(deserialized1.output_files[i]
|
||||
.table_properties.readable_properties[prop.first],
|
||||
prop.second);
|
||||
}
|
||||
}
|
||||
|
||||
// Test mismatch
|
||||
deserialized1.stats.num_input_files += 10;
|
||||
std::string mismatch;
|
||||
@@ -1737,12 +1708,6 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
ASSERT_FALSE(deserialized_tmp.TEST_Equals(&result, &mismatch));
|
||||
ASSERT_EQ(mismatch, "output_files.unique_id");
|
||||
deserialized_tmp.status.PermitUncheckedError();
|
||||
|
||||
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
|
||||
@@ -1902,8 +1867,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) {
|
||||
@@ -1977,8 +1942,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) {
|
||||
@@ -2039,8 +2004,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 = static_cast<int>(outputs_.size());
|
||||
stats_.num_output_files = 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,11 +455,9 @@ 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
|
||||
@@ -472,7 +470,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(!range_del_agg.IsEmpty());
|
||||
assert(HasRangeDel());
|
||||
FileMetaData& meta = current_output().meta;
|
||||
const Comparator* ucmp = icmp.user_comparator();
|
||||
InternalKey lower_bound_buf, upper_bound_buf;
|
||||
@@ -581,19 +579,13 @@ 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
|
||||
@@ -752,10 +744,11 @@ 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;
|
||||
}
|
||||
@@ -763,19 +756,20 @@ 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) {
|
||||
@@ -785,15 +779,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_proximal_level)
|
||||
: compaction_(compaction), is_proximal_level_(is_proximal_level) {
|
||||
const bool is_penultimate_level)
|
||||
: compaction_(compaction), is_penultimate_level_(is_penultimate_level) {
|
||||
partitioner_ = compaction->output_level() == 0
|
||||
? nullptr
|
||||
: compaction->CreateSstPartitioner();
|
||||
|
||||
@@ -30,32 +30,27 @@ class CompactionOutputs {
|
||||
// compaction output file
|
||||
struct Output {
|
||||
Output(FileMetaData&& _meta, const InternalKeyComparator& _icmp,
|
||||
bool _enable_hash, bool _finished, uint64_t precalculated_hash,
|
||||
bool _is_proximal_level)
|
||||
bool _enable_hash, bool _finished, uint64_t precalculated_hash)
|
||||
: meta(std::move(_meta)),
|
||||
validator(_icmp, _enable_hash, precalculated_hash),
|
||||
finished(_finished),
|
||||
is_proximal_level(_is_proximal_level) {}
|
||||
finished(_finished) {}
|
||||
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_proximal_level);
|
||||
|
||||
bool IsProximalLevel() const { return is_proximal_level_; }
|
||||
const bool is_penultimate_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, is_proximal_level_);
|
||||
precalculated_hash);
|
||||
}
|
||||
|
||||
// Set new table builder for the current output
|
||||
@@ -66,29 +61,33 @@ class CompactionOutputs {
|
||||
file_writer_.reset(writer);
|
||||
}
|
||||
|
||||
// TODO: Remove it when remote compaction support tiered compaction
|
||||
void SetTotalBytes(uint64_t bytes) { stats_.bytes_written += bytes; }
|
||||
void SetNumOutputRecords(uint64_t num) { stats_.num_output_records = num; }
|
||||
|
||||
// TODO: Move the BlobDB builder into CompactionOutputs
|
||||
const std::vector<BlobFileAddition>& GetBlobFileAdditions() const {
|
||||
if (is_proximal_level_) {
|
||||
if (is_penultimate_level_) {
|
||||
assert(blob_file_additions_.empty());
|
||||
}
|
||||
return blob_file_additions_;
|
||||
}
|
||||
|
||||
std::vector<BlobFileAddition>* GetBlobFileAdditionsPtr() {
|
||||
assert(!is_proximal_level_);
|
||||
assert(!is_penultimate_level_);
|
||||
return &blob_file_additions_;
|
||||
}
|
||||
|
||||
bool HasBlobFileAdditions() const { return !blob_file_additions_.empty(); }
|
||||
|
||||
BlobGarbageMeter* CreateBlobGarbageMeter() {
|
||||
assert(!is_proximal_level_);
|
||||
assert(!is_penultimate_level_);
|
||||
blob_garbage_meter_ = std::make_unique<BlobGarbageMeter>();
|
||||
return blob_garbage_meter_.get();
|
||||
}
|
||||
|
||||
BlobGarbageMeter* GetBlobGarbageMeter() const {
|
||||
if (is_proximal_level_) {
|
||||
if (is_penultimate_level_) {
|
||||
// blobdb doesn't support per_key_placement yet
|
||||
assert(blob_garbage_meter_ == nullptr);
|
||||
return nullptr;
|
||||
@@ -97,9 +96,8 @@ class CompactionOutputs {
|
||||
}
|
||||
|
||||
void UpdateBlobStats() {
|
||||
assert(!is_proximal_level_);
|
||||
stats_.num_output_files_blob =
|
||||
static_cast<int>(blob_file_additions_.size());
|
||||
assert(!is_penultimate_level_);
|
||||
stats_.num_output_files_blob = blob_file_additions_.size();
|
||||
for (const auto& blob : blob_file_additions_) {
|
||||
stats_.bytes_written_blob += blob.GetTotalBlobBytes();
|
||||
}
|
||||
@@ -109,12 +107,6 @@ class CompactionOutputs {
|
||||
Status Finish(const Status& intput_status,
|
||||
const SeqnoToTimeMapping& seqno_to_time_mapping);
|
||||
|
||||
// Update output table properties from already populated TableProperties.
|
||||
// Used for remote compaction
|
||||
void UpdateTableProperties(const TableProperties& table_properties) {
|
||||
current_output().table_properties =
|
||||
std::make_shared<TableProperties>(table_properties);
|
||||
}
|
||||
// Update output table properties from table builder
|
||||
void UpdateTableProperties() {
|
||||
current_output().table_properties =
|
||||
@@ -183,13 +175,18 @@ 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(
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class SubcompactionState;
|
||||
@@ -255,15 +252,11 @@ 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. 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()) {
|
||||
// handle subcompaction containing only range deletions
|
||||
if (status.ok() && !HasBuilder() && !HasOutput() && HasRangeDel()) {
|
||||
status = open_file_func(*this);
|
||||
}
|
||||
if (HasBuilder()) {
|
||||
@@ -288,6 +281,16 @@ 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
|
||||
@@ -303,12 +306,13 @@ class CompactionOutputs {
|
||||
std::vector<BlobFileAddition> blob_file_additions_;
|
||||
std::unique_ptr<BlobGarbageMeter> blob_garbage_meter_;
|
||||
|
||||
// Per level's output stat
|
||||
InternalStats::CompactionStats stats_;
|
||||
// Basic compaction output stats for this level's outputs
|
||||
InternalStats::CompactionOutputsStats stats_;
|
||||
|
||||
// indicate if this CompactionOutputs obj for proximal_level, should always
|
||||
// indicate if this CompactionOutputs obj for penultimate_level, should always
|
||||
// be false if per_key_placement feature is not enabled.
|
||||
const bool is_proximal_level_;
|
||||
const bool is_penultimate_level_;
|
||||
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg_ = nullptr;
|
||||
|
||||
// partitioner information
|
||||
std::string last_key_for_partitioner_;
|
||||
@@ -362,7 +366,7 @@ class CompactionOutputs {
|
||||
std::vector<size_t> level_ptrs_;
|
||||
};
|
||||
|
||||
// helper struct to concatenate the last level and proximal level outputs
|
||||
// helper struct to concatenate the last level and penultimate level outputs
|
||||
// which could be replaced by std::ranges::join_view() in c++20
|
||||
struct OutputIterator {
|
||||
public:
|
||||
|
||||
@@ -133,8 +133,7 @@ CompactionPicker::CompactionPicker(const ImmutableOptions& ioptions,
|
||||
CompactionPicker::~CompactionPicker() = default;
|
||||
|
||||
// Delete this compaction from the list of running compactions.
|
||||
void CompactionPicker::ReleaseCompactionFiles(Compaction* c,
|
||||
const Status& status) {
|
||||
void CompactionPicker::ReleaseCompactionFiles(Compaction* c, Status status) {
|
||||
UnregisterCompaction(c);
|
||||
if (!status.ok()) {
|
||||
c->ResetNextCompactionIndex();
|
||||
@@ -272,8 +271,8 @@ bool CompactionPicker::RangeOverlapWithCompaction(
|
||||
return true;
|
||||
}
|
||||
if (c->SupportsPerKeyPlacement()) {
|
||||
if (c->OverlapProximalLevelOutputRange(smallest_user_key,
|
||||
largest_user_key)) {
|
||||
if (c->OverlapPenultimateLevelOutputRange(smallest_user_key,
|
||||
largest_user_key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -284,7 +283,7 @@ bool CompactionPicker::RangeOverlapWithCompaction(
|
||||
|
||||
bool CompactionPicker::FilesRangeOverlapWithCompaction(
|
||||
const std::vector<CompactionInputFiles>& inputs, int level,
|
||||
int proximal_level) const {
|
||||
int penultimate_level) const {
|
||||
bool is_empty = true;
|
||||
for (auto& in : inputs) {
|
||||
if (!in.empty()) {
|
||||
@@ -301,18 +300,18 @@ bool CompactionPicker::FilesRangeOverlapWithCompaction(
|
||||
// files cannot be overlapped in the order of L0 files.
|
||||
InternalKey smallest, largest;
|
||||
GetRange(inputs, &smallest, &largest, Compaction::kInvalidLevel);
|
||||
if (proximal_level != Compaction::kInvalidLevel) {
|
||||
if (penultimate_level != Compaction::kInvalidLevel) {
|
||||
if (ioptions_.compaction_style == kCompactionStyleUniversal) {
|
||||
if (RangeOverlapWithCompaction(smallest.user_key(), largest.user_key(),
|
||||
proximal_level)) {
|
||||
penultimate_level)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
InternalKey proximal_smallest, proximal_largest;
|
||||
GetRange(inputs, &proximal_smallest, &proximal_largest, level);
|
||||
if (RangeOverlapWithCompaction(proximal_smallest.user_key(),
|
||||
proximal_largest.user_key(),
|
||||
proximal_level)) {
|
||||
InternalKey penultimate_smallest, penultimate_largest;
|
||||
GetRange(inputs, &penultimate_smallest, &penultimate_largest, level);
|
||||
if (RangeOverlapWithCompaction(penultimate_smallest.user_key(),
|
||||
penultimate_largest.user_key(),
|
||||
penultimate_level)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -341,8 +340,8 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
#ifndef NDEBUG
|
||||
assert(input_files.size());
|
||||
// This compaction output should not overlap with a running compaction as
|
||||
// `SanitizeAndConvertCompactionInputFiles` should've checked earlier and db
|
||||
// mutex shouldn't have been released since.
|
||||
// `SanitizeCompactionInputFiles` should've checked earlier and db mutex
|
||||
// shouldn't have been released since.
|
||||
int start_level = Compaction::kInvalidLevel;
|
||||
for (const auto& in : input_files) {
|
||||
// input_files should already be sorted by level
|
||||
@@ -351,11 +350,11 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(output_level == 0 || !FilesRangeOverlapWithCompaction(
|
||||
input_files, output_level,
|
||||
Compaction::EvaluateProximalLevel(
|
||||
vstorage, mutable_cf_options, ioptions_,
|
||||
start_level, output_level)));
|
||||
assert(output_level == 0 ||
|
||||
!FilesRangeOverlapWithCompaction(
|
||||
input_files, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage, ioptions_,
|
||||
start_level, output_level)));
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
CompressionType compression_type;
|
||||
@@ -380,8 +379,7 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_options.max_subcompactions,
|
||||
/* grandparents */ {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr, true);
|
||||
/* grandparents */ {}, true);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
@@ -659,9 +657,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
// overlaping outputs in the same level.
|
||||
if (FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluateProximalLevel(vstorage, mutable_cf_options,
|
||||
ioptions_, start_level,
|
||||
output_level))) {
|
||||
Compaction::EvaluatePenultimateLevel(vstorage, 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;
|
||||
@@ -679,9 +676,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_range_options.max_subcompactions,
|
||||
/* grandparents */ {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ true, trim_ts, /* score */ -1,
|
||||
/* grandparents */ {}, /* is manual */ true, trim_ts, /* score */ -1,
|
||||
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kUnknown,
|
||||
compact_range_options.blob_garbage_collection_policy,
|
||||
@@ -721,14 +716,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;
|
||||
assert(!inputs.empty());
|
||||
// Always include first file for progress.
|
||||
uint64_t input_level_total = inputs[0]->fd.GetFileSize();
|
||||
InternalKey* smallest = &(inputs[0]->smallest);
|
||||
InternalKey* smallest = nullptr;
|
||||
InternalKey* largest = nullptr;
|
||||
for (size_t i = 1; i < inputs.size(); ++i) {
|
||||
// Consider whether to include inputs[i]
|
||||
for (size_t i = 0; i + 1 < inputs.size(); ++i) {
|
||||
if (!smallest) {
|
||||
smallest = &inputs[i]->smallest;
|
||||
}
|
||||
largest = &inputs[i]->largest;
|
||||
|
||||
uint64_t input_file_size = inputs[i]->fd.GetFileSize();
|
||||
@@ -744,11 +739,13 @@ Compaction* CompactionPicker::CompactRange(
|
||||
|
||||
input_level_total += input_file_size;
|
||||
|
||||
if (input_level_total + output_level_total > limit) {
|
||||
// To ensure compaction size is <= limit, leave out inputs from
|
||||
// index i onwards.
|
||||
if (input_level_total + output_level_total >= limit) {
|
||||
covering_the_whole_range = false;
|
||||
inputs.files.resize(i);
|
||||
// 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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -817,9 +814,6 @@ 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;
|
||||
@@ -848,9 +842,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
// overlaping outputs in the same level.
|
||||
if (FilesRangeOverlapWithCompaction(
|
||||
compaction_inputs, output_level,
|
||||
Compaction::EvaluateProximalLevel(vstorage, mutable_cf_options,
|
||||
ioptions_, input_level,
|
||||
output_level))) {
|
||||
Compaction::EvaluatePenultimateLevel(vstorage, 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;
|
||||
@@ -872,7 +865,6 @@ Compaction* CompactionPicker::CompactRange(
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_range_options.max_subcompactions, std::move(grandparents),
|
||||
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
|
||||
/* is manual */ true, trim_ts, /* score */ -1,
|
||||
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kUnknown,
|
||||
@@ -1048,20 +1040,19 @@ Status CompactionPicker::SanitizeCompactionInputFilesForAllLevels(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (RangeOverlapWithCompaction(smallestkey, largestkey, output_level)) {
|
||||
return Status::Aborted(
|
||||
"A running compaction is writing to the same output level in an "
|
||||
"overlapping key range");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
|
||||
std::unordered_set<uint64_t>* input_files, const int output_level,
|
||||
Version* version,
|
||||
std::vector<CompactionInputFiles>* converted_input_files) const {
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
version->GetColumnFamilyMetaData(&cf_meta);
|
||||
|
||||
Status CompactionPicker::SanitizeCompactionInputFiles(
|
||||
std::unordered_set<uint64_t>* input_files,
|
||||
const ColumnFamilyMetaData& cf_meta, const int output_level) const {
|
||||
assert(static_cast<int>(cf_meta.levels.size()) - 1 ==
|
||||
cf_meta.levels[cf_meta.levels.size() - 1].level);
|
||||
assert(converted_input_files);
|
||||
|
||||
if (output_level >= static_cast<int>(cf_meta.levels.size())) {
|
||||
return Status::InvalidArgument(
|
||||
"Output level for column family " + cf_meta.name +
|
||||
@@ -1087,6 +1078,7 @@ Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
|
||||
|
||||
Status s = SanitizeCompactionInputFilesForAllLevels(input_files, cf_meta,
|
||||
output_level);
|
||||
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -1127,23 +1119,6 @@ Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
|
||||
}
|
||||
}
|
||||
|
||||
s = GetCompactionInputsFromFileNumbers(converted_input_files, input_files,
|
||||
version->storage_info(),
|
||||
CompactionOptions());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
assert(converted_input_files->size() > 0);
|
||||
if (output_level != 0 &&
|
||||
FilesRangeOverlapWithCompaction(
|
||||
*converted_input_files, output_level,
|
||||
Compaction::EvaluateProximalLevel(
|
||||
version->storage_info(), version->GetMutableCFOptions(),
|
||||
ioptions_, (*converted_input_files)[0].level, output_level))) {
|
||||
return Status::Aborted(
|
||||
"A running compaction is writing to the same output level(s) in an "
|
||||
"overlapping key range");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -1154,7 +1129,7 @@ void CompactionPicker::RegisterCompaction(Compaction* c) {
|
||||
assert(ioptions_.compaction_style != kCompactionStyleLevel ||
|
||||
c->output_level() == 0 ||
|
||||
!FilesRangeOverlapWithCompaction(*c->inputs(), c->output_level(),
|
||||
c->GetProximalLevel()));
|
||||
c->GetPenultimateLevel()));
|
||||
// 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
|
||||
@@ -1181,8 +1156,7 @@ void CompactionPicker::UnregisterCompaction(Compaction* c) {
|
||||
|
||||
void CompactionPicker::PickFilesMarkedForCompaction(
|
||||
const std::string& cf_name, VersionStorageInfo* vstorage, int* start_level,
|
||||
int* output_level, CompactionInputFiles* start_level_inputs,
|
||||
std::function<bool(const FileMetaData*)> skip_marked_file) {
|
||||
int* output_level, CompactionInputFiles* start_level_inputs) {
|
||||
if (vstorage->FilesMarkedForCompaction().empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -1192,9 +1166,6 @@ void CompactionPicker::PickFilesMarkedForCompaction(
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
if (skip_marked_file(level_file.second)) {
|
||||
return false;
|
||||
}
|
||||
*start_level = level_file.first;
|
||||
*output_level =
|
||||
(*start_level == 0) ? vstorage->base_level() : *start_level + 1;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "db/version_set.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -56,25 +55,23 @@ class CompactionPicker {
|
||||
// Returns nullptr if there is no compaction to be done.
|
||||
// Otherwise returns a pointer to a heap-allocated object that
|
||||
// describes the compaction. Caller should delete the result.
|
||||
// Currently, only universal compaction will query existing snapshots and
|
||||
// pass it to aid compaction picking. And it's only passed when user-defined
|
||||
// timestamps is not enabled. The other compaction styles do not pass or use
|
||||
// `existing_snapshots` or `snapshot_checker`.
|
||||
virtual Compaction* PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) = 0;
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) = 0;
|
||||
|
||||
// Return a compaction object for compacting the range [begin,end] in
|
||||
// the specified level. Returns nullptr if there is nothing in that
|
||||
// level that overlaps the specified range. Caller should delete
|
||||
// the result.
|
||||
//
|
||||
// The returned Compaction might not include the whole requested range.
|
||||
// In that case, compaction_end will be set to the next key that needs
|
||||
// compacting. In case the compaction will compact the whole range,
|
||||
// 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,
|
||||
@@ -89,24 +86,21 @@ class CompactionPicker {
|
||||
|
||||
virtual bool NeedsCompaction(const VersionStorageInfo* vstorage) const = 0;
|
||||
|
||||
// Sanitize the input set of compaction input files and convert it to
|
||||
// `std::vector<CompactionInputFiles>` in the output parameter
|
||||
// `converted_input_files`.
|
||||
// When the input parameters do not describe a valid
|
||||
// compaction, the function will try to fix the input_files by adding
|
||||
// necessary files. If it's not possible to convert an invalid input_files
|
||||
// into a valid one by adding more files, the function will return a
|
||||
// non-ok status with specific reason.
|
||||
//
|
||||
Status SanitizeAndConvertCompactionInputFiles(
|
||||
std::unordered_set<uint64_t>* input_files, const int output_level,
|
||||
Version* version,
|
||||
std::vector<CompactionInputFiles>* converted_input_files) const;
|
||||
// Sanitize the input set of compaction input files.
|
||||
// When the input parameters do not describe a valid compaction, the
|
||||
// function will try to fix the input_files by adding necessary
|
||||
// files. If it's not possible to conver an invalid input_files
|
||||
// into a valid one by adding more files, the function will return a
|
||||
// non-ok status with specific reason.
|
||||
//
|
||||
Status SanitizeCompactionInputFiles(std::unordered_set<uint64_t>* input_files,
|
||||
const ColumnFamilyMetaData& cf_meta,
|
||||
const int output_level) const;
|
||||
|
||||
// Free up the files that participated in a compaction
|
||||
//
|
||||
// Requirement: DB mutex held
|
||||
void ReleaseCompactionFiles(Compaction* c, const Status& status);
|
||||
void ReleaseCompactionFiles(Compaction* c, Status status);
|
||||
|
||||
// Returns true if any one of the specified files are being compacted
|
||||
bool AreFilesInCompaction(const std::vector<FileMetaData*>& files);
|
||||
@@ -115,8 +109,8 @@ class CompactionPicker {
|
||||
// object.
|
||||
//
|
||||
// Caller must provide a set of input files that has been passed through
|
||||
// `SanitizeAndConvertCompactionInputFiles` earlier. The lock should not be
|
||||
// released between that call and this one.
|
||||
// `SanitizeCompactionInputFiles` earlier. The lock should not be released
|
||||
// between that call and this one.
|
||||
Compaction* CompactFiles(const CompactionOptions& compact_options,
|
||||
const std::vector<CompactionInputFiles>& input_files,
|
||||
int output_level, VersionStorageInfo* vstorage,
|
||||
@@ -126,7 +120,6 @@ class CompactionPicker {
|
||||
|
||||
// Converts a set of compaction input file numbers into
|
||||
// a list of CompactionInputFiles.
|
||||
// TODO(hx235): remove the unused paramter `compact_options`
|
||||
Status GetCompactionInputsFromFileNumbers(
|
||||
std::vector<CompactionInputFiles>* input_files,
|
||||
std::unordered_set<uint64_t>* input_set,
|
||||
@@ -138,12 +131,6 @@ 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,
|
||||
@@ -196,7 +183,7 @@ class CompactionPicker {
|
||||
// key range of a currently running compaction.
|
||||
bool FilesRangeOverlapWithCompaction(
|
||||
const std::vector<CompactionInputFiles>& inputs, int level,
|
||||
int proximal_level) const;
|
||||
int penultimate_level) const;
|
||||
|
||||
bool SetupOtherInputs(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
@@ -211,11 +198,10 @@ class CompactionPicker {
|
||||
const CompactionInputFiles& output_level_inputs,
|
||||
std::vector<FileMetaData*>* grandparents);
|
||||
|
||||
void PickFilesMarkedForCompaction(
|
||||
const std::string& cf_name, VersionStorageInfo* vstorage,
|
||||
int* start_level, int* output_level,
|
||||
CompactionInputFiles* start_level_inputs,
|
||||
std::function<bool(const FileMetaData*)> skip_marked_file);
|
||||
void PickFilesMarkedForCompaction(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
int* start_level, int* output_level,
|
||||
CompactionInputFiles* start_level_inputs);
|
||||
|
||||
bool GetOverlappingL0Files(VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* start_level_inputs,
|
||||
@@ -239,8 +225,8 @@ class CompactionPicker {
|
||||
protected:
|
||||
const ImmutableOptions& ioptions_;
|
||||
|
||||
// A helper function to SanitizeAndConvertCompactionInputFiles() that
|
||||
// sanitizes "input_files" by adding necessary files.
|
||||
// A helper function to SanitizeCompactionInputFiles() that
|
||||
// sanitizes "input_files" by adding necessary files.
|
||||
virtual Status SanitizeCompactionInputFilesForAllLevels(
|
||||
std::unordered_set<uint64_t>* input_files,
|
||||
const ColumnFamilyMetaData& cf_meta, const int output_level) const;
|
||||
@@ -266,13 +252,11 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
virtual ~NullCompactionPicker() {}
|
||||
|
||||
// Always return "nullptr"
|
||||
Compaction* PickCompaction(
|
||||
const std::string& /*cf_name*/,
|
||||
const MutableCFOptions& /*mutable_cf_options*/,
|
||||
const MutableDBOptions& /*mutable_db_options*/,
|
||||
const std::vector<SequenceNumber>& /*existing_snapshots*/,
|
||||
const SnapshotChecker* /*snapshot_checker*/,
|
||||
VersionStorageInfo* /*vstorage*/, LogBuffer* /* log_buffer */) override {
|
||||
Compaction* PickCompaction(const std::string& /*cf_name*/,
|
||||
const MutableCFOptions& /*mutable_cf_options*/,
|
||||
const MutableDBOptions& /*mutable_db_options*/,
|
||||
VersionStorageInfo* /*vstorage*/,
|
||||
LogBuffer* /* log_buffer */) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,14 +79,10 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
FileMetaData* f = *ritr;
|
||||
assert(f);
|
||||
if (f->fd.table_reader && f->fd.table_reader->GetTableProperties()) {
|
||||
uint64_t newest_key_time = f->TryGetNewestKeyTime();
|
||||
uint64_t creation_time =
|
||||
f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
uint64_t est_newest_key_time = newest_key_time == kUnknownNewestKeyTime
|
||||
? creation_time
|
||||
: newest_key_time;
|
||||
if (est_newest_key_time == kUnknownNewestKeyTime ||
|
||||
est_newest_key_time >= (current_time - mutable_cf_options.ttl)) {
|
||||
if (creation_time == 0 ||
|
||||
creation_time >= (current_time - mutable_cf_options.ttl)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -106,19 +102,15 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
}
|
||||
|
||||
for (const auto& f : inputs[0].files) {
|
||||
assert(f);
|
||||
uint64_t newest_key_time = f->TryGetNewestKeyTime();
|
||||
uint64_t creation_time = 0;
|
||||
assert(f);
|
||||
if (f->fd.table_reader && f->fd.table_reader->GetTableProperties()) {
|
||||
creation_time = f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
}
|
||||
uint64_t est_newest_key_time = newest_key_time == kUnknownNewestKeyTime
|
||||
? creation_time
|
||||
: newest_key_time;
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with estimated newest key time %" PRIu64 " for deletion",
|
||||
cf_name.c_str(), f->fd.GetNumber(), est_newest_key_time);
|
||||
" with creation time %" PRIu64 " for deletion",
|
||||
cf_name.c_str(), f->fd.GetNumber(), creation_time);
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
@@ -126,9 +118,7 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
std::move(inputs), 0, 0, 0, 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kFIFOTtl);
|
||||
@@ -198,9 +188,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
0 /* output path ID */, mutable_cf_options.compression,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
0 /* max_subcompactions */, {},
|
||||
/* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr, /* is manual */ false,
|
||||
0 /* max_subcompactions */, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
/* l0_files_might_overlap */ true,
|
||||
@@ -296,9 +284,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
/* output_path_id */ 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true,
|
||||
/* l0_files_might_overlap */ true, CompactionReason::kFIFOMaxSize);
|
||||
@@ -308,7 +294,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) const {
|
||||
LogBuffer* log_buffer) {
|
||||
const std::vector<FileTemperatureAge>& ages =
|
||||
mutable_cf_options.compaction_options_fifo
|
||||
.file_temperature_age_thresholds;
|
||||
@@ -358,47 +344,73 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
|
||||
Temperature compaction_target_temp = Temperature::kLastTemperature;
|
||||
if (current_time > min_age) {
|
||||
uint64_t create_time_threshold = current_time - min_age;
|
||||
uint64_t compaction_size = 0;
|
||||
// We will ideally identify a file qualifying for temperature change by
|
||||
// knowing the timestamp for the youngest entry in the file. However, right
|
||||
// now we don't have the information. We infer it by looking at timestamp of
|
||||
// the previous file's (which is just younger) oldest entry's timestamp.
|
||||
Temperature cur_target_temp;
|
||||
// avoid index underflow
|
||||
assert(level_files.size() >= 1);
|
||||
for (size_t index = level_files.size(); index >= 1; --index) {
|
||||
for (size_t index = level_files.size() - 1; index >= 1; --index) {
|
||||
// Try to add cur_file to compaction inputs.
|
||||
FileMetaData* cur_file = level_files[index - 1];
|
||||
FileMetaData* prev_file = index < 2 ? nullptr : level_files[index - 2];
|
||||
FileMetaData* cur_file = level_files[index];
|
||||
// prev_file is just younger than cur_file
|
||||
FileMetaData* prev_file = level_files[index - 1];
|
||||
if (cur_file->being_compacted) {
|
||||
// Should not happen since we check for
|
||||
// `level0_compactions_in_progress_` above. Here we simply just don't
|
||||
// schedule anything.
|
||||
return nullptr;
|
||||
}
|
||||
uint64_t est_newest_key_time = cur_file->TryGetNewestKeyTime(prev_file);
|
||||
// Newer file could have newest_key_time populated
|
||||
if (est_newest_key_time == kUnknownNewestKeyTime) {
|
||||
continue;
|
||||
}
|
||||
if (est_newest_key_time > create_time_threshold) {
|
||||
uint64_t oldest_ancestor_time = prev_file->TryGetOldestAncesterTime();
|
||||
if (oldest_ancestor_time == kUnknownOldestAncesterTime) {
|
||||
// Older files might not have enough information. It is possible to
|
||||
// handle these files by looking at newer files, but maintaining the
|
||||
// logic isn't worth it.
|
||||
break;
|
||||
}
|
||||
Temperature cur_target_temp = ages[0].temperature;
|
||||
if (oldest_ancestor_time > create_time_threshold) {
|
||||
// cur_file is too fresh
|
||||
break;
|
||||
}
|
||||
cur_target_temp = ages[0].temperature;
|
||||
for (size_t i = 1; i < ages.size(); ++i) {
|
||||
if (current_time >= ages[i].age &&
|
||||
est_newest_key_time <= current_time - ages[i].age) {
|
||||
oldest_ancestor_time <= current_time - ages[i].age) {
|
||||
cur_target_temp = ages[i].temperature;
|
||||
}
|
||||
}
|
||||
if (cur_file->temperature == cur_target_temp) {
|
||||
continue;
|
||||
if (inputs[0].empty()) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// cur_file needs to change temperature
|
||||
assert(compaction_target_temp == Temperature::kLastTemperature);
|
||||
compaction_target_temp = cur_target_temp;
|
||||
inputs[0].files.push_back(cur_file);
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with estimated newest key time %" PRIu64 " for temperature %s.",
|
||||
cf_name.c_str(), cur_file->fd.GetNumber(), est_newest_key_time,
|
||||
temperature_to_string[cur_target_temp].c_str());
|
||||
break;
|
||||
if (compaction_target_temp == Temperature::kLastTemperature) {
|
||||
assert(inputs[0].empty());
|
||||
compaction_target_temp = cur_target_temp;
|
||||
} else if (cur_target_temp != compaction_target_temp) {
|
||||
assert(!inputs[0].empty());
|
||||
break;
|
||||
}
|
||||
if (inputs[0].empty() || compaction_size + cur_file->fd.GetFileSize() <=
|
||||
mutable_cf_options.max_compaction_bytes) {
|
||||
inputs[0].files.push_back(cur_file);
|
||||
compaction_size += cur_file->fd.GetFileSize();
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with next file's oldest time %" PRIu64 " for temperature %s.",
|
||||
cf_name.c_str(), cur_file->fd.GetNumber(), oldest_ancestor_time,
|
||||
temperature_to_string[cur_target_temp].c_str());
|
||||
}
|
||||
if (compaction_size > mutable_cf_options.max_compaction_bytes) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,18 +418,15 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
|
||||
return nullptr;
|
||||
}
|
||||
assert(compaction_target_temp != Temperature::kLastTemperature);
|
||||
// Only compact one file at a time.
|
||||
assert(inputs.size() == 1);
|
||||
assert(inputs[0].size() == 1);
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, mutable_db_options,
|
||||
std::move(inputs), 0, 0 /* output file size limit */,
|
||||
0 /* max compaction bytes, not applicable */, 0 /* output path ID */,
|
||||
mutable_cf_options.compression, mutable_cf_options.compression_opts,
|
||||
compaction_target_temp,
|
||||
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false, /* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false, /* trim_ts */ "",
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kChangeTemperature);
|
||||
return c;
|
||||
@@ -425,9 +434,7 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& /* existing_snapshots */,
|
||||
const SnapshotChecker* /* snapshot_checker */, VersionStorageInfo* vstorage,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) {
|
||||
Compaction* c = nullptr;
|
||||
if (mutable_cf_options.ttl > 0) {
|
||||
@@ -462,10 +469,8 @@ Compaction* FIFOCompactionPicker::CompactRange(
|
||||
assert(output_level == 0);
|
||||
*compaction_end = nullptr;
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, ioptions_.logger);
|
||||
Compaction* c =
|
||||
PickCompaction(cf_name, mutable_cf_options, mutable_db_options,
|
||||
/*existing_snapshots*/ {}, /*snapshot_checker*/ nullptr,
|
||||
vstorage, &log_buffer);
|
||||
Compaction* c = PickCompaction(cf_name, mutable_cf_options,
|
||||
mutable_db_options, vstorage, &log_buffer);
|
||||
log_buffer.FlushBufferToLog();
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -18,12 +18,11 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
|
||||
Compaction* PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& /* existing_snapshots */,
|
||||
const SnapshotChecker* /* snapshot_checker */,
|
||||
VersionStorageInfo* version, LogBuffer* log_buffer) override;
|
||||
Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
Compaction* CompactRange(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
@@ -54,10 +53,9 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Will pick one file to compact at a time, starting from the oldest file.
|
||||
Compaction* PickTemperatureChangeCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) const;
|
||||
LogBuffer* log_buffer);
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -262,10 +262,7 @@ void LevelCompactionBuilder::SetupInitialFiles() {
|
||||
parent_index_ = base_index_ = -1;
|
||||
|
||||
compaction_picker_->PickFilesMarkedForCompaction(
|
||||
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_,
|
||||
/*skip_marked_file*/ [](const FileMetaData* /* file */) {
|
||||
return false;
|
||||
});
|
||||
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
@@ -414,9 +411,8 @@ void LevelCompactionBuilder::SetupOtherFilesWithRoundRobinExpansion() {
|
||||
&tmp_start_level_inputs) ||
|
||||
compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
{tmp_start_level_inputs}, output_level_,
|
||||
Compaction::EvaluateProximalLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level_,
|
||||
output_level_))) {
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
vstorage_, ioptions_, start_level_, output_level_))) {
|
||||
// Constraint 1a
|
||||
tmp_start_level_inputs.clear();
|
||||
return;
|
||||
@@ -490,9 +486,8 @@ bool LevelCompactionBuilder::SetupOtherInputsIfNeeded() {
|
||||
// We need to disallow this from happening.
|
||||
if (compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
compaction_inputs_, output_level_,
|
||||
Compaction::EvaluateProximalLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level_,
|
||||
output_level_))) {
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
vstorage_, 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;
|
||||
@@ -559,9 +554,7 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
vstorage_->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level_),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, std::move(grandparents_),
|
||||
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
|
||||
is_manual_,
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
/* trim_ts */ "", start_level_score_, false /* deletion_compaction */,
|
||||
l0_files_might_overlap, compaction_reason_);
|
||||
|
||||
@@ -846,9 +839,8 @@ bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
&start_level_inputs_) ||
|
||||
compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
{start_level_inputs_}, output_level_,
|
||||
Compaction::EvaluateProximalLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level_,
|
||||
output_level_))) {
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
vstorage_, ioptions_, start_level_, output_level_))) {
|
||||
// A locked (pending compaction) input-level file was pulled in due to
|
||||
// user-key overlap.
|
||||
start_level_inputs_.clear();
|
||||
@@ -933,15 +925,11 @@ bool LevelCompactionBuilder::PickSizeBasedIntraL0Compaction() {
|
||||
}
|
||||
uint64_t l0_size = 0;
|
||||
for (const auto& file : l0_files) {
|
||||
assert(file->compensated_file_size >= file->fd.GetFileSize());
|
||||
// Compact down L0s with more deletions.
|
||||
l0_size += file->compensated_file_size;
|
||||
l0_size += file->fd.GetFileSize();
|
||||
}
|
||||
|
||||
// Avoid L0->Lbase compactions that are inefficient for write-amp.
|
||||
const double kMultiplier =
|
||||
std::max(10.0, mutable_cf_options_.max_bytes_for_level_multiplier) * 2;
|
||||
const uint64_t min_lbase_size = MultiplyCheckOverflow(l0_size, kMultiplier);
|
||||
const uint64_t min_lbase_size =
|
||||
l0_size * static_cast<uint64_t>(std::max(
|
||||
10.0, mutable_cf_options_.max_bytes_for_level_multiplier));
|
||||
assert(min_lbase_size >= l0_size);
|
||||
const std::vector<FileMetaData*>& lbase_files =
|
||||
vstorage_->LevelFiles(/*level=*/base_level);
|
||||
@@ -975,9 +963,7 @@ bool LevelCompactionBuilder::PickSizeBasedIntraL0Compaction() {
|
||||
|
||||
Compaction* LevelCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& /*existing_snapshots */,
|
||||
const SnapshotChecker* /*snapshot_checker*/, VersionStorageInfo* vstorage,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) {
|
||||
LevelCompactionBuilder builder(cf_name, vstorage, this, log_buffer,
|
||||
mutable_cf_options, ioptions_,
|
||||
|
||||
@@ -20,12 +20,11 @@ class LevelCompactionPicker : public CompactionPicker {
|
||||
LevelCompactionPicker(const ImmutableOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
Compaction* PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& /* existing_snapshots */,
|
||||
const SnapshotChecker* /* snapshot_checker */,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) override;
|
||||
Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -35,9 +35,7 @@ class UniversalCompactionBuilder {
|
||||
UniversalCompactionBuilder(
|
||||
const ImmutableOptions& ioptions, const InternalKeyComparator* icmp,
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
UniversalCompactionPicker* picker, LogBuffer* log_buffer)
|
||||
: ioptions_(ioptions),
|
||||
icmp_(icmp),
|
||||
@@ -46,19 +44,7 @@ class UniversalCompactionBuilder {
|
||||
mutable_db_options_(mutable_db_options),
|
||||
vstorage_(vstorage),
|
||||
picker_(picker),
|
||||
log_buffer_(log_buffer) {
|
||||
assert(icmp_);
|
||||
const auto* ucmp = icmp_->user_comparator();
|
||||
assert(ucmp);
|
||||
// These parameters are only passed when user-defined timestamp is not
|
||||
// enabled.
|
||||
if (ucmp->timestamp_size() == 0) {
|
||||
earliest_snapshot_ = existing_snapshots.empty()
|
||||
? kMaxSequenceNumber
|
||||
: existing_snapshots.at(0);
|
||||
snapshot_checker_ = snapshot_checker;
|
||||
}
|
||||
}
|
||||
log_buffer_(log_buffer) {}
|
||||
|
||||
// Form and return the compaction object. The caller owns return object.
|
||||
Compaction* PickCompaction();
|
||||
@@ -66,15 +52,12 @@ class UniversalCompactionBuilder {
|
||||
private:
|
||||
struct SortedRun {
|
||||
SortedRun(int _level, FileMetaData* _file, uint64_t _size,
|
||||
uint64_t _compensated_file_size, bool _being_compacted,
|
||||
bool _level_has_marked_standalone_rangedel)
|
||||
uint64_t _compensated_file_size, bool _being_compacted)
|
||||
: level(_level),
|
||||
file(_file),
|
||||
size(_size),
|
||||
compensated_file_size(_compensated_file_size),
|
||||
being_compacted(_being_compacted),
|
||||
level_has_marked_standalone_rangedel(
|
||||
_level_has_marked_standalone_rangedel) {
|
||||
being_compacted(_being_compacted) {
|
||||
assert(compensated_file_size > 0);
|
||||
assert(level != 0 || file != nullptr);
|
||||
}
|
||||
@@ -96,10 +79,6 @@ class UniversalCompactionBuilder {
|
||||
uint64_t size;
|
||||
uint64_t compensated_file_size;
|
||||
bool being_compacted;
|
||||
// True if this level has any file that is a standalone range deletion file
|
||||
// marked for compaction. Best effort is made to make only deletion
|
||||
// triggered compaction pick this type of file.
|
||||
bool level_has_marked_standalone_rangedel;
|
||||
};
|
||||
|
||||
// Pick Universal compaction to limit read amplification
|
||||
@@ -119,11 +98,6 @@ class UniversalCompactionBuilder {
|
||||
|
||||
Compaction* PickDeleteTriggeredCompaction();
|
||||
|
||||
// Returns true if this given file (that is marked be compaction) should be
|
||||
// skipped from being picked for now. We do this to best use standalone range
|
||||
// tombstone files.
|
||||
bool ShouldSkipMarkedFile(const FileMetaData* file) const;
|
||||
|
||||
// Form a compaction from the sorted run indicated by start_index to the
|
||||
// oldest sorted run.
|
||||
// The caller is responsible for making sure that those files are not in
|
||||
@@ -142,7 +116,7 @@ class UniversalCompactionBuilder {
|
||||
|
||||
bool ShouldSkipLastSortedRunForSizeAmpCompaction() const {
|
||||
assert(!sorted_runs_.empty());
|
||||
return mutable_cf_options_.preclude_last_level_data_seconds > 0 &&
|
||||
return ioptions_.preclude_last_level_data_seconds > 0 &&
|
||||
ioptions_.num_levels > 2 &&
|
||||
sorted_runs_.back().level == ioptions_.num_levels - 1 &&
|
||||
sorted_runs_.size() > 1;
|
||||
@@ -253,27 +227,15 @@ class UniversalCompactionBuilder {
|
||||
const InternalKeyComparator* icmp_;
|
||||
double score_;
|
||||
std::vector<SortedRun> sorted_runs_;
|
||||
uint64_t max_run_size_;
|
||||
const std::string& cf_name_;
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
const MutableDBOptions& mutable_db_options_;
|
||||
VersionStorageInfo* vstorage_;
|
||||
UniversalCompactionPicker* picker_;
|
||||
LogBuffer* log_buffer_;
|
||||
// Optional earliest snapshot at time of compaction picking. This is only
|
||||
// provided if the column family doesn't enable user-defined timestamps.
|
||||
// And this information is only passed to `Compaction` picked by deletion
|
||||
// triggered compaction for possible optimizations.
|
||||
std::optional<SequenceNumber> earliest_snapshot_;
|
||||
const SnapshotChecker* snapshot_checker_;
|
||||
// Mapping from file id to its index in the sorted run for the files that are
|
||||
// marked for compaction. This is only populated when snapshot info is
|
||||
// populated.
|
||||
std::map<uint64_t, size_t> file_marked_for_compaction_to_sorted_run_index_;
|
||||
|
||||
std::vector<UniversalCompactionBuilder::SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, int last_level,
|
||||
uint64_t* max_run_size);
|
||||
static std::vector<UniversalCompactionBuilder::SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, int last_level);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
@@ -288,9 +250,7 @@ class UniversalCompactionBuilder {
|
||||
// and the index of the file in that level
|
||||
|
||||
struct InputFileInfo {
|
||||
InputFileInfo() : InputFileInfo(nullptr, 0, 0) {}
|
||||
InputFileInfo(FileMetaData* file_meta, size_t l, size_t i)
|
||||
: f(file_meta), level(l), index(i) {}
|
||||
InputFileInfo() : f(nullptr), level(0), index(0) {}
|
||||
|
||||
FileMetaData* f;
|
||||
size_t level;
|
||||
@@ -323,14 +283,22 @@ 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++) {
|
||||
smallest_key_priority_q.emplace(c->input(0, i), 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));
|
||||
}
|
||||
} else {
|
||||
smallest_key_priority_q.emplace(c->input(l, 0), l, 0);
|
||||
input_file.f = c->input(l, 0);
|
||||
input_file.level = l;
|
||||
input_file.index = 0;
|
||||
smallest_key_priority_q.push(std::move(input_file));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -368,7 +336,7 @@ bool UniversalCompactionBuilder::IsInputFilesNonOverlapping(Compaction* c) {
|
||||
auto comparator = icmp_->user_comparator();
|
||||
int first_iter = 1;
|
||||
|
||||
InputFileInfo prev, curr;
|
||||
InputFileInfo prev, curr, next;
|
||||
|
||||
SmallestKeyHeap smallest_key_priority_q =
|
||||
create_level_heap(c, icmp_->user_comparator());
|
||||
@@ -391,10 +359,17 @@ 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) {
|
||||
smallest_key_priority_q.emplace(c->input(curr.level, curr.index + 1),
|
||||
curr.level, curr.index + 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));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -417,13 +392,11 @@ bool UniversalCompactionPicker::NeedsCompaction(
|
||||
|
||||
Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) {
|
||||
UniversalCompactionBuilder builder(
|
||||
ioptions_, icmp_, cf_name, mutable_cf_options, mutable_db_options,
|
||||
existing_snapshots, snapshot_checker, vstorage, this, log_buffer);
|
||||
UniversalCompactionBuilder builder(ioptions_, icmp_, cf_name,
|
||||
mutable_cf_options, mutable_db_options,
|
||||
vstorage, this, log_buffer);
|
||||
return builder.PickCompaction();
|
||||
}
|
||||
|
||||
@@ -467,26 +440,16 @@ void UniversalCompactionBuilder::SortedRun::DumpSizeInfo(
|
||||
|
||||
std::vector<UniversalCompactionBuilder::SortedRun>
|
||||
UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, int last_level,
|
||||
uint64_t* max_run_size) {
|
||||
assert(max_run_size);
|
||||
*max_run_size = 0;
|
||||
const VersionStorageInfo& vstorage, int last_level) {
|
||||
std::vector<UniversalCompactionBuilder::SortedRun> ret;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(0)) {
|
||||
if (earliest_snapshot_.has_value() && f->marked_for_compaction) {
|
||||
file_marked_for_compaction_to_sorted_run_index_.emplace(f->fd.GetNumber(),
|
||||
ret.size());
|
||||
}
|
||||
ret.emplace_back(
|
||||
0, f, f->fd.GetFileSize(), f->compensated_file_size, f->being_compacted,
|
||||
f->marked_for_compaction && f->FileIsStandAloneRangeTombstone());
|
||||
*max_run_size = std::max(*max_run_size, f->fd.GetFileSize());
|
||||
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
|
||||
f->being_compacted);
|
||||
}
|
||||
for (int level = 1; level <= last_level; level++) {
|
||||
uint64_t total_compensated_size = 0U;
|
||||
uint64_t total_size = 0U;
|
||||
bool being_compacted = false;
|
||||
bool level_has_marked_standalone_rangedel = false;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(level)) {
|
||||
total_compensated_size += f->compensated_file_size;
|
||||
total_size += f->fd.GetFileSize();
|
||||
@@ -498,57 +461,15 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
if (f->being_compacted) {
|
||||
being_compacted = f->being_compacted;
|
||||
}
|
||||
level_has_marked_standalone_rangedel =
|
||||
level_has_marked_standalone_rangedel ||
|
||||
(f->marked_for_compaction && f->FileIsStandAloneRangeTombstone());
|
||||
if (earliest_snapshot_.has_value() && f->marked_for_compaction) {
|
||||
file_marked_for_compaction_to_sorted_run_index_.emplace(
|
||||
f->fd.GetNumber(), ret.size());
|
||||
}
|
||||
}
|
||||
if (total_compensated_size > 0) {
|
||||
ret.emplace_back(level, nullptr, total_size, total_compensated_size,
|
||||
being_compacted, level_has_marked_standalone_rangedel);
|
||||
being_compacted);
|
||||
}
|
||||
*max_run_size = std::max(*max_run_size, total_size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool UniversalCompactionBuilder::ShouldSkipMarkedFile(
|
||||
const FileMetaData* file) const {
|
||||
assert(file->marked_for_compaction);
|
||||
if (!earliest_snapshot_.has_value()) {
|
||||
return false;
|
||||
}
|
||||
if (!file->FileIsStandAloneRangeTombstone()) {
|
||||
return false;
|
||||
}
|
||||
// Skip until earliest snapshot advances at or above this standalone range
|
||||
// tombstone file. `DB::ReleaseSnapshot` will re-examine and schedule
|
||||
// compaction for it.
|
||||
if (!DataIsDefinitelyInSnapshot(file->fd.largest_seqno,
|
||||
earliest_snapshot_.value(),
|
||||
snapshot_checker_)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto iter = file_marked_for_compaction_to_sorted_run_index_.find(
|
||||
file->fd.GetNumber());
|
||||
assert(iter != file_marked_for_compaction_to_sorted_run_index_.end());
|
||||
size_t idx = iter->second;
|
||||
const SortedRun* succeeding_sorted_run =
|
||||
idx < sorted_runs_.size() - 1 ? &sorted_runs_[idx + 1] : nullptr;
|
||||
// Marked standalone range tombstone file is best used if it's in the start
|
||||
// input level. Skip to let that compaction happen first.
|
||||
if (succeeding_sorted_run &&
|
||||
succeeding_sorted_run->level_has_marked_standalone_rangedel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Universal style of compaction. Pick files that are contiguous in
|
||||
// time-range to compact.
|
||||
Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
@@ -556,16 +477,13 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
score_ = vstorage_->CompactionScore(kLevel0);
|
||||
int max_output_level =
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
max_run_size_ = 0;
|
||||
sorted_runs_ =
|
||||
CalculateSortedRuns(*vstorage_, max_output_level, &max_run_size_);
|
||||
int file_num_compaction_trigger =
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger;
|
||||
sorted_runs_ = CalculateSortedRuns(*vstorage_, max_output_level);
|
||||
|
||||
if (sorted_runs_.size() == 0 ||
|
||||
(vstorage_->FilesMarkedForPeriodicCompaction().empty() &&
|
||||
vstorage_->FilesMarkedForCompaction().empty() &&
|
||||
sorted_runs_.size() < (unsigned int)file_num_compaction_trigger)) {
|
||||
sorted_runs_.size() < (unsigned int)mutable_cf_options_
|
||||
.level0_file_num_compaction_trigger)) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Universal: nothing to do\n",
|
||||
cf_name_.c_str());
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
@@ -587,9 +505,11 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
TEST_SYNC_POINT_CALLBACK("PostPickPeriodicCompaction", c);
|
||||
}
|
||||
|
||||
// Check for size amplification.
|
||||
if (c == nullptr &&
|
||||
sorted_runs_.size() >= static_cast<size_t>(file_num_compaction_trigger)) {
|
||||
// Check for size amplification.
|
||||
sorted_runs_.size() >=
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger)) {
|
||||
if ((c = PickCompactionToReduceSizeAmp()) != nullptr) {
|
||||
TEST_SYNC_POINT("PickCompactionToReduceSizeAmpReturnNonnullptr");
|
||||
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Universal: compacting for size amp\n",
|
||||
@@ -607,78 +527,34 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
cf_name_.c_str());
|
||||
} else {
|
||||
// Size amplification and file size ratios are within configured limits.
|
||||
// If max read amplification exceeds configured limits, then force
|
||||
// compaction to reduce the number sorted runs without looking at file
|
||||
// size ratios.
|
||||
|
||||
// If max read amplification is exceeding configured limits, then force
|
||||
// compaction without looking at filesize ratios and try to reduce
|
||||
// the number of files to fewer than level0_file_num_compaction_trigger.
|
||||
// This is guaranteed by NeedsCompaction()
|
||||
assert(sorted_runs_.size() >=
|
||||
static_cast<size_t>(file_num_compaction_trigger));
|
||||
int max_num_runs =
|
||||
mutable_cf_options_.compaction_options_universal.max_read_amp;
|
||||
if (max_num_runs < 0) {
|
||||
// any value < -1 is not valid
|
||||
assert(max_num_runs == -1);
|
||||
// By default, fall back to `level0_file_num_compaction_trigger`
|
||||
max_num_runs = file_num_compaction_trigger;
|
||||
} else if (max_num_runs == 0) {
|
||||
if (mutable_cf_options_.compaction_options_universal.stop_style ==
|
||||
kCompactionStopStyleTotalSize) {
|
||||
// 0 means auto-tuning by RocksDB. We estimate max num run based on
|
||||
// max_run_size, size_ratio and write buffer size:
|
||||
// Assume the size of the lowest level size is equal to
|
||||
// write_buffer_size. Each subsequent level is the max size without
|
||||
// triggering size_ratio compaction. `max_num_runs` is the minimum
|
||||
// number of levels required such that the target size of the
|
||||
// largest level is at least `max_run_size_`.
|
||||
max_num_runs = 1;
|
||||
double cur_level_max_size =
|
||||
static_cast<double>(mutable_cf_options_.write_buffer_size);
|
||||
double total_run_size = 0;
|
||||
while (cur_level_max_size < static_cast<double>(max_run_size_)) {
|
||||
// This loop should not take too many iterations since
|
||||
// cur_level_max_size at least doubles each iteration.
|
||||
total_run_size += cur_level_max_size;
|
||||
cur_level_max_size = (100.0 + ratio) / 100.0 * total_run_size;
|
||||
++max_num_runs;
|
||||
}
|
||||
} else {
|
||||
// TODO: implement the auto-tune logic for this stop style
|
||||
max_num_runs = file_num_compaction_trigger;
|
||||
}
|
||||
} else {
|
||||
// max_num_runs > 0, it's the limit on the number of sorted run
|
||||
}
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger));
|
||||
// Get the total number of sorted runs that are not being compacted
|
||||
int num_sr_not_compacted = 0;
|
||||
for (size_t i = 0; i < sorted_runs_.size(); i++) {
|
||||
if (sorted_runs_[i].being_compacted == false &&
|
||||
!sorted_runs_[i].level_has_marked_standalone_rangedel) {
|
||||
if (sorted_runs_[i].being_compacted == false) {
|
||||
num_sr_not_compacted++;
|
||||
}
|
||||
}
|
||||
|
||||
// The number of sorted runs that are not being compacted is greater
|
||||
// than the maximum allowed number of sorted runs
|
||||
if (num_sr_not_compacted > max_num_runs) {
|
||||
unsigned int num_files = num_sr_not_compacted - max_num_runs + 1;
|
||||
if (num_sr_not_compacted >
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger) {
|
||||
unsigned int num_files =
|
||||
num_sr_not_compacted -
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger + 1;
|
||||
if ((c = PickCompactionToReduceSortedRuns(UINT_MAX, num_files)) !=
|
||||
nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: compacting for file num, to "
|
||||
"compact file num -- %u, max num runs allowed"
|
||||
"-- %d, max_run_size -- %" PRIu64 "\n",
|
||||
cf_name_.c_str(), num_files, max_num_runs,
|
||||
max_run_size_);
|
||||
"[%s] Universal: compacting for file num -- %u\n",
|
||||
cf_name_.c_str(), num_files);
|
||||
}
|
||||
} else {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: skipping compaction for file num, num runs not "
|
||||
"being compacted -- %u, max num runs allowed -- %d, max_run_size "
|
||||
"-- %" PRIu64 "\n",
|
||||
cf_name_.c_str(), num_sr_not_compacted, max_num_runs,
|
||||
max_run_size_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -816,24 +692,16 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
for (sr = nullptr; loop < sorted_runs_.size(); loop++) {
|
||||
sr = &sorted_runs_[loop];
|
||||
|
||||
if (!sr->being_compacted && !sr->level_has_marked_standalone_rangedel) {
|
||||
if (!sr->being_compacted) {
|
||||
candidate_count = 1;
|
||||
break;
|
||||
}
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
sr->Dump(file_num_buf, sizeof(file_num_buf));
|
||||
if (sr->being_compacted) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: %s"
|
||||
"[%d] being compacted, skipping",
|
||||
cf_name_.c_str(), file_num_buf, loop);
|
||||
} else if (sr->level_has_marked_standalone_rangedel) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: %s"
|
||||
"[%d] has standalone range tombstone files marked for "
|
||||
"compaction, skipping",
|
||||
cf_name_.c_str(), file_num_buf, loop);
|
||||
}
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: %s"
|
||||
"[%d] being compacted, skipping",
|
||||
cf_name_.c_str(), file_num_buf, loop);
|
||||
|
||||
sr = nullptr;
|
||||
}
|
||||
@@ -854,8 +722,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
candidate_count < max_files_to_compact && i < sorted_runs_.size();
|
||||
i++) {
|
||||
const SortedRun* succeeding_sr = &sorted_runs_[i];
|
||||
if (succeeding_sr->being_compacted ||
|
||||
succeeding_sr->level_has_marked_standalone_rangedel) {
|
||||
if (succeeding_sr->being_compacted) {
|
||||
break;
|
||||
}
|
||||
// Pick files if the total/last candidate file size (increased by the
|
||||
@@ -981,11 +848,11 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
grandparents = vstorage_->LevelFiles(sorted_runs_[first_index_after].level);
|
||||
}
|
||||
|
||||
if (output_level != 0 && picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluateProximalLevel(
|
||||
vstorage_, mutable_cf_options_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
if (output_level != 0 &&
|
||||
picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
return nullptr;
|
||||
}
|
||||
CompactionReason compaction_reason;
|
||||
@@ -1005,8 +872,6 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
output_level, enable_compression),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, grandparents,
|
||||
/* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false, /* trim_ts */ "", score_,
|
||||
false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true, compaction_reason);
|
||||
@@ -1023,8 +888,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
const size_t end_index = ShouldSkipLastSortedRunForSizeAmpCompaction()
|
||||
? sorted_runs_.size() - 2
|
||||
: sorted_runs_.size() - 1;
|
||||
if (sorted_runs_[end_index].being_compacted ||
|
||||
sorted_runs_[end_index].level_has_marked_standalone_rangedel) {
|
||||
if (sorted_runs_[end_index].being_compacted) {
|
||||
return nullptr;
|
||||
}
|
||||
const uint64_t base_sr_size = sorted_runs_[end_index].size;
|
||||
@@ -1035,23 +899,14 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
// Get longest span (i.e, [start_index, end_index]) of available sorted runs
|
||||
while (start_index > 0) {
|
||||
const SortedRun* sr = &sorted_runs_[start_index - 1];
|
||||
if (sr->being_compacted || sr->level_has_marked_standalone_rangedel) {
|
||||
if (sr->being_compacted) {
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
sr->Dump(file_num_buf, sizeof(file_num_buf), true);
|
||||
if (sr->being_compacted) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: stopping at sorted run undergoing compaction: "
|
||||
"%s[%" ROCKSDB_PRIszt "]",
|
||||
cf_name_.c_str(), file_num_buf, start_index - 1);
|
||||
} else if (sr->level_has_marked_standalone_rangedel) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: stopping at sorted run that has standalone range "
|
||||
"tombstone files marked for compaction: "
|
||||
"%s[%" ROCKSDB_PRIszt "]",
|
||||
cf_name_.c_str(), file_num_buf, start_index - 1);
|
||||
}
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: stopping at sorted run undergoing compaction: "
|
||||
"%s[%" ROCKSDB_PRIszt "]",
|
||||
cf_name_.c_str(), file_num_buf, start_index - 1);
|
||||
break;
|
||||
}
|
||||
candidate_size += sr->compensated_file_size;
|
||||
@@ -1330,11 +1185,11 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
|
||||
}
|
||||
|
||||
// intra L0 compactions outputs could have overlap
|
||||
if (output_level != 0 && picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluateProximalLevel(
|
||||
vstorage_, mutable_cf_options_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
if (output_level != 0 &&
|
||||
picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1351,10 +1206,7 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {},
|
||||
/* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true,
|
||||
CompactionReason::kUniversalSizeAmplification);
|
||||
@@ -1385,7 +1237,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
continue;
|
||||
}
|
||||
FileMetaData* f = vstorage_->LevelFiles(0)[loop];
|
||||
if (f->marked_for_compaction && !ShouldSkipMarkedFile(f)) {
|
||||
if (f->marked_for_compaction) {
|
||||
start_level_inputs.files.push_back(f);
|
||||
start_index =
|
||||
static_cast<int>(loop); // Consider this as the first candidate.
|
||||
@@ -1399,7 +1251,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
|
||||
for (size_t loop = start_index + 1; loop < sorted_runs_.size(); loop++) {
|
||||
SortedRun* sr = &sorted_runs_[loop];
|
||||
if (sr->being_compacted || sr->level_has_marked_standalone_rangedel) {
|
||||
if (sr->being_compacted) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1418,10 +1270,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
// leveled. We pick one of the files marked for compaction and compact with
|
||||
// overlapping files in the adjacent level.
|
||||
picker_->PickFilesMarkedForCompaction(cf_name_, vstorage_, &start_level,
|
||||
&output_level, &start_level_inputs,
|
||||
[this](const FileMetaData* file) {
|
||||
return ShouldSkipMarkedFile(file);
|
||||
});
|
||||
&output_level, &start_level_inputs);
|
||||
if (start_level_inputs.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1473,9 +1322,8 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
}
|
||||
if (picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluateProximalLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level,
|
||||
output_level))) {
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
vstorage_, ioptions_, start_level, output_level))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1502,9 +1350,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, grandparents, earliest_snapshot_,
|
||||
snapshot_checker_,
|
||||
/* is manual */ false,
|
||||
/* max_subcompactions */ 0, grandparents, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
@@ -1575,11 +1421,11 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
|
||||
}
|
||||
|
||||
// intra L0 compactions outputs could have overlap
|
||||
if (output_level != 0 && picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluateProximalLevel(
|
||||
vstorage_, mutable_cf_options_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
if (output_level != 0 &&
|
||||
picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1597,10 +1443,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {},
|
||||
/* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true, compaction_reason);
|
||||
}
|
||||
@@ -1621,8 +1464,7 @@ Compaction* UniversalCompactionBuilder::PickPeriodicCompaction() {
|
||||
// included in the compaction.
|
||||
|
||||
size_t start_index = sorted_runs_.size();
|
||||
while (start_index > 0 && !sorted_runs_[start_index - 1].being_compacted &&
|
||||
!sorted_runs_[start_index - 1].level_has_marked_standalone_rangedel) {
|
||||
while (start_index > 0 && !sorted_runs_[start_index - 1].being_compacted) {
|
||||
start_index--;
|
||||
}
|
||||
if (start_index == sorted_runs_.size()) {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class UniversalCompactionPicker : public CompactionPicker {
|
||||
@@ -18,12 +17,11 @@ class UniversalCompactionPicker : public CompactionPicker {
|
||||
UniversalCompactionPicker(const ImmutableOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
Compaction* PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
int MaxOutputLevel() const override { return NumberLevels() - 1; }
|
||||
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
|
||||
@@ -39,8 +39,12 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
MakeTableFileName(file->fd.GetNumber()));
|
||||
}
|
||||
}
|
||||
|
||||
compaction_input.cf_name = compaction->column_family_data()->GetName();
|
||||
compaction_input.column_family.name =
|
||||
compaction->column_family_data()->GetName();
|
||||
compaction_input.column_family.options =
|
||||
compaction->column_family_data()->GetLatestCFOptions();
|
||||
compaction_input.db_options =
|
||||
BuildDBOptions(db_options_, mutable_db_options_copy_);
|
||||
compaction_input.snapshots = existing_snapshots_;
|
||||
compaction_input.has_begin = sub_compact->start.has_value();
|
||||
compaction_input.begin =
|
||||
@@ -48,11 +52,6 @@ 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 = options_file_number_;
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionServiceJob::ProcessKeyValueCompactionWithCompactionService",
|
||||
&compaction_input);
|
||||
|
||||
std::string compaction_input_binary;
|
||||
Status s = compaction_input.Write(&compaction_input_binary);
|
||||
@@ -71,81 +70,46 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Starting remote compaction (output level: %d): %s",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
compaction_input.column_family.name.c_str(), job_id_,
|
||||
compaction_input.output_level, input_files_oss.str().c_str());
|
||||
CompactionServiceJobInfo info(
|
||||
dbname_, db_id_, db_session_id_, GetCompactionId(sub_compact),
|
||||
thread_pri_, compaction->compaction_reason(),
|
||||
compaction->is_full_compaction(), compaction->is_manual_compaction(),
|
||||
compaction->bottommost_level());
|
||||
CompactionServiceJobInfo info(dbname_, db_id_, db_session_id_,
|
||||
GetCompactionId(sub_compact), thread_pri_);
|
||||
CompactionServiceScheduleResponse response =
|
||||
db_options_.compaction_service->Schedule(info, compaction_input_binary);
|
||||
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.");
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction failed to start.",
|
||||
compaction->column_family_data()->GetName().c_str(),
|
||||
job_id_);
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return response.status;
|
||||
case CompactionServiceJobStatus::kUseLocal:
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction fallback to local by API (Schedule)",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_);
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return response.status;
|
||||
default:
|
||||
assert(false); // unknown status
|
||||
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_);
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
std::string compaction_result_binary;
|
||||
CompactionServiceJobStatus compaction_status =
|
||||
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,
|
||||
"[%s] [JOB %d] Remote compaction fallback to local by API (Wait)",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_);
|
||||
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_);
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return compaction_status;
|
||||
}
|
||||
|
||||
@@ -170,19 +134,15 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
"result is returned).");
|
||||
compaction_result.status.PermitUncheckedError();
|
||||
}
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log, "[%s] [JOB %d] Remote compaction failed.",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_);
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction failed.",
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return compaction_status;
|
||||
}
|
||||
|
||||
// CompactionServiceJobStatus::kSuccess was returned, but somehow we failed to
|
||||
// read the result. Consider this as an installation failure
|
||||
if (!s.ok()) {
|
||||
sub_compact->status = s;
|
||||
compaction_result.status.PermitUncheckedError();
|
||||
db_options_.compaction_service->OnInstallation(
|
||||
response.scheduled_job_id, CompactionServiceJobStatus::kFailure);
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
sub_compact->status = compaction_result.status;
|
||||
@@ -194,38 +154,34 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
is_first_one = false;
|
||||
}
|
||||
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Received remote compaction result, output path: "
|
||||
"%s, files: %s",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
compaction_result.output_path.c_str(), output_files_oss.str().c_str());
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Receive remote compaction result, output path: "
|
||||
"%s, files: %s",
|
||||
compaction_input.column_family.name.c_str(), job_id_,
|
||||
compaction_result.output_path.c_str(),
|
||||
output_files_oss.str().c_str());
|
||||
|
||||
if (!s.ok()) {
|
||||
sub_compact->status = s;
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
|
||||
// Installation Starts
|
||||
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()) {
|
||||
sub_compact->status = s;
|
||||
db_options_.compaction_service->OnInstallation(
|
||||
response.scheduled_job_id, CompactionServiceJobStatus::kFailure);
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
|
||||
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;
|
||||
db_options_.compaction_service->OnInstallation(
|
||||
response.scheduled_job_id, CompactionServiceJobStatus::kFailure);
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
meta.fd = FileDescriptor(file_num, compaction->output_path_id(), file_size,
|
||||
@@ -235,43 +191,21 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
meta.oldest_ancester_time = file.oldest_ancester_time;
|
||||
meta.file_creation_time = file.file_creation_time;
|
||||
meta.epoch_number = file.epoch_number;
|
||||
meta.file_checksum = file.file_checksum;
|
||||
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();
|
||||
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);
|
||||
sub_compact->Current().AddOutput(std::move(meta),
|
||||
cfd->internal_comparator(), false, true,
|
||||
file.paranoid_hash);
|
||||
}
|
||||
|
||||
// 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.num_output_records);
|
||||
sub_compact->Current().SetTotalBytes(compaction_result.total_bytes);
|
||||
RecordTick(stats_, REMOTE_COMPACT_READ_BYTES, compaction_result.bytes_read);
|
||||
RecordTick(stats_, REMOTE_COMPACT_WRITE_BYTES,
|
||||
compaction_result.bytes_written);
|
||||
db_options_.compaction_service->OnInstallation(
|
||||
response.scheduled_job_id, CompactionServiceJobStatus::kSuccess);
|
||||
return CompactionServiceJobStatus::kSuccess;
|
||||
}
|
||||
|
||||
@@ -300,61 +234,62 @@ 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);
|
||||
assert(c->column_family_data()->current()->storage_info()->NumLevelFiles(
|
||||
compact_->compaction->level()) > 0);
|
||||
|
||||
write_hint_ =
|
||||
c->column_family_data()->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();
|
||||
|
||||
compaction_result_->stats.Reset();
|
||||
|
||||
const uint64_t start_micros = db_options_.clock->NowMicros();
|
||||
c->GetOrInitInputTableProperties();
|
||||
|
||||
// Pick the only sub-compaction we should have
|
||||
assert(compact_->sub_compact_states.size() == 1);
|
||||
SubcompactionState* sub_compact = compact_->sub_compact_states.data();
|
||||
|
||||
ProcessKeyValueCompaction(sub_compact);
|
||||
|
||||
uint64_t elapsed_micros = db_options_.clock->NowMicros() - start_micros;
|
||||
internal_stats_.SetMicros(elapsed_micros);
|
||||
internal_stats_.AddCpuMicros(elapsed_micros);
|
||||
compaction_stats_.stats.micros =
|
||||
db_options_.clock->NowMicros() - start_micros;
|
||||
compaction_stats_.stats.cpu_micros =
|
||||
sub_compact->compaction_job_stats.cpu_micros;
|
||||
|
||||
RecordTimeToHistogram(stats_, COMPACTION_TIME,
|
||||
internal_stats_.output_level_stats.micros);
|
||||
compaction_stats_.stats.micros);
|
||||
RecordTimeToHistogram(stats_, COMPACTION_CPU_TIME,
|
||||
internal_stats_.output_level_stats.cpu_micros);
|
||||
compaction_stats_.stats.cpu_micros);
|
||||
|
||||
Status status = sub_compact->status;
|
||||
IOStatus io_s = sub_compact->io_status;
|
||||
@@ -377,57 +312,38 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
if (status.ok()) {
|
||||
status = io_s;
|
||||
}
|
||||
if (status.ok()) {
|
||||
// TODO: Add verify_table()
|
||||
}
|
||||
|
||||
// Finish up all book-keeping to unify the subcompaction results
|
||||
compact_->AggregateCompactionStats(compaction_stats_, *compaction_job_stats_);
|
||||
UpdateCompactionStats();
|
||||
RecordCompactionIOStats();
|
||||
|
||||
LogFlush(db_options_.info_log);
|
||||
compact_->status = status;
|
||||
compact_->status.PermitUncheckedError();
|
||||
|
||||
// Build 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 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;
|
||||
|
||||
// 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_;
|
||||
// Build compaction result
|
||||
compaction_result_->output_level = compact_->compaction->output_level();
|
||||
compaction_result_->output_path = output_path_;
|
||||
if (status.ok()) {
|
||||
for (const auto& output_file : sub_compact->GetOutputs()) {
|
||||
auto& meta = output_file.meta;
|
||||
compaction_result_->output_files.emplace_back(
|
||||
MakeTableFileName(meta.fd.GetNumber()), meta.fd.smallest_seqno,
|
||||
meta.fd.largest_seqno, meta.smallest.Encode().ToString(),
|
||||
meta.largest.Encode().ToString(), meta.oldest_ancester_time,
|
||||
meta.file_creation_time, meta.epoch_number, meta.file_checksum,
|
||||
meta.file_checksum_func_name, output_file.validator.GetHash(),
|
||||
meta.marked_for_compaction, meta.unique_id,
|
||||
*output_file.table_properties, output_file.is_proximal_level,
|
||||
meta.temperature);
|
||||
}
|
||||
for (const auto& output_file : sub_compact->GetOutputs()) {
|
||||
auto& meta = output_file.meta;
|
||||
compaction_result_->output_files.emplace_back(
|
||||
MakeTableFileName(meta.fd.GetNumber()), meta.fd.smallest_seqno,
|
||||
meta.fd.largest_seqno, meta.smallest.Encode().ToString(),
|
||||
meta.largest.Encode().ToString(), meta.oldest_ancester_time,
|
||||
meta.file_creation_time, meta.epoch_number,
|
||||
output_file.validator.GetHash(), meta.marked_for_compaction,
|
||||
meta.unique_id);
|
||||
}
|
||||
InternalStats::CompactionStatsFull compaction_stats;
|
||||
sub_compact->AggregateCompactionStats(compaction_stats);
|
||||
compaction_result_->num_output_records =
|
||||
compaction_stats.stats.num_output_records;
|
||||
compaction_result_->total_bytes = compaction_stats.TotalBytesWritten();
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionServiceCompactionJob::Run:0",
|
||||
&compaction_result_);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -480,9 +396,42 @@ static std::unordered_map<std::string, OptionTypeInfo> cfd_type_info = {
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo> cs_input_type_info = {
|
||||
{"cf_name",
|
||||
{offsetof(struct CompactionServiceInput, cf_name),
|
||||
OptionType::kEncodedString}},
|
||||
{"column_family",
|
||||
OptionTypeInfo::Struct(
|
||||
"column_family", &cfd_type_info,
|
||||
offsetof(struct CompactionServiceInput, column_family),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
|
||||
{"db_options",
|
||||
{offsetof(struct CompactionServiceInput, db_options),
|
||||
OptionType::kConfigurable, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone,
|
||||
[](const ConfigOptions& opts, const std::string& /*name*/,
|
||||
const std::string& value, void* addr) {
|
||||
auto options = static_cast<DBOptions*>(addr);
|
||||
return GetDBOptionsFromString(opts, DBOptions(), value, options);
|
||||
},
|
||||
[](const ConfigOptions& opts, const std::string& /*name*/,
|
||||
const void* addr, std::string* value) {
|
||||
const auto options = static_cast<const DBOptions*>(addr);
|
||||
std::string result;
|
||||
auto status = GetStringFromDBOptions(opts, *options, &result);
|
||||
*value = "{" + result + "}";
|
||||
return status;
|
||||
},
|
||||
[](const ConfigOptions& opts, const std::string& name, const void* addr1,
|
||||
const void* addr2, std::string* mismatch) {
|
||||
const auto this_one = static_cast<const DBOptions*>(addr1);
|
||||
const auto that_one = static_cast<const DBOptions*>(addr2);
|
||||
auto this_conf = DBOptionsAsConfigurable(*this_one);
|
||||
auto that_conf = DBOptionsAsConfigurable(*that_one);
|
||||
std::string mismatch_opt;
|
||||
bool result =
|
||||
this_conf->AreEquivalent(opts, that_conf.get(), &mismatch_opt);
|
||||
if (!result) {
|
||||
*mismatch = name + "." + mismatch_opt;
|
||||
}
|
||||
return result;
|
||||
}}},
|
||||
{"snapshots", OptionTypeInfo::Vector<uint64_t>(
|
||||
offsetof(struct CompactionServiceInput, snapshots),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
|
||||
@@ -510,10 +459,6 @@ static std::unordered_map<std::string, OptionTypeInfo> cs_input_type_info = {
|
||||
{"end",
|
||||
{offsetof(struct CompactionServiceInput, end), OptionType::kEncodedString,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
|
||||
{"options_file_number",
|
||||
{offsetof(struct CompactionServiceInput, options_file_number),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
@@ -550,14 +495,6 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
{offsetof(struct CompactionServiceOutputFile, epoch_number),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"file_checksum",
|
||||
{offsetof(struct CompactionServiceOutputFile, file_checksum),
|
||||
OptionType::kEncodedString, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"file_checksum_func_name",
|
||||
{offsetof(struct CompactionServiceOutputFile, file_checksum_func_name),
|
||||
OptionType::kEncodedString, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"paranoid_hash",
|
||||
{offsetof(struct CompactionServiceOutputFile, paranoid_hash),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
@@ -571,39 +508,7 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
offsetof(struct CompactionServiceOutputFile, unique_id),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
|
||||
{0, OptionType::kUInt64T})},
|
||||
{"table_properties",
|
||||
{offsetof(struct CompactionServiceOutputFile, table_properties),
|
||||
OptionType::kStruct, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone,
|
||||
[](const ConfigOptions& opts, const std::string& /*name*/,
|
||||
const std::string& value, void* addr) {
|
||||
auto table_properties = static_cast<TableProperties*>(addr);
|
||||
return TableProperties::Parse(opts, value, table_properties);
|
||||
},
|
||||
[](const ConfigOptions& opts, const std::string& /*name*/,
|
||||
const void* addr, std::string* value) {
|
||||
const auto table_properties =
|
||||
static_cast<const TableProperties*>(addr);
|
||||
std::string result;
|
||||
auto status = table_properties->Serialize(opts, &result);
|
||||
*value = "{" + result + "}";
|
||||
return status;
|
||||
},
|
||||
[](const ConfigOptions& opts, const std::string& /*name*/,
|
||||
const void* addr1, const void* addr2, std::string* mismatch) {
|
||||
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 = {
|
||||
@@ -650,10 +555,6 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
{offsetof(struct CompactionJobStats, is_manual_compaction),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"is_remote_compaction",
|
||||
{offsetof(struct CompactionJobStats, is_remote_compaction),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"total_input_bytes",
|
||||
{offsetof(struct CompactionJobStats, total_input_bytes),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
@@ -728,125 +629,6 @@ 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.
|
||||
@@ -941,6 +723,14 @@ static std::unordered_map<std::string, OptionTypeInfo> cs_result_type_info = {
|
||||
{offsetof(struct CompactionServiceResult, output_path),
|
||||
OptionType::kEncodedString, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"num_output_records",
|
||||
{offsetof(struct CompactionServiceResult, num_output_records),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"total_bytes",
|
||||
{offsetof(struct CompactionServiceResult, total_bytes),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"bytes_read",
|
||||
{offsetof(struct CompactionServiceResult, bytes_read),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
@@ -953,11 +743,6 @@ 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,
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/utilities/options_util.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -21,10 +21,8 @@ class MyTestCompactionService : public CompactionService {
|
||||
: db_path_(std::move(db_path)),
|
||||
options_(options),
|
||||
statistics_(statistics),
|
||||
start_info_("na", "na", "na", 0, Env::TOTAL, CompactionReason::kUnknown,
|
||||
false, false, false),
|
||||
wait_info_("na", "na", "na", 0, Env::TOTAL, CompactionReason::kUnknown,
|
||||
false, false, false),
|
||||
start_info_("na", "na", "na", 0, Env::TOTAL),
|
||||
wait_info_("na", "na", "na", 0, Env::TOTAL),
|
||||
listeners_(listeners),
|
||||
table_properties_collector_factories_(
|
||||
std::move(table_properties_collector_factories)) {}
|
||||
@@ -99,12 +97,8 @@ class MyTestCompactionService : public CompactionService {
|
||||
Status s =
|
||||
DB::OpenAndCompact(options, db_path_, db_path_ + "/" + scheduled_job_id,
|
||||
compaction_input, result, options_override);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (is_override_wait_result_) {
|
||||
*result = override_wait_result_;
|
||||
}
|
||||
result_ = *result;
|
||||
if (is_override_wait_result_) {
|
||||
*result = override_wait_result_;
|
||||
}
|
||||
compaction_num_.fetch_add(1);
|
||||
if (s.ok()) {
|
||||
@@ -114,13 +108,6 @@ class MyTestCompactionService : public CompactionService {
|
||||
}
|
||||
}
|
||||
|
||||
void CancelAwaitingJobs() override { canceled_ = true; }
|
||||
|
||||
void OnInstallation(const std::string& /*scheduled_job_id*/,
|
||||
CompactionServiceJobStatus status) override {
|
||||
final_updated_status_ = status;
|
||||
}
|
||||
|
||||
int GetCompactionNum() { return compaction_num_.load(); }
|
||||
|
||||
CompactionServiceJobInfo GetCompactionInfoForStart() { return start_info_; }
|
||||
@@ -148,15 +135,6 @@ class MyTestCompactionService : public CompactionService {
|
||||
}
|
||||
|
||||
void SetCanceled(bool canceled) { canceled_ = canceled; }
|
||||
bool GetCanceled() { return canceled_; }
|
||||
|
||||
void GetResult(CompactionServiceResult* deserialized) {
|
||||
CompactionServiceResult::Read(result_, deserialized).PermitUncheckedError();
|
||||
}
|
||||
|
||||
CompactionServiceJobStatus GetFinalCompactionServiceJobStatus() {
|
||||
return final_updated_status_.load();
|
||||
}
|
||||
|
||||
private:
|
||||
InstrumentedMutex mutex_;
|
||||
@@ -175,14 +153,11 @@ class MyTestCompactionService : public CompactionService {
|
||||
CompactionServiceJobStatus override_wait_status_ =
|
||||
CompactionServiceJobStatus::kFailure;
|
||||
bool is_override_wait_result_ = false;
|
||||
std::string result_;
|
||||
std::string override_wait_result_;
|
||||
std::vector<std::shared_ptr<EventListener>> listeners_;
|
||||
std::vector<std::shared_ptr<TablePropertiesCollectorFactory>>
|
||||
table_properties_collector_factories_;
|
||||
std::atomic_bool canceled_{false};
|
||||
std::atomic<CompactionServiceJobStatus> final_updated_status_{
|
||||
CompactionServiceJobStatus::kUseLocal};
|
||||
};
|
||||
|
||||
class CompactionServiceTest : public DBTestBase {
|
||||
@@ -197,15 +172,12 @@ class CompactionServiceTest : public DBTestBase {
|
||||
options->statistics = primary_statistics_;
|
||||
compactor_statistics_ = CreateDBStatistics();
|
||||
|
||||
auto my_cs = std::make_shared<MyTestCompactionService>(
|
||||
compaction_service_ = 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(); }
|
||||
@@ -283,8 +255,6 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), 1);
|
||||
ASSERT_EQ(CompactionServiceJobStatus::kSuccess,
|
||||
my_cs->GetFinalCompactionServiceJobStatus());
|
||||
|
||||
// make sure the compaction statistics is only recorded on the remote side
|
||||
ASSERT_GE(compactor_statistics->getTickerCount(COMPACT_WRITE_BYTES), 1);
|
||||
@@ -345,40 +315,9 @@ 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);
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
if (s.IsAborted()) {
|
||||
ASSERT_NOK(result.status);
|
||||
} else {
|
||||
ASSERT_OK(result.status);
|
||||
}
|
||||
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.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) {
|
||||
std::string file_name = result.output_path + "/" + output_file.file_name;
|
||||
|
||||
uint64_t file_size = 0;
|
||||
ASSERT_OK(options.env->GetFileSize(file_name, &file_size));
|
||||
ASSERT_GT(file_size, 0);
|
||||
total_size += file_size;
|
||||
}
|
||||
ASSERT_EQ(total_size, result.internal_stats.TotalBytesWritten());
|
||||
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_FALSE(result.stats.is_full_compaction);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -417,587 +356,6 @@ TEST_F(CompactionServiceTest, ManualCompaction) {
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
VerifyTestData();
|
||||
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CompactionOutputFileIOError) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::FinishCompactionOutputFile()::AfterFinish",
|
||||
[&](void* status) {
|
||||
// override status
|
||||
auto s = static_cast<Status*>(status);
|
||||
*s = Status::IOError("Injected IOError!");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
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();
|
||||
ASSERT_NOK(db_->CompactRange(CompactRangeOptions(), &start, &end));
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_NOK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, PreservedOptionsLocalCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
for (auto j = 0; j < 10; ++j) {
|
||||
ASSERT_OK(
|
||||
Put("foo" + std::to_string(i * 10 + j), rnd.RandomString(1024)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::ProcessKeyValueCompaction()::Processing", [&](void* arg) {
|
||||
auto compaction = static_cast<Compaction*>(arg);
|
||||
std::string options_file_name = OptionsFileName(
|
||||
dbname_,
|
||||
compaction->input_version()->version_set()->options_file_number());
|
||||
|
||||
// Change option twice to make sure the very first OPTIONS file gets
|
||||
// purged
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"level0_file_num_compaction_trigger", "4"}}));
|
||||
ASSERT_EQ(4, dbfull()->GetOptions().level0_file_num_compaction_trigger);
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"level0_file_num_compaction_trigger", "6"}}));
|
||||
ASSERT_EQ(6, dbfull()->GetOptions().level0_file_num_compaction_trigger);
|
||||
dbfull()->TEST_DeleteObsoleteFiles();
|
||||
|
||||
// For non-remote compactions, OPTIONS file can be deleted while
|
||||
// using option at the start of the compaction
|
||||
Status s = env_->FileExists(options_file_name);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
// Should be old value
|
||||
ASSERT_EQ(2, compaction->mutable_cf_options()
|
||||
.level0_file_num_compaction_trigger);
|
||||
ASSERT_TRUE(dbfull()->min_options_file_numbers_.empty());
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_TRUE(s.ok());
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, PreservedOptionsRemoteCompaction) {
|
||||
// For non-remote compaction do not preserve options file
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.disable_auto_compactions = true;
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
Random rnd(301);
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
for (auto j = 0; j < 10; ++j) {
|
||||
ASSERT_OK(
|
||||
Put("foo" + std::to_string(i * 10 + j), rnd.RandomString(1024)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"CompactionServiceTest::OptionsFileChanged",
|
||||
"DBImplSecondary::OpenAndCompact::BeforeLoadingOptions:1"}});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImplSecondary::OpenAndCompact::BeforeLoadingOptions:0",
|
||||
[&](void* arg) {
|
||||
auto options_file_number = static_cast<uint64_t*>(arg);
|
||||
// Change the option twice before the compaction run
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"level0_file_num_compaction_trigger", "4"}}));
|
||||
ASSERT_EQ(4, dbfull()->GetOptions().level0_file_num_compaction_trigger);
|
||||
ASSERT_TRUE(dbfull()->versions_->options_file_number() >
|
||||
*options_file_number);
|
||||
|
||||
// Change the option twice before the compaction run
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"level0_file_num_compaction_trigger", "5"}}));
|
||||
ASSERT_EQ(5, dbfull()->GetOptions().level0_file_num_compaction_trigger);
|
||||
ASSERT_TRUE(dbfull()->versions_->options_file_number() >
|
||||
*options_file_number);
|
||||
|
||||
TEST_SYNC_POINT("CompactionServiceTest::OptionsFileChanged");
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionServiceJob::ProcessKeyValueCompactionWithCompactionService",
|
||||
[&](void* arg) {
|
||||
auto input = static_cast<CompactionServiceInput*>(arg);
|
||||
std::string options_file_name =
|
||||
OptionsFileName(dbname_, input->options_file_number);
|
||||
|
||||
ASSERT_OK(env_->FileExists(options_file_name));
|
||||
ASSERT_FALSE(dbfull()->min_options_file_numbers_.empty());
|
||||
ASSERT_EQ(dbfull()->min_options_file_numbers_.front(),
|
||||
input->options_file_number);
|
||||
|
||||
DBOptions db_options;
|
||||
ConfigOptions config_options;
|
||||
std::vector<ColumnFamilyDescriptor> all_column_families;
|
||||
config_options.env = env_;
|
||||
ASSERT_OK(LoadOptionsFromFile(config_options, options_file_name,
|
||||
&db_options, &all_column_families));
|
||||
bool has_cf = false;
|
||||
for (auto& cf : all_column_families) {
|
||||
if (cf.name == input->cf_name) {
|
||||
// Should be old value
|
||||
ASSERT_EQ(2, cf.options.level0_file_num_compaction_trigger);
|
||||
has_cf = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(has_cf);
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::ProcessKeyValueCompaction()::Processing", [&](void* arg) {
|
||||
auto compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_EQ(2, compaction->mutable_cf_options()
|
||||
.level0_file_num_compaction_trigger);
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_TRUE(s.ok());
|
||||
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
class EventVerifier : public EventListener {
|
||||
public:
|
||||
explicit EventVerifier(uint64_t expected_num_input_records,
|
||||
size_t expected_num_input_files,
|
||||
uint64_t expected_num_output_records,
|
||||
size_t expected_num_output_files,
|
||||
const std::string& expected_smallest_output_key_prefix,
|
||||
const std::string& expected_largest_output_key_prefix,
|
||||
bool expected_is_remote_compaction_on_begin,
|
||||
bool expected_is_remote_compaction_on_complete)
|
||||
: expected_num_input_records_(expected_num_input_records),
|
||||
expected_num_input_files_(expected_num_input_files),
|
||||
expected_num_output_records_(expected_num_output_records),
|
||||
expected_num_output_files_(expected_num_output_files),
|
||||
expected_smallest_output_key_prefix_(
|
||||
expected_smallest_output_key_prefix),
|
||||
expected_largest_output_key_prefix_(expected_largest_output_key_prefix),
|
||||
expected_is_remote_compaction_on_begin_(
|
||||
expected_is_remote_compaction_on_begin),
|
||||
expected_is_remote_compaction_on_complete_(
|
||||
expected_is_remote_compaction_on_complete) {}
|
||||
void OnCompactionBegin(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(expected_num_input_files_, ci.input_files.size());
|
||||
ASSERT_EQ(expected_num_input_files_, ci.input_file_infos.size());
|
||||
ASSERT_EQ(expected_is_remote_compaction_on_begin_,
|
||||
ci.stats.is_remote_compaction);
|
||||
ASSERT_TRUE(ci.stats.is_manual_compaction);
|
||||
ASSERT_FALSE(ci.stats.is_full_compaction);
|
||||
}
|
||||
void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
ASSERT_GT(ci.stats.elapsed_micros, 0);
|
||||
ASSERT_GT(ci.stats.cpu_micros, 0);
|
||||
ASSERT_EQ(expected_num_input_records_, ci.stats.num_input_records);
|
||||
ASSERT_EQ(expected_num_input_files_, ci.stats.num_input_files);
|
||||
ASSERT_EQ(expected_num_output_records_, ci.stats.num_output_records);
|
||||
ASSERT_EQ(expected_num_output_files_, ci.stats.num_output_files);
|
||||
ASSERT_EQ(expected_smallest_output_key_prefix_,
|
||||
ci.stats.smallest_output_key_prefix);
|
||||
ASSERT_EQ(expected_largest_output_key_prefix_,
|
||||
ci.stats.largest_output_key_prefix);
|
||||
ASSERT_GT(ci.stats.total_input_bytes, 0);
|
||||
ASSERT_GT(ci.stats.total_output_bytes, 0);
|
||||
ASSERT_EQ(ci.stats.num_input_records,
|
||||
ci.stats.num_output_records + ci.stats.num_records_replaced);
|
||||
ASSERT_EQ(expected_is_remote_compaction_on_complete_,
|
||||
ci.stats.is_remote_compaction);
|
||||
ASSERT_TRUE(ci.stats.is_manual_compaction);
|
||||
ASSERT_FALSE(ci.stats.is_full_compaction);
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t expected_num_input_records_;
|
||||
size_t expected_num_input_files_;
|
||||
uint64_t expected_num_output_records_;
|
||||
size_t expected_num_output_files_;
|
||||
std::string expected_smallest_output_key_prefix_;
|
||||
std::string expected_largest_output_key_prefix_;
|
||||
bool expected_is_remote_compaction_on_begin_;
|
||||
bool expected_is_remote_compaction_on_complete_;
|
||||
};
|
||||
|
||||
TEST_F(CompactionServiceTest, VerifyStats) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
auto event_verifier = std::make_shared<EventVerifier>(
|
||||
30 /* expected_num_input_records */, 3 /* expected_num_input_files */,
|
||||
20 /* expected_num_output_records */, 1 /* expected_num_output_files */,
|
||||
"key00000" /* expected_smallest_output_key_prefix */,
|
||||
"key00001" /* expected_largest_output_key_prefix */,
|
||||
true /* expected_is_remote_compaction_on_begin */,
|
||||
true /* expected_is_remote_compaction_on_complete */);
|
||||
options.listeners.push_back(event_verifier);
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
std::string start_str = Key(0);
|
||||
std::string end_str = Key(1);
|
||||
Slice start(start_str);
|
||||
Slice end(end_str);
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
VerifyTestData();
|
||||
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, VerifyStatsLocalFallback) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
auto event_verifier = std::make_shared<EventVerifier>(
|
||||
30 /* expected_num_input_records */, 3 /* expected_num_input_files */,
|
||||
20 /* expected_num_output_records */, 1 /* expected_num_output_files */,
|
||||
"key00000" /* expected_smallest_output_key_prefix */,
|
||||
"key00001" /* expected_largest_output_key_prefix */,
|
||||
true /* expected_is_remote_compaction_on_begin */,
|
||||
false /* expected_is_remote_compaction_on_complete */);
|
||||
options.listeners.push_back(event_verifier);
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
my_cs->OverrideStartStatus(CompactionServiceJobStatus::kUseLocal);
|
||||
|
||||
std::string start_str = Key(0);
|
||||
std::string end_str = Key(1);
|
||||
Slice start(start_str);
|
||||
Slice end(end_str);
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
|
||||
// Remote Compaction did not happen
|
||||
ASSERT_EQ(my_cs->GetCompactionNum(), comp_num);
|
||||
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;
|
||||
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();
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
|
||||
CompactionServiceResult* compaction_result =
|
||||
*(static_cast<CompactionServiceResult**>(arg));
|
||||
ASSERT_TRUE(compaction_result != nullptr &&
|
||||
!compaction_result->output_files.empty());
|
||||
// Corrupt files here
|
||||
for (const auto& output_file : compaction_result->output_files) {
|
||||
std::string file_name =
|
||||
compaction_result->output_path + "/" + output_file.file_name;
|
||||
|
||||
uint64_t file_size = 0;
|
||||
Status s = options.env->GetFileSize(file_name, &file_size);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_GT(file_size, 0);
|
||||
|
||||
ASSERT_OK(test::CorruptFile(env_, file_name, 0,
|
||||
static_cast<int>(file_size),
|
||||
true /* verifyChecksum */));
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// CompactRange() should fail
|
||||
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
// On the worker side, the compaction is considered success
|
||||
// Verification is done on the primary side
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CorruptedOutputParanoidFileCheck) {
|
||||
for (bool paranoid_file_check_enabled : {false, true}) {
|
||||
SCOPED_TRACE("paranoid_file_check_enabled=" +
|
||||
std::to_string(paranoid_file_check_enabled));
|
||||
|
||||
Options options = CurrentOptions();
|
||||
Destroy(options);
|
||||
options.disable_auto_compactions = true;
|
||||
options.paranoid_file_checks = paranoid_file_check_enabled;
|
||||
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();
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
|
||||
CompactionServiceResult* compaction_result =
|
||||
*(static_cast<CompactionServiceResult**>(arg));
|
||||
ASSERT_TRUE(compaction_result != nullptr &&
|
||||
!compaction_result->output_files.empty());
|
||||
// Corrupt files here
|
||||
for (const auto& output_file : compaction_result->output_files) {
|
||||
std::string file_name =
|
||||
compaction_result->output_path + "/" + output_file.file_name;
|
||||
|
||||
// Corrupt very small range of bytes. This corruption is so small
|
||||
// that this isn't caught by default light-weight check
|
||||
ASSERT_OK(test::CorruptFile(env_, file_name, 0, 1,
|
||||
false /* verifyChecksum */));
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
|
||||
if (paranoid_file_check_enabled) {
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_EQ(Status::Corruption("Paranoid checksums do not match"), s);
|
||||
} else {
|
||||
// CompactRange() goes through if paranoid file check is not enabled
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
// On the worker side, the compaction is considered success
|
||||
// Verification is done on the primary side
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, TruncatedOutput) {
|
||||
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();
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
|
||||
CompactionServiceResult* compaction_result =
|
||||
*(static_cast<CompactionServiceResult**>(arg));
|
||||
ASSERT_TRUE(compaction_result != nullptr &&
|
||||
!compaction_result->output_files.empty());
|
||||
// Truncate files here
|
||||
for (const auto& output_file : compaction_result->output_files) {
|
||||
std::string file_name =
|
||||
compaction_result->output_path + "/" + output_file.file_name;
|
||||
|
||||
uint64_t file_size = 0;
|
||||
Status s = options.env->GetFileSize(file_name, &file_size);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_GT(file_size, 0);
|
||||
|
||||
ASSERT_OK(test::TruncateFile(env_, file_name, file_size / 2));
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// CompactRange() should fail
|
||||
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
// On the worker side, the compaction is considered success
|
||||
// Verification is done on the primary side
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CustomFileChecksum) {
|
||||
Options options = CurrentOptions();
|
||||
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
|
||||
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();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
|
||||
CompactionServiceResult* compaction_result =
|
||||
*(static_cast<CompactionServiceResult**>(arg));
|
||||
ASSERT_TRUE(compaction_result != nullptr &&
|
||||
!compaction_result->output_files.empty());
|
||||
// Validate Checksum files here
|
||||
for (const auto& output_file : compaction_result->output_files) {
|
||||
std::string file_name =
|
||||
compaction_result->output_path + "/" + output_file.file_name;
|
||||
|
||||
FileChecksumGenContext gen_context;
|
||||
gen_context.file_name = file_name;
|
||||
std::unique_ptr<FileChecksumGenerator> file_checksum_gen =
|
||||
options.file_checksum_gen_factory->CreateFileChecksumGenerator(
|
||||
gen_context);
|
||||
|
||||
std::unique_ptr<SequentialFile> file_reader;
|
||||
uint64_t file_size = 0;
|
||||
Status s = options.env->GetFileSize(file_name, &file_size);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_GT(file_size, 0);
|
||||
|
||||
s = options.env->NewSequentialFile(file_name, &file_reader,
|
||||
EnvOptions());
|
||||
ASSERT_OK(s);
|
||||
|
||||
Slice result;
|
||||
std::unique_ptr<char[]> scratch(new char[file_size]);
|
||||
s = file_reader->Read(file_size, &result, scratch.get());
|
||||
ASSERT_OK(s);
|
||||
|
||||
file_checksum_gen->Update(scratch.get(), result.size());
|
||||
file_checksum_gen->Finalize();
|
||||
|
||||
// Verify actual checksum and the func name
|
||||
ASSERT_EQ(file_checksum_gen->Name(),
|
||||
output_file.file_checksum_func_name);
|
||||
ASSERT_EQ(file_checksum_gen->GetChecksum(),
|
||||
output_file.file_checksum);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CancelCompactionOnRemoteSide) {
|
||||
@@ -1045,41 +403,6 @@ 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;
|
||||
@@ -1114,8 +437,6 @@ TEST_F(CompactionServiceTest, InvalidResult) {
|
||||
Slice end(end_str);
|
||||
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
|
||||
ASSERT_FALSE(s.ok());
|
||||
ASSERT_EQ(CompactionServiceJobStatus::kFailure,
|
||||
my_cs->GetFinalCompactionServiceJobStatus());
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, SubCompaction) {
|
||||
@@ -1202,67 +523,6 @@ 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;
|
||||
@@ -1326,20 +586,11 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
{file.db_path + "/" + file.name}, 2));
|
||||
info = my_cs->GetCompactionInfoForStart();
|
||||
ASSERT_EQ(Env::USER, info.priority);
|
||||
ASSERT_EQ(CompactionReason::kManualCompaction, info.compaction_reason);
|
||||
ASSERT_EQ(true, info.is_manual_compaction);
|
||||
ASSERT_EQ(false, info.is_full_compaction);
|
||||
ASSERT_EQ(true, info.bottommost_level);
|
||||
info = my_cs->GetCompactionInfoForWait();
|
||||
ASSERT_EQ(Env::USER, info.priority);
|
||||
ASSERT_EQ(CompactionReason::kManualCompaction, info.compaction_reason);
|
||||
ASSERT_EQ(true, info.is_manual_compaction);
|
||||
ASSERT_EQ(false, info.is_full_compaction);
|
||||
ASSERT_EQ(true, info.bottommost_level);
|
||||
|
||||
// Test priority BOTTOM
|
||||
env_->SetBackgroundThreads(1, Env::BOTTOM);
|
||||
// This will set bottommost_level = true but is_full_compaction = false
|
||||
options.num_levels = 2;
|
||||
ReopenWithCompactionService(&options);
|
||||
my_cs =
|
||||
@@ -1362,71 +613,9 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
info = my_cs->GetCompactionInfoForStart();
|
||||
ASSERT_EQ(CompactionReason::kLevelL0FilesNum, info.compaction_reason);
|
||||
ASSERT_EQ(false, info.is_manual_compaction);
|
||||
ASSERT_EQ(false, info.is_full_compaction);
|
||||
ASSERT_EQ(true, info.bottommost_level);
|
||||
ASSERT_EQ(Env::BOTTOM, info.priority);
|
||||
info = my_cs->GetCompactionInfoForWait();
|
||||
ASSERT_EQ(Env::BOTTOM, info.priority);
|
||||
ASSERT_EQ(CompactionReason::kLevelL0FilesNum, info.compaction_reason);
|
||||
ASSERT_EQ(false, info.is_manual_compaction);
|
||||
ASSERT_EQ(false, info.is_full_compaction);
|
||||
ASSERT_EQ(true, info.bottommost_level);
|
||||
|
||||
// Test Non-Bottommost Level
|
||||
options.num_levels = 4;
|
||||
ReopenWithCompactionService(&options);
|
||||
my_cs =
|
||||
static_cast_with_check<MyTestCompactionService>(GetCompactionService());
|
||||
|
||||
for (int i = 0; i < options.level0_file_num_compaction_trigger; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 10 + j;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
info = my_cs->GetCompactionInfoForStart();
|
||||
ASSERT_EQ(false, info.is_manual_compaction);
|
||||
ASSERT_EQ(false, info.is_full_compaction);
|
||||
ASSERT_EQ(false, info.bottommost_level);
|
||||
info = my_cs->GetCompactionInfoForWait();
|
||||
ASSERT_EQ(false, info.is_manual_compaction);
|
||||
ASSERT_EQ(false, info.is_full_compaction);
|
||||
ASSERT_EQ(false, info.bottommost_level);
|
||||
|
||||
// Test Full Compaction + Bottommost Level
|
||||
options.num_levels = 6;
|
||||
ReopenWithCompactionService(&options);
|
||||
my_cs =
|
||||
static_cast_with_check<MyTestCompactionService>(GetCompactionService());
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 10 + j;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
info = my_cs->GetCompactionInfoForStart();
|
||||
ASSERT_EQ(true, info.is_manual_compaction);
|
||||
ASSERT_EQ(true, info.is_full_compaction);
|
||||
ASSERT_EQ(true, info.bottommost_level);
|
||||
ASSERT_EQ(CompactionReason::kManualCompaction, info.compaction_reason);
|
||||
info = my_cs->GetCompactionInfoForWait();
|
||||
ASSERT_EQ(true, info.is_manual_compaction);
|
||||
ASSERT_EQ(true, info.is_full_compaction);
|
||||
ASSERT_EQ(true, info.bottommost_level);
|
||||
ASSERT_EQ(CompactionReason::kManualCompaction, info.compaction_reason);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, FallbackLocalAuto) {
|
||||
@@ -1516,40 +705,6 @@ 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& internal_stats,
|
||||
CompactionJobStats& job_stats) {
|
||||
InternalStats::CompactionStatsFull& compaction_stats,
|
||||
CompactionJobStats& compaction_job_stats) {
|
||||
for (const auto& sc : sub_compact_states) {
|
||||
sc.AggregateCompactionOutputStats(internal_stats);
|
||||
job_stats.Add(sc.compaction_job_stats);
|
||||
sc.AggregateCompactionStats(compaction_stats);
|
||||
compaction_job_stats.Add(sc.compaction_job_stats);
|
||||
}
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -29,8 +29,8 @@ class CompactionState {
|
||||
Status status;
|
||||
|
||||
void AggregateCompactionStats(
|
||||
InternalStats::CompactionStatsFull& internal_stats,
|
||||
CompactionJobStats& job_stats);
|
||||
InternalStats::CompactionStatsFull& compaction_stats,
|
||||
CompactionJobStats& compaction_job_stats);
|
||||
|
||||
explicit CompactionState(Compaction* c) : compaction(c) {}
|
||||
|
||||
|
||||
@@ -13,47 +13,30 @@
|
||||
#include "rocksdb/sst_partitioner.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
void SubcompactionState::AggregateCompactionOutputStats(
|
||||
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_);
|
||||
void SubcompactionState::AggregateCompactionStats(
|
||||
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_);
|
||||
}
|
||||
}
|
||||
|
||||
OutputIterator SubcompactionState::GetOutputs() const {
|
||||
return OutputIterator(proximal_level_outputs_.outputs_,
|
||||
return OutputIterator(penultimate_level_outputs_.outputs_,
|
||||
compaction_outputs_.outputs_);
|
||||
}
|
||||
|
||||
void SubcompactionState::Cleanup(Cache* cache) {
|
||||
proximal_level_outputs_.Cleanup();
|
||||
penultimate_level_outputs_.Cleanup();
|
||||
compaction_outputs_.Cleanup();
|
||||
|
||||
if (!status.ok()) {
|
||||
for (const auto& out : GetOutputs()) {
|
||||
// If this file was inserted into the table cache then remove it here
|
||||
// because this compaction was not committed. This is not strictly
|
||||
// required because of a backstop TableCache::Evict() in
|
||||
// PurgeObsoleteFiles() but is our opportunity to apply
|
||||
// uncache_aggressiveness. TODO: instead, put these files into the
|
||||
// VersionSet::obsolete_files_ pipeline so that they don't have to
|
||||
// be picked up by scanning the DB directory.
|
||||
TableCache::ReleaseObsolete(
|
||||
cache, out.meta.fd.GetNumber(), nullptr /*handle*/,
|
||||
compaction->mutable_cf_options().uncache_aggressiveness);
|
||||
// If this file was inserted into the table cache then remove
|
||||
// them here because this compaction was not committed.
|
||||
TableCache::Evict(cache, out.meta.fd.GetNumber());
|
||||
}
|
||||
}
|
||||
// TODO: sub_compact.io_status is not checked like status. Not sure if thats
|
||||
@@ -62,9 +45,9 @@ void SubcompactionState::Cleanup(Cache* cache) {
|
||||
}
|
||||
|
||||
Slice SubcompactionState::SmallestUserKey() const {
|
||||
if (proximal_level_outputs_.HasOutput()) {
|
||||
if (has_penultimate_level_outputs_) {
|
||||
Slice a = compaction_outputs_.SmallestUserKey();
|
||||
Slice b = proximal_level_outputs_.SmallestUserKey();
|
||||
Slice b = penultimate_level_outputs_.SmallestUserKey();
|
||||
if (a.empty()) {
|
||||
return b;
|
||||
}
|
||||
@@ -84,9 +67,9 @@ Slice SubcompactionState::SmallestUserKey() const {
|
||||
}
|
||||
|
||||
Slice SubcompactionState::LargestUserKey() const {
|
||||
if (proximal_level_outputs_.HasOutput()) {
|
||||
if (has_penultimate_level_outputs_) {
|
||||
Slice a = compaction_outputs_.LargestUserKey();
|
||||
Slice b = proximal_level_outputs_.LargestUserKey();
|
||||
Slice b = penultimate_level_outputs_.LargestUserKey();
|
||||
if (a.empty()) {
|
||||
return b;
|
||||
}
|
||||
@@ -106,13 +89,18 @@ Slice SubcompactionState::LargestUserKey() const {
|
||||
}
|
||||
|
||||
Status SubcompactionState::AddToOutput(
|
||||
const CompactionIterator& iter, bool use_proximal_output,
|
||||
const CompactionIterator& iter,
|
||||
const CompactionFileOpenFunc& open_file_func,
|
||||
const CompactionFileCloseFunc& 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);
|
||||
// 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);
|
||||
}
|
||||
|
||||
} // 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 proximal level outputs
|
||||
// 2. another for the penultimate 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 proximal level, `current` is changed to point to
|
||||
// `proximal_level_outputs`.
|
||||
// the penultimate level, `current` is changed to point to
|
||||
// `penultimate_level_outputs`.
|
||||
// The later operations uses `Current()` to get the target group.
|
||||
//
|
||||
// +----------+ +-----------------------------+ +---------+
|
||||
@@ -43,7 +43,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
// | | ... |
|
||||
// |
|
||||
// | +-----------------------------+ +---------+
|
||||
// +-------------> | proximal_level_outputs |----->| output |
|
||||
// +-------------> | penultimate_level_outputs |----->| output |
|
||||
// +-----------------------------+ +---------+
|
||||
// | ... |
|
||||
|
||||
@@ -78,21 +78,30 @@ class SubcompactionState {
|
||||
Slice LargestUserKey() const;
|
||||
|
||||
// Get all outputs from the subcompaction. For per_key_placement compaction,
|
||||
// it returns both the last level outputs and proximal level outputs.
|
||||
// it returns both the last level outputs and penultimate level outputs.
|
||||
OutputIterator GetOutputs() const;
|
||||
|
||||
// Assign range dels aggregator. The various tombstones will potentially
|
||||
// be filtered to different outputs.
|
||||
// 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).
|
||||
void AssignRangeDelAggregator(
|
||||
std::unique_ptr<CompactionRangeDelAggregator>&& range_del_agg) {
|
||||
assert(range_del_agg_ == nullptr);
|
||||
assert(range_del_agg);
|
||||
range_del_agg_ = std::move(range_del_agg);
|
||||
if (compaction->SupportsPerKeyPlacement()) {
|
||||
penultimate_level_outputs_.AssignRangeDelAggregator(
|
||||
std::move(range_del_agg));
|
||||
} else {
|
||||
compaction_outputs_.AssignRangeDelAggregator(std::move(range_del_agg));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveLastEmptyOutput() {
|
||||
compaction_outputs_.RemoveLastEmptyOutput();
|
||||
proximal_level_outputs_.RemoveLastEmptyOutput();
|
||||
penultimate_level_outputs_.RemoveLastEmptyOutput();
|
||||
}
|
||||
|
||||
void BuildSubcompactionJobInfo(
|
||||
@@ -119,14 +128,14 @@ class SubcompactionState {
|
||||
start(_start),
|
||||
end(_end),
|
||||
sub_job_id(_sub_job_id),
|
||||
compaction_outputs_(c, /*is_proximal_level=*/false),
|
||||
proximal_level_outputs_(c, /*is_proximal_level=*/true) {
|
||||
compaction_outputs_(c, /*is_penultimate_level=*/false),
|
||||
penultimate_level_outputs_(c, /*is_penultimate_level=*/true) {
|
||||
assert(compaction != nullptr);
|
||||
// Set output split key (used for RoundRobin feature) only for normal
|
||||
// compaction_outputs, output to proximal_level feature doesn't support
|
||||
// compaction_outputs, output to penultimate_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_proximal_level).
|
||||
// per-key placement with output_to_penultimate_level).
|
||||
compaction_outputs_.SetOutputSlitKey(start, end);
|
||||
}
|
||||
|
||||
@@ -141,17 +150,27 @@ 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_)),
|
||||
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_
|
||||
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_
|
||||
: &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 : proximal_level_outputs_.outputs_) {
|
||||
out_edit->AddFile(compaction->GetProximalLevel(), file.meta);
|
||||
for (const auto& file : penultimate_level_outputs_.outputs_) {
|
||||
out_edit->AddFile(compaction->GetPenultimateLevel(), file.meta);
|
||||
}
|
||||
for (const auto& file : compaction_outputs_.outputs_) {
|
||||
out_edit->AddFile(compaction->output_level(), file.meta);
|
||||
@@ -160,75 +179,42 @@ class SubcompactionState {
|
||||
|
||||
void Cleanup(Cache* cache);
|
||||
|
||||
void AggregateCompactionOutputStats(
|
||||
InternalStats::CompactionStatsFull& internal_stats) const;
|
||||
void AggregateCompactionStats(
|
||||
InternalStats::CompactionStatsFull& compaction_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, bool use_proximal_output,
|
||||
Status AddToOutput(const CompactionIterator& iter,
|
||||
const CompactionFileOpenFunc& open_file_func,
|
||||
const CompactionFileCloseFunc& close_file_func);
|
||||
|
||||
// Close all compaction output files, both output_to_proximal_level outputs
|
||||
// Close all compaction output files, both output_to_penultimate_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.
|
||||
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);
|
||||
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);
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
// State kept for output being generated
|
||||
CompactionOutputs compaction_outputs_;
|
||||
CompactionOutputs proximal_level_outputs_;
|
||||
CompactionOutputs penultimate_level_outputs_;
|
||||
CompactionOutputs* current_outputs_ = &compaction_outputs_;
|
||||
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg_;
|
||||
bool is_current_penultimate_level_ = false;
|
||||
bool has_penultimate_level_outputs_ = false;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
File diff suppressed because it is too large.
Load diff
+2
-15
@@ -4,6 +4,7 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
|
||||
|
||||
#include "rocksdb/convenience.h"
|
||||
|
||||
#include "db/convenience_impl.h"
|
||||
@@ -26,17 +27,6 @@ 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);
|
||||
}
|
||||
@@ -81,9 +71,6 @@ Status VerifySstFileChecksumInternal(const Options& options,
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
std::unique_ptr<TableReader> table_reader;
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(
|
||||
@@ -97,7 +84,7 @@ Status VerifySstFileChecksumInternal(const Options& options,
|
||||
options.block_protection_bytes_per_key, false /* skip_filters */,
|
||||
!kImmortal, false /* force_direct_prefetch */, -1 /* level */);
|
||||
reader_options.largest_seqno = largest_seqno;
|
||||
s = options.table_factory->NewTableReader(
|
||||
s = ioptions.table_factory->NewTableReader(
|
||||
read_options, reader_options, std::move(file_reader), file_size,
|
||||
&table_reader, false /* prefetch_index_and_filter_in_cache */);
|
||||
if (!s.ok()) {
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "rocksdb/options.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
@@ -23,7 +25,6 @@
|
||||
#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"
|
||||
@@ -851,9 +852,6 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
// Skip verifying record count against TableProperties for
|
||||
// MockTables
|
||||
options.compaction_verify_record_count = false;
|
||||
Status s;
|
||||
for (const auto& mode : corruption_modes) {
|
||||
delete db_;
|
||||
@@ -866,6 +864,7 @@ 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,6 +3,7 @@
|
||||
// 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"
|
||||
@@ -348,3 +349,4 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
+84
-436
@@ -161,7 +161,6 @@ TEST_F(DBBasicTest, UniqueSession) {
|
||||
|
||||
ASSERT_EQ(sid2, sid3);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"goku"}, options);
|
||||
ASSERT_OK(db_->GetDbSessionId(sid1));
|
||||
ASSERT_OK(Put("bar", "e1"));
|
||||
@@ -180,7 +179,6 @@ 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();
|
||||
|
||||
@@ -210,11 +208,10 @@ TEST_F(DBBasicTest, ReadOnlyDB) {
|
||||
|
||||
auto options = CurrentOptions();
|
||||
assert(options.env == env_);
|
||||
ASSERT_OK(EnforcedReadOnlyReopen(options));
|
||||
ASSERT_OK(ReadOnlyReopen(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.
|
||||
@@ -222,38 +219,26 @@ TEST_F(DBBasicTest, ReadOnlyDB) {
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
// Now check keys in read only mode.
|
||||
ASSERT_OK(EnforcedReadOnlyReopen(options));
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
ASSERT_EQ("v2", Get("bar"));
|
||||
verify_all_iters();
|
||||
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);
|
||||
ASSERT_TRUE(db_->SyncWAL().IsNotSupported());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, ReadOnlyDBWithWriteDBIdToManifestSet) {
|
||||
auto options = CurrentOptions();
|
||||
options.write_dbid_to_manifest = false;
|
||||
DestroyAndReopen(options);
|
||||
// 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) {
|
||||
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(EnforcedReadOnlyReopen(options));
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
std::string db_id1;
|
||||
ASSERT_OK(db_->GetDbIdentity(db_id1));
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
@@ -273,7 +258,7 @@ TEST_F(DBBasicTest, ReadOnlyDBWithWriteDBIdToManifestSet) {
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
// Now check keys in read only mode.
|
||||
ASSERT_OK(EnforcedReadOnlyReopen(options));
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
ASSERT_EQ("v2", Get("bar"));
|
||||
ASSERT_TRUE(db_->SyncWAL().IsNotSupported());
|
||||
@@ -703,100 +688,76 @@ TEST_F(DBBasicTest, IdentityAcrossRestarts) {
|
||||
constexpr size_t kMinIdSize = 10;
|
||||
do {
|
||||
for (bool with_manifest : {false, true}) {
|
||||
for (bool write_file : {false, true}) {
|
||||
std::string idfilename = IdentityFileName(dbname_);
|
||||
std::string id1, tmp;
|
||||
ASSERT_OK(db_->GetDbIdentity(id1));
|
||||
ASSERT_GE(id1.size(), kMinIdSize);
|
||||
std::string idfilename = IdentityFileName(dbname_);
|
||||
std::string id1, tmp;
|
||||
ASSERT_OK(db_->GetDbIdentity(id1));
|
||||
ASSERT_GE(id1.size(), kMinIdSize);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.write_dbid_to_manifest = with_manifest;
|
||||
options.write_identity_file = true; // initially
|
||||
Reopen(options);
|
||||
std::string id2;
|
||||
ASSERT_OK(db_->GetDbIdentity(id2));
|
||||
// id2 should match id1 because identity was not regenerated
|
||||
ASSERT_EQ(id1, id2);
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id2);
|
||||
Options options = CurrentOptions();
|
||||
options.write_dbid_to_manifest = with_manifest;
|
||||
Reopen(options);
|
||||
std::string id2;
|
||||
ASSERT_OK(db_->GetDbIdentity(id2));
|
||||
// id2 should match id1 because identity was not regenerated
|
||||
ASSERT_EQ(id1, id2);
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id2);
|
||||
|
||||
if (write_file) {
|
||||
// Recover from deleted/missing IDENTITY
|
||||
ASSERT_OK(env_->DeleteFile(idfilename));
|
||||
} else {
|
||||
// Transition to no IDENTITY file
|
||||
options.write_identity_file = false;
|
||||
if (!with_manifest) {
|
||||
// Incompatible options, should fail
|
||||
ASSERT_NOK(TryReopen(options));
|
||||
// Back to a usable config and continue
|
||||
options.write_identity_file = true;
|
||||
Reopen(options);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Reopen(options);
|
||||
std::string id3;
|
||||
ASSERT_OK(db_->GetDbIdentity(id3));
|
||||
if (with_manifest) {
|
||||
// id3 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id3);
|
||||
} else {
|
||||
// id3 should NOT match id1 because identity was regenerated
|
||||
ASSERT_NE(id1, id3);
|
||||
ASSERT_GE(id3.size(), kMinIdSize);
|
||||
}
|
||||
if (write_file) {
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id3);
|
||||
|
||||
// Recover from truncated IDENTITY
|
||||
std::unique_ptr<WritableFile> w;
|
||||
ASSERT_OK(env_->NewWritableFile(idfilename, &w, EnvOptions()));
|
||||
ASSERT_OK(w->Close());
|
||||
} else {
|
||||
ASSERT_TRUE(env_->FileExists(idfilename).IsNotFound());
|
||||
}
|
||||
Reopen(options);
|
||||
std::string id4;
|
||||
ASSERT_OK(db_->GetDbIdentity(id4));
|
||||
if (with_manifest) {
|
||||
// id4 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id4);
|
||||
} else {
|
||||
// id4 should NOT match id1 because identity was regenerated
|
||||
ASSERT_NE(id1, id4);
|
||||
ASSERT_GE(id4.size(), kMinIdSize);
|
||||
}
|
||||
std::string silly_id = "asdf123456789";
|
||||
if (write_file) {
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id4);
|
||||
|
||||
// Recover from overwritten IDENTITY
|
||||
std::unique_ptr<WritableFile> w;
|
||||
ASSERT_OK(env_->NewWritableFile(idfilename, &w, EnvOptions()));
|
||||
ASSERT_OK(w->Append(silly_id));
|
||||
ASSERT_OK(w->Close());
|
||||
} else {
|
||||
ASSERT_TRUE(env_->FileExists(idfilename).IsNotFound());
|
||||
}
|
||||
Reopen(options);
|
||||
std::string id5;
|
||||
ASSERT_OK(db_->GetDbIdentity(id5));
|
||||
if (with_manifest) {
|
||||
// id4 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id5);
|
||||
} else {
|
||||
ASSERT_EQ(id5, silly_id);
|
||||
}
|
||||
if (write_file) {
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id5);
|
||||
} else {
|
||||
ASSERT_TRUE(env_->FileExists(idfilename).IsNotFound());
|
||||
}
|
||||
// Recover from deleted/missing IDENTITY
|
||||
ASSERT_OK(env_->DeleteFile(idfilename));
|
||||
Reopen(options);
|
||||
std::string id3;
|
||||
ASSERT_OK(db_->GetDbIdentity(id3));
|
||||
if (with_manifest) {
|
||||
// id3 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id3);
|
||||
} else {
|
||||
// id3 should NOT match id1 because identity was regenerated
|
||||
ASSERT_NE(id1, id3);
|
||||
ASSERT_GE(id3.size(), kMinIdSize);
|
||||
}
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id3);
|
||||
|
||||
// Recover from truncated IDENTITY
|
||||
{
|
||||
std::unique_ptr<WritableFile> w;
|
||||
ASSERT_OK(env_->NewWritableFile(idfilename, &w, EnvOptions()));
|
||||
ASSERT_OK(w->Close());
|
||||
}
|
||||
Reopen(options);
|
||||
std::string id4;
|
||||
ASSERT_OK(db_->GetDbIdentity(id4));
|
||||
if (with_manifest) {
|
||||
// id4 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id4);
|
||||
} else {
|
||||
// id4 should NOT match id1 because identity was regenerated
|
||||
ASSERT_NE(id1, id4);
|
||||
ASSERT_GE(id4.size(), kMinIdSize);
|
||||
}
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id4);
|
||||
|
||||
// Recover from overwritten IDENTITY
|
||||
std::string silly_id = "asdf123456789";
|
||||
{
|
||||
std::unique_ptr<WritableFile> w;
|
||||
ASSERT_OK(env_->NewWritableFile(idfilename, &w, EnvOptions()));
|
||||
ASSERT_OK(w->Append(silly_id));
|
||||
ASSERT_OK(w->Close());
|
||||
}
|
||||
Reopen(options);
|
||||
std::string id5;
|
||||
ASSERT_OK(db_->GetDbIdentity(id5));
|
||||
if (with_manifest) {
|
||||
// id4 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id5);
|
||||
} else {
|
||||
ASSERT_EQ(id5, silly_id);
|
||||
}
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id5);
|
||||
}
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
@@ -893,6 +854,7 @@ TEST_F(DBBasicTest, Snapshot) {
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
|
||||
class DBBasicMultiConfigs : public DBBasicTest,
|
||||
public ::testing::WithParamInterface<int> {
|
||||
public:
|
||||
@@ -2618,7 +2580,8 @@ 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
|
||||
@@ -3288,7 +3251,8 @@ TEST_F(DBBasicTest, GetAllKeyVersions) {
|
||||
ASSERT_OK(Delete(std::to_string(i)));
|
||||
}
|
||||
std::vector<KeyVersion> key_versions;
|
||||
ASSERT_OK(GetAllKeyVersions(db_, {}, {}, std::numeric_limits<size_t>::max(),
|
||||
ASSERT_OK(GetAllKeyVersions(db_, Slice(), Slice(),
|
||||
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++) {
|
||||
@@ -3298,7 +3262,7 @@ TEST_F(DBBasicTest, GetAllKeyVersions) {
|
||||
ASSERT_EQ(key_versions[i].GetTypeName(), "TypeValue");
|
||||
}
|
||||
}
|
||||
ASSERT_OK(GetAllKeyVersions(db_, handles_[0], {}, {},
|
||||
ASSERT_OK(GetAllKeyVersions(db_, handles_[0], Slice(), Slice(),
|
||||
std::numeric_limits<size_t>::max(),
|
||||
&key_versions));
|
||||
ASSERT_EQ(kNumInserts + kNumDeletes + kNumUpdates, key_versions.size());
|
||||
@@ -3313,17 +3277,10 @@ 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], {}, {},
|
||||
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);
|
||||
ASSERT_EQ(kNumInserts + kNumDeletes + kNumUpdates - 3, key_versions.size());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, ValueTypeString) {
|
||||
@@ -3375,69 +3332,6 @@ 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);
|
||||
@@ -3513,46 +3407,6 @@ class TableFileListener : public EventListener {
|
||||
InstrumentedMutex mutex_;
|
||||
std::unordered_map<std::string, std::vector<std::string>> cf_to_paths_;
|
||||
};
|
||||
|
||||
class FlushTableFileListener : public EventListener {
|
||||
public:
|
||||
void OnTableFileCreated(const TableFileCreationInfo& info) override {
|
||||
InstrumentedMutexLock lock(&mutex_);
|
||||
if (info.reason != TableFileCreationReason::kFlush) {
|
||||
return;
|
||||
}
|
||||
cf_to_flushed_files_[info.cf_name].push_back(info.file_path);
|
||||
}
|
||||
std::vector<std::string>& GetFlushedFiles(const std::string& cf_name) {
|
||||
InstrumentedMutexLock lock(&mutex_);
|
||||
return cf_to_flushed_files_[cf_name];
|
||||
}
|
||||
|
||||
private:
|
||||
InstrumentedMutex mutex_;
|
||||
std::unordered_map<std::string, std::vector<std::string>>
|
||||
cf_to_flushed_files_;
|
||||
};
|
||||
|
||||
class FlushBlobFileListener : public EventListener {
|
||||
public:
|
||||
void OnBlobFileCreated(const BlobFileCreationInfo& info) override {
|
||||
InstrumentedMutexLock lock(&mutex_);
|
||||
if (info.reason != BlobFileCreationReason::kFlush) {
|
||||
return;
|
||||
}
|
||||
cf_to_flushed_blobs_files_[info.cf_name].push_back(info.file_path);
|
||||
}
|
||||
std::vector<std::string>& GetFlushedBlobFiles(const std::string& cf_name) {
|
||||
InstrumentedMutexLock lock(&mutex_);
|
||||
return cf_to_flushed_blobs_files_[cf_name];
|
||||
}
|
||||
|
||||
private:
|
||||
InstrumentedMutex mutex_;
|
||||
std::unordered_map<std::string, std::vector<std::string>>
|
||||
cf_to_flushed_blobs_files_;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBasicTest, LastSstFileNotInManifest) {
|
||||
@@ -3658,121 +3512,6 @@ TEST_F(DBBasicTest, RecoverWithMissingFiles) {
|
||||
}
|
||||
}
|
||||
|
||||
// Param 0: whether to enable blob DB.
|
||||
// Param 1: when blob DB is enabled, whether to also delete the missing L0
|
||||
// file's associated blob file.
|
||||
class BestEffortsRecoverIncompleteVersionTest
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
BestEffortsRecoverIncompleteVersionTest()
|
||||
: DBTestBase("best_efforts_recover_incomplete_version_test",
|
||||
/*env_do_fsync=*/false) {}
|
||||
};
|
||||
|
||||
TEST_P(BestEffortsRecoverIncompleteVersionTest, Basic) {
|
||||
Options options = CurrentOptions();
|
||||
options.enable_blob_files = std::get<0>(GetParam());
|
||||
bool delete_blob_file_too = std::get<1>(GetParam());
|
||||
DestroyAndReopen(options);
|
||||
FlushTableFileListener* flush_table_listener = new FlushTableFileListener();
|
||||
FlushBlobFileListener* flush_blob_listener = new FlushBlobFileListener();
|
||||
// Disable auto compaction to simplify SST file name tracking.
|
||||
options.disable_auto_compactions = true;
|
||||
options.listeners.emplace_back(flush_table_listener);
|
||||
options.listeners.emplace_back(flush_blob_listener);
|
||||
CreateAndReopenWithCF({"pikachu", "eevee"}, options);
|
||||
std::vector<std::string> all_cf_names = {kDefaultColumnFamilyName, "pikachu",
|
||||
"eevee"};
|
||||
int num_cfs = static_cast<int>(handles_.size());
|
||||
ASSERT_EQ(3, num_cfs);
|
||||
std::string start = "a";
|
||||
Slice start_slice = start;
|
||||
std::string end = "d";
|
||||
Slice end_slice = end;
|
||||
for (int cf = 0; cf != num_cfs; ++cf) {
|
||||
ASSERT_OK(Put(cf, "a", "a_value"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
// Compact file to L1 to avoid trivial file move in the next compaction
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), handles_[cf],
|
||||
&start_slice, &end_slice));
|
||||
ASSERT_OK(Put(cf, "a", "a_value_new"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
ASSERT_OK(Put(cf, "b", "b_value"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
ASSERT_OK(Put(cf, "f", "f_value"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), handles_[cf],
|
||||
&start_slice, &end_slice));
|
||||
}
|
||||
|
||||
dbfull()->TEST_DeleteObsoleteFiles();
|
||||
|
||||
// Delete the most recent L0 file which is before a compaction.
|
||||
for (int i = 0; i < num_cfs; ++i) {
|
||||
std::vector<std::string>& files =
|
||||
flush_table_listener->GetFlushedFiles(all_cf_names[i]);
|
||||
ASSERT_EQ(4, files.size());
|
||||
ASSERT_OK(env_->DeleteFile(files[files.size() - 1]));
|
||||
if (options.enable_blob_files) {
|
||||
std::vector<std::string>& blob_files =
|
||||
flush_blob_listener->GetFlushedBlobFiles(all_cf_names[i]);
|
||||
ASSERT_EQ(4, blob_files.size());
|
||||
if (delete_blob_file_too) {
|
||||
ASSERT_OK(env_->DeleteFile(blob_files[files.size() - 1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
options.best_efforts_recovery = true;
|
||||
ReopenWithColumnFamilies(all_cf_names, options);
|
||||
|
||||
for (int i = 0; i < num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
VersionStorageInfo* vstorage = cfd->current()->storage_info();
|
||||
// The L0 file flushed right before the last compaction is missing.
|
||||
ASSERT_EQ(0, vstorage->LevelFiles(0).size());
|
||||
// Only the output of the last compaction is available.
|
||||
ASSERT_EQ(1, vstorage->LevelFiles(1).size());
|
||||
}
|
||||
// Verify data
|
||||
ReadOptions read_opts;
|
||||
read_opts.total_order_seek = true;
|
||||
for (int i = 0; i < num_cfs; ++i) {
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts, handles_[i]));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("a", iter->key());
|
||||
ASSERT_EQ("a_value_new", iter->value());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("b", iter->key());
|
||||
ASSERT_EQ("b_value", iter->value());
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// Write more data.
|
||||
for (int cf = 0; cf < num_cfs; ++cf) {
|
||||
ASSERT_OK(Put(cf, "g", "g_value"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), handles_[cf], nullptr,
|
||||
nullptr));
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(ReadOptions(), handles_[cf], "g", &value));
|
||||
ASSERT_EQ("g_value", value);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(BestEffortsRecoverIncompleteVersionTest,
|
||||
BestEffortsRecoverIncompleteVersionTest,
|
||||
testing::Values(std::make_tuple(false, false),
|
||||
std::make_tuple(true, false),
|
||||
std::make_tuple(true, true)));
|
||||
|
||||
TEST_F(DBBasicTest, BestEffortsRecoveryTryMultipleManifests) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -5078,97 +4817,6 @@ 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) {
|
||||
|
||||
+144
-333
@@ -26,7 +26,6 @@
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
#include "test_util/secondary_cache_test_util.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/hash.h"
|
||||
@@ -305,6 +304,7 @@ 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) {
|
||||
@@ -562,7 +562,7 @@ TEST_P(DBBlockCacheTest1, WarmCacheWithBlocksDuringFlush) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, DynamicOptions) {
|
||||
TEST_F(DBBlockCacheTest, DynamicallyWarmCacheDuringFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
@@ -577,74 +577,39 @@ TEST_F(DBBlockCacheTest, DynamicOptions) {
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::string value(kValueSize, 'a');
|
||||
auto st = options.statistics;
|
||||
|
||||
size_t i = 1;
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
for (size_t i = 1; i <= 5; i++) {
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(
|
||||
0, options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
}
|
||||
|
||||
++i;
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"block_based_table_factory", "{prepopulate_block_cache=kDisable;}"}}));
|
||||
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
for (size_t i = 6; i <= kNumBlocks; i++) {
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
++i;
|
||||
ASSERT_OK(dbfull()->SetOptions({{"block_based_table_factory",
|
||||
"{prepopulate_block_cache=kFlushOnly;}"}}));
|
||||
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
++i;
|
||||
// NOT YET SUPPORTED
|
||||
// FIXME: find a way to make this fail again (until well supported)
|
||||
// ASSERT_NOK(dbfull()->SetOptions(
|
||||
// {{"block_based_table_factory", "{block_cache=null;}"}}));
|
||||
|
||||
// ASSERT_OK(Put(std::to_string(i), value));
|
||||
// ASSERT_OK(Flush());
|
||||
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
// ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
// ++i;
|
||||
|
||||
// NOT YET SUPPORTED
|
||||
// FIXME: find a way to make this fail again (until well supported)
|
||||
// ASSERT_NOK(dbfull()->SetOptions(
|
||||
// {{"block_based_table_factory", "{block_cache=1M;}"}}));
|
||||
|
||||
// ASSERT_OK(Put(std::to_string(i), value));
|
||||
// ASSERT_OK(Flush());
|
||||
// ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
// ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
// ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(
|
||||
1, options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -775,6 +740,118 @@ class LookupLiarCache : public CacheWrapper {
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBlockCacheTest, AddRedundantStats) {
|
||||
const size_t capacity = size_t{1} << 25;
|
||||
const int num_shard_bits = 0; // 1 shard
|
||||
int iterations_tested = 0;
|
||||
for (const std::shared_ptr<Cache>& base_cache :
|
||||
{NewLRUCache(capacity, num_shard_bits),
|
||||
// FixedHyperClockCache
|
||||
HyperClockCacheOptions(
|
||||
capacity,
|
||||
BlockBasedTableOptions().block_size /*estimated_value_size*/,
|
||||
num_shard_bits)
|
||||
.MakeSharedCache(),
|
||||
// AutoHyperClockCache
|
||||
HyperClockCacheOptions(capacity, 0 /*estimated_value_size*/,
|
||||
num_shard_bits)
|
||||
.MakeSharedCache()}) {
|
||||
if (!base_cache) {
|
||||
// Skip clock cache when not supported
|
||||
continue;
|
||||
}
|
||||
++iterations_tested;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
|
||||
std::shared_ptr<LookupLiarCache> cache =
|
||||
std::make_shared<LookupLiarCache>(base_cache);
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(50));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Create a new table.
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
// Normal access filter+index+data.
|
||||
ASSERT_EQ("value", Get("foo"));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Againt access filter+index+data, but force redundant load+insert on index
|
||||
cache->SetNthLookupNotFound(2);
|
||||
ASSERT_EQ("value", Get("bar"));
|
||||
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(4, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just filter (with high probability), and force redundant
|
||||
// load+insert
|
||||
cache->SetNthLookupNotFound(1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("this key was not added"));
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(5, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just data, forcing redundant load+insert
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<Iterator> iter{db_->NewIterator(read_options)};
|
||||
cache->SetNthLookupNotFound(1);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), "bar");
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(6, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
}
|
||||
EXPECT_GE(iterations_tested, 1);
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, ParanoidFileChecks) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -835,6 +912,8 @@ 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) {
|
||||
@@ -1268,274 +1347,6 @@ TEST_F(DBBlockCacheTest, HyperClockCacheReportProblems) {
|
||||
EXPECT_EQ(logger->PopCounts(), (std::array<int, 3>{{0, 1, 0}}));
|
||||
}
|
||||
|
||||
class DBBlockCacheTypeTest
|
||||
: public DBBlockCacheTest,
|
||||
public secondary_cache_test_util::WithCacheTypeParam {};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBBlockCacheTypeTestInstance, DBBlockCacheTypeTest,
|
||||
secondary_cache_test_util::GetTestingCacheTypes());
|
||||
|
||||
TEST_P(DBBlockCacheTypeTest, AddRedundantStats) {
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
const size_t capacity = size_t{1} << 25;
|
||||
const int num_shard_bits = 0; // 1 shard
|
||||
estimated_value_size_ = table_options.block_size;
|
||||
std::shared_ptr<Cache> base_cache =
|
||||
NewCache(capacity, num_shard_bits, /*strict_capacity_limit=*/false);
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
|
||||
std::shared_ptr<LookupLiarCache> cache =
|
||||
std::make_shared<LookupLiarCache>(base_cache);
|
||||
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(50));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Create a new table.
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
// Normal access filter+index+data.
|
||||
ASSERT_EQ("value", Get("foo"));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Againt access filter+index+data, but force redundant load+insert on index
|
||||
cache->SetNthLookupNotFound(2);
|
||||
ASSERT_EQ("value", Get("bar"));
|
||||
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(4, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just filter (with high probability), and force redundant
|
||||
// load+insert
|
||||
cache->SetNthLookupNotFound(1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("this key was not added"));
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(5, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just data, forcing redundant load+insert
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<Iterator> iter{db_->NewIterator(read_options)};
|
||||
cache->SetNthLookupNotFound(1);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), "bar");
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(6, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string AltKey(int i) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "altkey%06d", i);
|
||||
return std::string(buf);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_P(DBBlockCacheTypeTest, Uncache) {
|
||||
for (bool partitioned : {false, true}) {
|
||||
SCOPED_TRACE("partitioned=" + std::to_string(partitioned));
|
||||
for (uint32_t ua : {0, 1, 2, 10000}) {
|
||||
SCOPED_TRACE("ua=" + std::to_string(ua));
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
Options options = CurrentOptions();
|
||||
options.uncache_aggressiveness = ua;
|
||||
options.create_if_missing = true;
|
||||
// Don't allow background operations to keep Versions referenced
|
||||
options.stats_dump_period_sec = 0;
|
||||
options.stats_persist_period_sec = 0;
|
||||
auto stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.statistics = stats;
|
||||
|
||||
const size_t capacity = size_t{1} << 25;
|
||||
const int num_shard_bits = 0; // 1 shard
|
||||
estimated_value_size_ = table_options.block_size;
|
||||
std::shared_ptr<Cache> cache =
|
||||
NewCache(capacity, num_shard_bits, /*strict_capacity_limit=*/false);
|
||||
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
table_options.partition_filters = partitioned;
|
||||
table_options.index_type =
|
||||
partitioned ? BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch
|
||||
: BlockBasedTableOptions::IndexType::kBinarySearch;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
size_t kBaselineCount = 1; // Because of entry stats collector
|
||||
|
||||
ASSERT_EQ(kBaselineCount, cache->GetOccupancyCount());
|
||||
ASSERT_EQ(0U, cache->GetUsage());
|
||||
|
||||
constexpr uint8_t kNumDataBlocks = 10;
|
||||
constexpr uint8_t kNumFiles = 3;
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
// Force some overlap with ordering
|
||||
ASSERT_OK(Put(Key((i * 7) % kNumDataBlocks),
|
||||
Random::GetTLSInstance()->RandomBinaryString(
|
||||
static_cast<int>(table_options.block_size))));
|
||||
if (i >= kNumDataBlocks - kNumFiles) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(int{kNumFiles}, NumTableFilesAtLevel(0));
|
||||
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
size_t meta_blocks_per_file = /*index & filter*/ 2U * (1U + partitioned);
|
||||
ASSERT_EQ(
|
||||
cache->GetOccupancyCount(),
|
||||
kBaselineCount + kNumDataBlocks + meta_blocks_per_file * kNumFiles);
|
||||
ASSERT_GE(cache->GetUsage(), kNumDataBlocks * table_options.block_size);
|
||||
|
||||
// Combine into one file, making the originals obsolete
|
||||
ASSERT_OK(db_->CompactRange({}, nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
if (ua == 0) {
|
||||
// Expect to see cache entries for new file and obsolete files
|
||||
EXPECT_EQ(cache->GetOccupancyCount(),
|
||||
kBaselineCount + kNumDataBlocks * 2U +
|
||||
meta_blocks_per_file * (kNumFiles + 1));
|
||||
EXPECT_GE(cache->GetUsage(),
|
||||
kNumDataBlocks * table_options.block_size * 2U);
|
||||
} else {
|
||||
// Expect only to see cache entries for new file
|
||||
EXPECT_EQ(cache->GetOccupancyCount(),
|
||||
kBaselineCount + kNumDataBlocks + meta_blocks_per_file);
|
||||
EXPECT_GE(cache->GetUsage(), kNumDataBlocks * table_options.block_size);
|
||||
EXPECT_LT(cache->GetUsage(),
|
||||
kNumDataBlocks * table_options.block_size * 2U);
|
||||
}
|
||||
|
||||
size_t alt_baseline_count = cache->GetOccupancyCount();
|
||||
size_t alt_baseline_usage = cache->GetUsage();
|
||||
ASSERT_OK(stats->Reset());
|
||||
// We aren't generally cleaning up cache entries on DB::Close, especially
|
||||
// because someone might just re-open the same DB.
|
||||
Reopen(options);
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
EXPECT_EQ(cache->GetOccupancyCount(), alt_baseline_count);
|
||||
EXPECT_EQ(cache->GetUsage(), alt_baseline_usage);
|
||||
|
||||
// Check for unnecessary unncessary cache churn
|
||||
ASSERT_EQ(stats->getTickerCount(BLOCK_CACHE_ADD), 0U);
|
||||
ASSERT_EQ(stats->getTickerCount(BLOCK_CACHE_MISS), 0U);
|
||||
ASSERT_GT(stats->getTickerCount(BLOCK_CACHE_HIT), 0U);
|
||||
|
||||
// And now do a similar test as above except with trivial moves, making
|
||||
// sure that we aren't falsely uncaching in that case, which would cause
|
||||
// unnecessary cache misses. Using AltKey instead of Key to avoid
|
||||
// interference.
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
// No overlap
|
||||
ASSERT_OK(
|
||||
Put(AltKey(i), Random::GetTLSInstance()->RandomBinaryString(
|
||||
static_cast<int>(table_options.block_size))));
|
||||
if (i >= kNumDataBlocks - kNumFiles) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(int{kNumFiles}, NumTableFilesAtLevel(0));
|
||||
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(AltKey(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
ASSERT_EQ(cache->GetOccupancyCount(),
|
||||
alt_baseline_count + kNumDataBlocks +
|
||||
meta_blocks_per_file * kNumFiles);
|
||||
ASSERT_GE(cache->GetUsage(),
|
||||
alt_baseline_usage + kNumDataBlocks * table_options.block_size);
|
||||
|
||||
ASSERT_OK(stats->Reset());
|
||||
|
||||
// Make trivial move
|
||||
{
|
||||
auto a = AltKey(0);
|
||||
auto b = AltKey(kNumDataBlocks);
|
||||
Slice slice_a{a};
|
||||
Slice slice_b{b};
|
||||
ASSERT_OK(db_->CompactRange({}, &slice_a, &slice_b));
|
||||
}
|
||||
ASSERT_EQ(/*old*/ 1 + /*new*/ int{kNumFiles}, NumTableFilesAtLevel(1));
|
||||
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(AltKey(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
// Should be the same if trivial move
|
||||
ASSERT_EQ(cache->GetOccupancyCount(),
|
||||
alt_baseline_count + kNumDataBlocks +
|
||||
meta_blocks_per_file * kNumFiles);
|
||||
|
||||
// Check for unnecessary unncessary cache churn
|
||||
ASSERT_EQ(stats->getTickerCount(BLOCK_CACHE_ADD), 0U);
|
||||
ASSERT_EQ(stats->getTickerCount(BLOCK_CACHE_MISS), 0U);
|
||||
ASSERT_GT(stats->getTickerCount(BLOCK_CACHE_HIT), 0U);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DBBlockCacheKeyTest
|
||||
: public DBTestBase,
|
||||
@@ -1755,8 +1566,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);
|
||||
|
||||
+561
-1107
File diff suppressed because it is too large.
Load diff
+50
-418
@@ -16,7 +16,6 @@
|
||||
#include "env/mock_env.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/advanced_options.h"
|
||||
#include "rocksdb/concurrent_task_limiter.h"
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
@@ -127,19 +126,6 @@ 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() {}
|
||||
@@ -1314,9 +1300,6 @@ 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);
|
||||
|
||||
@@ -1377,10 +1360,6 @@ 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_;
|
||||
|
||||
@@ -1428,11 +1407,6 @@ 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 = {
|
||||
@@ -1480,11 +1454,6 @@ 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_;
|
||||
@@ -2117,10 +2086,13 @@ 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);
|
||||
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);
|
||||
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);
|
||||
ASSERT_OK(DeleteFilesInRanges(db_, db_->DefaultColumnFamily(),
|
||||
ranges.data(), ranges.size()));
|
||||
ASSERT_EQ("0,3,7", FilesPerLevel(0));
|
||||
@@ -2168,7 +2140,7 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
|
||||
// Delete all files.
|
||||
{
|
||||
RangeOpt range;
|
||||
RangePtr range;
|
||||
ASSERT_OK(DeleteFilesInRanges(db_, db_->DefaultColumnFamily(), &range, 1));
|
||||
ASSERT_EQ("", FilesPerLevel(0));
|
||||
|
||||
@@ -5883,26 +5855,6 @@ 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')));
|
||||
@@ -5916,19 +5868,6 @@ 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) {
|
||||
@@ -5939,9 +5878,6 @@ 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*/,
|
||||
@@ -5951,9 +5887,6 @@ 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 {
|
||||
@@ -5983,25 +5916,10 @@ 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();
|
||||
@@ -6021,7 +5939,6 @@ 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++) {
|
||||
@@ -6031,18 +5948,11 @@ 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());
|
||||
@@ -6050,8 +5960,6 @@ 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) {
|
||||
@@ -6237,7 +6145,7 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
|
||||
std::vector<std::string> pending_compaction_cfs;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"EnqueuePendingCompaction::cfd", [&](void* arg) {
|
||||
"SchedulePendingCompaction::cfd", [&](void* arg) {
|
||||
const std::string& cf_name =
|
||||
static_cast<ColumnFamilyData*>(arg)->GetName();
|
||||
pending_compaction_cfs.emplace_back(cf_name);
|
||||
@@ -6296,14 +6204,29 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
NumTableFilesAtLevel(0, 0));
|
||||
}
|
||||
|
||||
// Wait until all CFs are pending compaction. WaitForFlushMemtable() can
|
||||
// return before the next compaction is scheduled, so we need to do some
|
||||
// waiting here.
|
||||
// All CFs are pending compaction
|
||||
unsigned int tp_len = env_->GetThreadPoolQueueLen(Env::LOW);
|
||||
for (int i = 0; i < 10000 && tp_len < cf_count; i++) {
|
||||
env_->SleepForMicroseconds(1000);
|
||||
tp_len = env_->GetThreadPoolQueueLen(Env::LOW);
|
||||
if (cf_count != tp_len) {
|
||||
// The test is flaky and fails the assertion below.
|
||||
// Print some debug information.
|
||||
uint64_t num_running_flushes = 0;
|
||||
if (db_->GetIntProperty(DB::Properties::kNumRunningFlushes,
|
||||
&num_running_flushes)) {
|
||||
fprintf(stdout, "Running flushes: %" PRIu64 "\n", num_running_flushes);
|
||||
}
|
||||
fprintf(stdout,
|
||||
"%zu CF in compaction queue: ", pending_compaction_cfs.size());
|
||||
for (const auto& cf_name : pending_compaction_cfs) {
|
||||
fprintf(stdout, "%s, ", cf_name.c_str());
|
||||
}
|
||||
fprintf(stdout, "\n");
|
||||
|
||||
// print lsm
|
||||
for (unsigned int cf = 0; cf < cf_count; cf++) {
|
||||
fprintf(stdout, "%s: %s\n", cf_names[cf], FilesPerLevel(cf).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(cf_count, tp_len);
|
||||
|
||||
// Unblock all compaction threads
|
||||
@@ -6498,40 +6421,20 @@ TEST_F(DBCompactionTest, PersistRoundRobinCompactCursor) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
TEST_P(RoundRobinSubcompactionsAgainstPressureToken, 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;
|
||||
// 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().
|
||||
// Target size is chosen so that filling the level with `kFilesPerLevel` files
|
||||
// will make it oversized by `kNumSubcompactions` files.
|
||||
options.max_bytes_for_level_base =
|
||||
(kFilesPerLevel - 10) * kKeysPerBuffer * 1024;
|
||||
(kFilesPerLevel - kNumSubcompactions) * kKeysPerBuffer * 1024;
|
||||
options.disable_auto_compactions = true;
|
||||
// Setup `kNumSubcompactions` threads but limited subcompactions so
|
||||
// that RoundRobin requires extra compactions from reserved threads
|
||||
@@ -6539,6 +6442,7 @@ TEST_P(RoundRobinSubcompactionsAgainstPressureToken,
|
||||
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--) {
|
||||
@@ -6556,34 +6460,21 @@ TEST_P(RoundRobinSubcompactionsAgainstPressureToken,
|
||||
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) {
|
||||
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;
|
||||
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;
|
||||
});
|
||||
|
||||
// The following 3 dependencies have to be added to ensure the auto
|
||||
@@ -7034,8 +6925,7 @@ TEST_F(DBCompactionTest, ManualCompactionMax) {
|
||||
DestroyAndReopen(opts);
|
||||
generate_sst_func();
|
||||
uint64_t total_size = (l1_avg_size * 10) + (l2_avg_size * 100);
|
||||
// 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.max_compaction_bytes = total_size / num_split;
|
||||
opts.target_file_size_base = total_size / num_split;
|
||||
Reopen(opts);
|
||||
num_compactions.store(0);
|
||||
@@ -7057,10 +6947,8 @@ 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 + 10 * l2_avg_size)},
|
||||
{{"max_compaction_bytes", std::to_string(total_size / num_split)},
|
||||
{"target_file_size_base", std::to_string(total_size / num_split)}});
|
||||
ASSERT_OK(s);
|
||||
|
||||
@@ -7247,8 +7135,7 @@ class DBCompactionTestWithOngoingFileIngestionParam
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"ExternalSstFileIngestionJob::Run", [&](void*) {
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactFilesImpl::"
|
||||
"PostSanitizeAndConvertCompactionInputFiles",
|
||||
{{"DBImpl::CompactFilesImpl::PostSanitizeCompactionInputFiles",
|
||||
"VersionSet::LogAndApply:WriteManifest"}});
|
||||
});
|
||||
} else {
|
||||
@@ -7730,72 +7617,6 @@ class DBCompactionTestL0FilesMisorderCorruption : public DBCompactionTest {
|
||||
std::shared_ptr<test::SleepingBackgroundTask> sleeping_task_;
|
||||
};
|
||||
|
||||
TEST_F(DBCompactionTest, CompactFilesSupportKeyPlacementRangeConflict) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = 3;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// To create LSM of below shape:
|
||||
// L0: [k2]
|
||||
// L1: [k3],[k4]
|
||||
// L2: [k1, k5]
|
||||
ASSERT_OK(Put("k1", "v"));
|
||||
ASSERT_OK(Put("k5", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,1", FilesPerLevel());
|
||||
|
||||
ASSERT_OK(Put("k3", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k4", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(experimental::PromoteL0(db_, db_->DefaultColumnFamily(), 1));
|
||||
ASSERT_EQ("0,2,1", FilesPerLevel());
|
||||
|
||||
ASSERT_OK(Put("k2", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("1,2,1", FilesPerLevel());
|
||||
|
||||
Close();
|
||||
|
||||
// To force below two CompactFiles() in order to coerce range conflict on L1
|
||||
// upon (2)
|
||||
// (1): Compact [k2] at L0 and [k3] at L1 with output to L1
|
||||
// (2): Compact [k4] at L1 and [k1, k5] at L2 and output to L1 and L2
|
||||
options.preclude_last_level_data_seconds = 1;
|
||||
Reopen(options);
|
||||
|
||||
ColumnFamilyMetaData cf_meta_data;
|
||||
db_->GetColumnFamilyMetaData(&cf_meta_data);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactFilesImpl:0", [&](void* /*arg*/) {
|
||||
std::vector<std::string> c2_input_files;
|
||||
c2_input_files.push_back(cf_meta_data.levels[1].files[1].name);
|
||||
c2_input_files.push_back(cf_meta_data.levels[2].files[0].name);
|
||||
// To verify CompactFiles() is aborted upon range conflict instead
|
||||
// of crashing upon internal assertion
|
||||
Status s = db_->CompactFiles(CompactionOptions(), c2_input_files,
|
||||
2 /* output_level */);
|
||||
ASSERT_TRUE(s.IsAborted());
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<std::string> c1_input_files;
|
||||
c1_input_files.push_back(cf_meta_data.levels[0].files[0].name);
|
||||
c1_input_files.push_back(cf_meta_data.levels[1].files[0].name);
|
||||
Status s = db_->CompactFiles(CompactionOptions(), c1_input_files,
|
||||
1 /* output_level */);
|
||||
ASSERT_OK(s);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTestL0FilesMisorderCorruption,
|
||||
FlushAfterIntraL0LevelCompactionWithIngestedFile) {
|
||||
SetupOptions(CompactionStyle::kCompactionStyleLevel, "");
|
||||
@@ -9483,13 +9304,12 @@ TEST_F(DBCompactionTest, FIFOChangeTemperature) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// First two L0 files both become eligible for temperature change compaction
|
||||
// They should be compacted one-by-one.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(1200);
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -10530,7 +10350,7 @@ TEST_F(DBCompactionTest, NumberOfSubcompactions) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, VerifyInputRecordCount) {
|
||||
TEST_F(DBCompactionTest, VerifyRecordCount) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
@@ -10568,103 +10388,6 @@ TEST_F(DBCompactionTest, VerifyInputRecordCount) {
|
||||
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.
|
||||
@@ -10846,97 +10569,6 @@ TEST_F(DBCompactionTest, ReleaseCompactionDuringManifestWrite) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, RecordNewestKeyTimeForTtlCompaction) {
|
||||
Options options;
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
options.env = CurrentOptions().env;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.write_buffer_size = 10 << 10; // 10KB
|
||||
options.arena_block_size = 4096;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.num_levels = 1;
|
||||
env_->SetMockSleep();
|
||||
options.env = env_;
|
||||
options.ttl = 1 * 60 * 60; // 1 hour
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
// Generate and flush 4 files, each about 10KB
|
||||
// Compaction is manually disabled at this point so we can check
|
||||
// each file's newest_key_time
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
env_->MockSleepForSeconds(5);
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 4);
|
||||
|
||||
// Check that we are populating newest_key_time on flush
|
||||
std::vector<FileMetaData*> file_metadatas = GetLevelFileMetadatas(0);
|
||||
ASSERT_EQ(file_metadatas.size(), 4);
|
||||
uint64_t first_newest_key_time =
|
||||
file_metadatas[0]->fd.table_reader->GetTableProperties()->newest_key_time;
|
||||
ASSERT_NE(first_newest_key_time, kUnknownNewestKeyTime);
|
||||
// Check that the newest_key_times are in expected ordering
|
||||
uint64_t prev_newest_key_time = first_newest_key_time;
|
||||
for (size_t idx = 1; idx < file_metadatas.size(); idx++) {
|
||||
uint64_t newest_key_time = file_metadatas[idx]
|
||||
->fd.table_reader->GetTableProperties()
|
||||
->newest_key_time;
|
||||
|
||||
ASSERT_LT(newest_key_time, prev_newest_key_time);
|
||||
prev_newest_key_time = newest_key_time;
|
||||
ASSERT_EQ(newest_key_time, file_metadatas[idx]
|
||||
->fd.table_reader->GetTableProperties()
|
||||
->creation_time);
|
||||
}
|
||||
// The delta between the first and last newest_key_times is 15s
|
||||
uint64_t last_newest_key_time = prev_newest_key_time;
|
||||
ASSERT_EQ(15, first_newest_key_time - last_newest_key_time);
|
||||
|
||||
// After compaction, the newest_key_time of the output file should be the max
|
||||
// of the input files
|
||||
options.compaction_options_fifo.allow_compaction = true;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
|
||||
file_metadatas = GetLevelFileMetadatas(0);
|
||||
ASSERT_EQ(file_metadatas.size(), 1);
|
||||
ASSERT_EQ(
|
||||
file_metadatas[0]->fd.table_reader->GetTableProperties()->newest_key_time,
|
||||
first_newest_key_time);
|
||||
// Contrast newest_key_time with creation_time, which records the oldest
|
||||
// ancestor time (15s older than newest_key_time)
|
||||
ASSERT_EQ(
|
||||
file_metadatas[0]->fd.table_reader->GetTableProperties()->creation_time,
|
||||
last_newest_key_time);
|
||||
ASSERT_EQ(file_metadatas[0]->oldest_ancester_time, last_newest_key_time);
|
||||
|
||||
// Make sure TTL of 5s causes compaction
|
||||
env_->MockSleepForSeconds(6);
|
||||
|
||||
// The oldest input file is older than 15s
|
||||
// However the newest of the compaction input files is younger than 15s, so
|
||||
// we don't compact
|
||||
ASSERT_OK(dbfull()->SetOptions({{"ttl", "15"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 15);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
|
||||
|
||||
// Now even the youngest input file is too old
|
||||
ASSERT_OK(dbfull()->SetOptions({{"ttl", "5"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 5);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -491,6 +491,7 @@ TEST_F(DBTestDynamicLevel, DISABLED_MigrateToDynamicLevelMaxBytesBase) {
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
// 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 {
|
||||
|
||||
@@ -26,6 +25,7 @@ class DBEncryptionTest : public DBTestBase {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TEST_F(DBEncryptionTest, CheckEncrypted) {
|
||||
ASSERT_OK(Put("foo567", "v1.fetdq"));
|
||||
ASSERT_OK(Put("bar123", "v2.dfgkjdfghsd"));
|
||||
@@ -116,6 +116,7 @@ TEST_F(DBEncryptionTest, ReadEmptyFile) {
|
||||
ASSERT_TRUE(data.empty());
|
||||
}
|
||||
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+32
-106
@@ -4,6 +4,7 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -20,7 +21,6 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/metadata.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/file_checksum_helper.h"
|
||||
@@ -91,12 +91,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::GetSortedWalFiles(VectorWalPtr& files) {
|
||||
return GetSortedWalFilesImpl(files,
|
||||
/*need_seqnos*/ true);
|
||||
}
|
||||
|
||||
Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
|
||||
Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
// Record tracked WALs as a (minimum) cross-check for directory scan
|
||||
std::vector<uint64_t> required_by_manifest;
|
||||
|
||||
@@ -122,10 +117,7 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: need to include archived WALs because needed WALs might have been
|
||||
// archived since getting required_by_manifest set
|
||||
Status s = wal_manager_.GetSortedWalFiles(files, need_seqnos,
|
||||
/*include_archived*/ true);
|
||||
Status s = wal_manager_.GetSortedWalFiles(files);
|
||||
|
||||
// DisableFileDeletions / EnableFileDeletions not supported in read-only DB
|
||||
if (deletions_disabled.ok()) {
|
||||
@@ -160,11 +152,11 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
size_t wal_count = files.size();
|
||||
size_t wal_size = files.size();
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Number of WAL files %" ROCKSDB_PRIszt " (%" ROCKSDB_PRIszt
|
||||
"Number of log files %" ROCKSDB_PRIszt " (%" ROCKSDB_PRIszt
|
||||
" required by manifest)",
|
||||
wal_count, required_by_manifest.size());
|
||||
wal_size, required_by_manifest.size());
|
||||
#ifndef NDEBUG
|
||||
std::ostringstream wal_names;
|
||||
for (const auto& wal : files) {
|
||||
@@ -185,7 +177,7 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::GetCurrentWalFile(std::unique_ptr<WalFile>* current_log_file) {
|
||||
Status DBImpl::GetCurrentWalFile(std::unique_ptr<LogFile>* current_log_file) {
|
||||
uint64_t current_logfile_number;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -206,20 +198,14 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
// NOTE: This implementation was largely migrated from Checkpoint.
|
||||
|
||||
Status s;
|
||||
VectorWalPtr live_wal_files;
|
||||
VectorLogPtr live_wal_files;
|
||||
bool flush_memtable = true;
|
||||
if (!immutable_db_options_.allow_2pc) {
|
||||
if (opts.wal_size_for_flush == std::numeric_limits<uint64_t>::max()) {
|
||||
flush_memtable = false;
|
||||
} else if (opts.wal_size_for_flush > 0) {
|
||||
// FIXME: avoid querying the filesystem for current WAL state
|
||||
// If the outstanding WAL files are small, we skip the flush.
|
||||
// Don't take archived log size into account when calculating wal
|
||||
// size for flush, and don't need to verify consistency with manifest
|
||||
// here & now.
|
||||
s = wal_manager_.GetSortedWalFiles(live_wal_files,
|
||||
/* need_seqnos */ false,
|
||||
/*include_archived*/ false);
|
||||
// If the outstanding log files are small, we skip the flush.
|
||||
s = GetSortedWalFiles(live_wal_files);
|
||||
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -230,7 +216,6 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
// We may be able to cover 2PC case too.
|
||||
uint64_t total_wal_size = 0;
|
||||
for (auto& wal : live_wal_files) {
|
||||
assert(wal->Type() == kAliveLogFile);
|
||||
total_wal_size += wal->SizeFileBytes();
|
||||
}
|
||||
if (total_wal_size < opts.wal_size_for_flush) {
|
||||
@@ -244,18 +229,12 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
// metadata.
|
||||
mutex_.Lock();
|
||||
if (flush_memtable) {
|
||||
bool wal_locked = lock_wal_count_ > 0;
|
||||
if (wal_locked) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Can't FlushForGetLiveFiles while WAL is locked");
|
||||
} else {
|
||||
Status status = FlushForGetLiveFiles();
|
||||
if (!status.ok()) {
|
||||
mutex_.Unlock();
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"Cannot Flush data %s\n", status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
Status status = FlushForGetLiveFiles();
|
||||
if (!status.ok()) {
|
||||
mutex_.Unlock();
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log, "Cannot Flush data %s\n",
|
||||
status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +244,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
|
||||
@@ -331,8 +310,6 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
const uint64_t options_number = versions_->options_file_number();
|
||||
const uint64_t options_size = versions_->options_file_size_;
|
||||
const uint64_t min_log_num = MinLogNumberToKeep();
|
||||
// Ensure consistency with manifest for track_and_verify_wals_in_manifest
|
||||
const uint64_t max_log_num = logfile_number_;
|
||||
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -396,13 +373,10 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
TEST_SYNC_POINT("CheckpointImpl::CreateCheckpoint:SavedLiveFiles2");
|
||||
|
||||
if (s.ok()) {
|
||||
// FlushWAL is required to ensure we can physically copy everything
|
||||
// logically written to the WAL. (Sync not strictly required for
|
||||
// active WAL to be copied rather than hard linked, even when
|
||||
// Checkpoint guarantees that the copied-to file is sync-ed. Plus we can't
|
||||
// help track_and_verify_wals_in_manifest after manifest_size is
|
||||
// already determined.)
|
||||
s = FlushWAL(/*sync=*/false);
|
||||
// To maximize the effectiveness of track_and_verify_wals_in_manifest,
|
||||
// sync WAL when it is enabled.
|
||||
s = FlushWAL(
|
||||
immutable_db_options_.track_and_verify_wals_in_manifest /* sync */);
|
||||
if (s.IsNotSupported()) { // read-only DB or similar
|
||||
s = Status::OK();
|
||||
}
|
||||
@@ -411,52 +385,21 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
TEST_SYNC_POINT("CheckpointImpl::CreateCustomCheckpoint:AfterGetLive1");
|
||||
TEST_SYNC_POINT("CheckpointImpl::CreateCustomCheckpoint:AfterGetLive2");
|
||||
|
||||
// Even after WAL flush, there could be multiple WALs that are not
|
||||
// fully synced. Although the output DB of a Checkpoint or Backup needs
|
||||
// to be fully synced on return, we don't strictly need to sync this
|
||||
// DB (the input DB). If we allow Checkpoint to hard link an inactive
|
||||
// WAL that isn't fully synced, that could result in an insufficiently
|
||||
// sync-ed Checkpoint. Here we get the set of WALs that are potentially
|
||||
// unsynced or still being written to, to prevent them from being hard
|
||||
// linked. Enforcing max_log_num from above ensures any new WALs after
|
||||
// GetOpenWalSizes() and before GetSortedWalFiles() are not included in
|
||||
// the results.
|
||||
// NOTE: we might still hard link a file that is open for writing, even
|
||||
// if we don't do any more writes to it.
|
||||
//
|
||||
// In a step toward reducing unnecessary file metadata queries, we also
|
||||
// get and use our known flushed sizes for those WALs.
|
||||
// FIXME: eventually we should not be using filesystem queries at all for
|
||||
// the required set of WAL files.
|
||||
//
|
||||
// However for recycled log files, we just copy the whole file,
|
||||
// for better or worse.
|
||||
//
|
||||
std::map<uint64_t, uint64_t> open_wal_number_to_size;
|
||||
bool recycling_log_files = immutable_db_options_.recycle_log_file_num > 0;
|
||||
if (s.ok() && !recycling_log_files) {
|
||||
s = GetOpenWalSizes(open_wal_number_to_size);
|
||||
}
|
||||
|
||||
// [old comment] If we have more than one column family, we also need to get
|
||||
// WAL files.
|
||||
// If we have more than one column family, we also need to get WAL files.
|
||||
if (s.ok()) {
|
||||
// FIXME: avoid querying the filesystem for current WAL state
|
||||
s = GetSortedWalFilesImpl(live_wal_files,
|
||||
/* need_seqnos */ false);
|
||||
s = GetSortedWalFiles(live_wal_files);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
size_t wal_count = live_wal_files.size();
|
||||
size_t wal_size = live_wal_files.size();
|
||||
// Link WAL files. Copy exact size of last one because it is the only one
|
||||
// that has changes after the last flush.
|
||||
auto wal_dir = immutable_db_options_.GetWalDir();
|
||||
for (size_t i = 0; s.ok() && i < wal_count; ++i) {
|
||||
for (size_t i = 0; s.ok() && i < wal_size; ++i) {
|
||||
if ((live_wal_files[i]->Type() == kAliveLogFile) &&
|
||||
(!flush_memtable || live_wal_files[i]->LogNumber() >= min_log_num) &&
|
||||
live_wal_files[i]->LogNumber() <= max_log_num) {
|
||||
(!flush_memtable || live_wal_files[i]->LogNumber() >= min_log_num)) {
|
||||
results.emplace_back();
|
||||
LiveFileStorageInfo& info = results.back();
|
||||
auto f = live_wal_files[i]->PathName();
|
||||
@@ -465,29 +408,12 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
info.directory = wal_dir;
|
||||
info.file_number = live_wal_files[i]->LogNumber();
|
||||
info.file_type = kWalFile;
|
||||
if (recycling_log_files) {
|
||||
info.size = live_wal_files[i]->SizeFileBytes();
|
||||
// Recyclable WAL files must be copied instead of hard linked
|
||||
info.trim_to_size = true;
|
||||
} else {
|
||||
auto it = open_wal_number_to_size.find(info.file_number);
|
||||
if (it == open_wal_number_to_size.end()) {
|
||||
// Known fully synced and no future writes (in part from
|
||||
// max_log_num check). Ok to hard link
|
||||
info.size = live_wal_files[i]->SizeFileBytes();
|
||||
assert(!info.trim_to_size);
|
||||
} else {
|
||||
// Marked as (possibly) still open -> use our known flushed size
|
||||
// and force file copy instead of hard link
|
||||
info.size = it->second;
|
||||
info.trim_to_size = true;
|
||||
// FIXME: this is needed as long as db_stress uses
|
||||
// SetReadUnsyncedData(false), because it will only be able to
|
||||
// copy the synced portion of the WAL, which under
|
||||
// SetReadUnsyncedData(false) is given by the reported file size.
|
||||
info.size = std::min(info.size, live_wal_files[i]->SizeFileBytes());
|
||||
}
|
||||
}
|
||||
info.size = live_wal_files[i]->SizeFileBytes();
|
||||
// Trim the log either if its the last one, or log file recycling is
|
||||
// enabled. In the latter case, a hard link doesn't prevent the file
|
||||
// from being renamed and recycled. So we need to copy it instead.
|
||||
info.trim_to_size = (i + 1 == wal_size) ||
|
||||
(immutable_db_options_.recycle_log_file_num > 0);
|
||||
if (opts.include_checksum_info) {
|
||||
info.file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
info.file_checksum = kUnknownFileChecksum;
|
||||
|
||||
+5
-5
@@ -86,8 +86,8 @@ TEST_F(DBFlushTest, SyncFail) {
|
||||
options.env = fault_injection_env.get();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBFlushTest::SyncFail:1", "DBImpl::SyncClosedWals:Start"},
|
||||
{"DBImpl::SyncClosedWals:Failed", "DBFlushTest::SyncFail:2"}});
|
||||
{{"DBFlushTest::SyncFail:1", "DBImpl::SyncClosedLogs:Start"},
|
||||
{"DBImpl::SyncClosedLogs:Failed", "DBFlushTest::SyncFail:2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -111,8 +111,8 @@ TEST_F(DBFlushTest, SyncSkip) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBFlushTest::SyncSkip:1", "DBImpl::SyncClosedWals:Skip"},
|
||||
{"DBImpl::SyncClosedWals:Skip", "DBFlushTest::SyncSkip:2"}});
|
||||
{{"DBFlushTest::SyncSkip:1", "DBImpl::SyncClosedLogs:Skip"},
|
||||
{"DBImpl::SyncClosedLogs:Skip", "DBFlushTest::SyncSkip:2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Reopen(options);
|
||||
@@ -2381,7 +2381,7 @@ TEST_F(DBFlushTest, PickRightMemtables) {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::SyncClosedWals:BeforeReLock", [&](void* /*arg*/) {
|
||||
"DBImpl::SyncClosedLogs:BeforeReLock", [&](void* /*arg*/) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles_[1], "what", "v"));
|
||||
auto* cfhi =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(handles_[1]);
|
||||
|
||||
+2
-470
@@ -17,7 +17,6 @@ class DBFollowerTest : public DBTestBase {
|
||||
// Create the leader DB object
|
||||
DBFollowerTest() : DBTestBase("/db_follower_test", /*env_do_fsync*/ false) {
|
||||
follower_name_ = dbname_ + "/follower";
|
||||
db_parent_ = dbname_;
|
||||
Close();
|
||||
Destroy(CurrentOptions());
|
||||
EXPECT_EQ(env_->CreateDirIfMissing(dbname_), Status::OK());
|
||||
@@ -28,215 +27,17 @@ class DBFollowerTest : public DBTestBase {
|
||||
~DBFollowerTest() {
|
||||
follower_.reset();
|
||||
EXPECT_EQ(DestroyDB(follower_name_, CurrentOptions()), Status::OK());
|
||||
Destroy(CurrentOptions());
|
||||
dbname_ = db_parent_;
|
||||
}
|
||||
|
||||
protected:
|
||||
class DBFollowerTestFS : public FileSystemWrapper {
|
||||
public:
|
||||
explicit DBFollowerTestFS(const std::shared_ptr<FileSystem>& target)
|
||||
: FileSystemWrapper(target),
|
||||
cv_(&mutex_),
|
||||
barrier_(false),
|
||||
count_(0),
|
||||
reinit_count_(0) {}
|
||||
|
||||
const char* Name() const override { return "DBFollowerTestFS"; }
|
||||
|
||||
IOStatus NewSequentialFile(const std::string& fname,
|
||||
const FileOptions& file_opts,
|
||||
std::unique_ptr<FSSequentialFile>* result,
|
||||
IODebugContext* dbg = nullptr) override {
|
||||
class DBFollowerTestSeqFile : public FSSequentialFileWrapper {
|
||||
public:
|
||||
DBFollowerTestSeqFile(DBFollowerTestFS* fs,
|
||||
std::unique_ptr<FSSequentialFile>&& file,
|
||||
uint64_t /*size*/)
|
||||
: FSSequentialFileWrapper(file.get()),
|
||||
fs_(fs),
|
||||
file_(std::move(file)) {}
|
||||
|
||||
IOStatus Read(size_t n, const IOOptions& options, Slice* result,
|
||||
char* scratch, IODebugContext* dbg) override {
|
||||
fs_->BarrierWait();
|
||||
return target()->Read(n, options, result, scratch, dbg);
|
||||
}
|
||||
|
||||
private:
|
||||
DBFollowerTestFS* fs_;
|
||||
std::unique_ptr<FSSequentialFile> file_;
|
||||
};
|
||||
|
||||
std::unique_ptr<FSSequentialFile> file;
|
||||
IOStatus s = target()->NewSequentialFile(fname, file_opts, &file, dbg);
|
||||
|
||||
if (s.ok() && test::GetFileType(fname) == kDescriptorFile) {
|
||||
uint64_t size = 0;
|
||||
EXPECT_EQ(target()->GetFileSize(fname, IOOptions(), &size, nullptr),
|
||||
IOStatus::OK());
|
||||
result->reset(new DBFollowerTestSeqFile(this, std::move(file), size));
|
||||
} else {
|
||||
*result = std::move(file);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void BarrierInit(int count) {
|
||||
MutexLock l(&mutex_);
|
||||
barrier_ = true;
|
||||
count_ = count;
|
||||
}
|
||||
|
||||
void BarrierWait() {
|
||||
MutexLock l(&mutex_);
|
||||
if (!barrier_) {
|
||||
return;
|
||||
}
|
||||
if (--count_ == 0) {
|
||||
if (reinit_count_ > 0) {
|
||||
count_ = reinit_count_;
|
||||
reinit_count_ = 0;
|
||||
} else {
|
||||
barrier_ = false;
|
||||
}
|
||||
cv_.SignalAll();
|
||||
} else {
|
||||
cv_.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void BarrierWaitAndReinit(int count) {
|
||||
MutexLock l(&mutex_);
|
||||
if (!barrier_) {
|
||||
return;
|
||||
}
|
||||
reinit_count_ = count;
|
||||
if (--count_ == 0) {
|
||||
if (reinit_count_ > 0) {
|
||||
count_ = reinit_count_;
|
||||
reinit_count_ = 0;
|
||||
} else {
|
||||
barrier_ = false;
|
||||
}
|
||||
cv_.SignalAll();
|
||||
} else {
|
||||
cv_.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
port::Mutex mutex_;
|
||||
port::CondVar cv_;
|
||||
bool barrier_;
|
||||
int count_;
|
||||
int reinit_count_;
|
||||
};
|
||||
|
||||
class DBFollowerTestSstPartitioner : public SstPartitioner {
|
||||
public:
|
||||
explicit DBFollowerTestSstPartitioner(uint64_t max_keys)
|
||||
: max_keys_(max_keys), num_keys_(0) {}
|
||||
|
||||
const char* Name() const override { return "DBFollowerTestSstPartitioner"; }
|
||||
|
||||
PartitionerResult ShouldPartition(
|
||||
const PartitionerRequest& /*request*/) override {
|
||||
if (++num_keys_ > max_keys_) {
|
||||
num_keys_ = 0;
|
||||
return PartitionerResult::kRequired;
|
||||
} else {
|
||||
return PartitionerResult::kNotRequired;
|
||||
}
|
||||
}
|
||||
|
||||
bool CanDoTrivialMove(const Slice& /*smallest_user_key*/,
|
||||
const Slice& /*largest_user_key*/) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t max_keys_;
|
||||
uint64_t num_keys_;
|
||||
};
|
||||
|
||||
class DBFollowerTestSstPartitionerFactory : public SstPartitionerFactory {
|
||||
public:
|
||||
explicit DBFollowerTestSstPartitionerFactory(uint64_t max_keys)
|
||||
: max_keys_(max_keys) {}
|
||||
|
||||
std::unique_ptr<SstPartitioner> CreatePartitioner(
|
||||
const SstPartitioner::Context& /*context*/) const override {
|
||||
std::unique_ptr<SstPartitioner> partitioner;
|
||||
partitioner.reset(new DBFollowerTestSstPartitioner(max_keys_));
|
||||
return partitioner;
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "DBFollowerTestSstPartitionerFactory";
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t max_keys_;
|
||||
};
|
||||
|
||||
Status OpenAsFollower() {
|
||||
Options opts = CurrentOptions();
|
||||
if (!follower_env_) {
|
||||
follower_env_ = NewCompositeEnv(
|
||||
std::make_shared<DBFollowerTestFS>(env_->GetFileSystem()));
|
||||
}
|
||||
opts.env = follower_env_.get();
|
||||
opts.follower_refresh_catchup_period_ms = 100;
|
||||
return DB::OpenAsFollower(opts, follower_name_, dbname_, &follower_);
|
||||
return DB::OpenAsFollower(CurrentOptions(), follower_name_, dbname_,
|
||||
&follower_);
|
||||
}
|
||||
|
||||
std::string FollowerGet(const std::string& k) {
|
||||
ReadOptions options;
|
||||
options.verify_checksums = true;
|
||||
std::string result;
|
||||
Status s = follower()->Get(options, k, &result);
|
||||
if (s.IsNotFound()) {
|
||||
result = "NOT_FOUND";
|
||||
} else if (!s.ok()) {
|
||||
result = s.ToString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DB* follower() { return follower_.get(); }
|
||||
DBFollowerTestFS* follower_fs() {
|
||||
return static_cast<DBFollowerTestFS*>(follower_env_->GetFileSystem().get());
|
||||
}
|
||||
|
||||
void CheckDirs() {
|
||||
std::vector<std::string> db_children;
|
||||
std::vector<std::string> follower_children;
|
||||
EXPECT_OK(env_->GetChildren(dbname_, &db_children));
|
||||
EXPECT_OK(env_->GetChildren(follower_name_, &follower_children));
|
||||
|
||||
std::set<uint64_t> db_filenums;
|
||||
std::set<uint64_t> follower_filenums;
|
||||
for (auto& name : db_children) {
|
||||
if (test::GetFileType(name) != kTableFile) {
|
||||
continue;
|
||||
}
|
||||
db_filenums.insert(test::GetFileNumber(name));
|
||||
}
|
||||
for (auto& name : follower_children) {
|
||||
if (test::GetFileType(name) != kTableFile) {
|
||||
continue;
|
||||
}
|
||||
follower_filenums.insert(test::GetFileNumber(name));
|
||||
}
|
||||
db_filenums.merge(follower_filenums);
|
||||
EXPECT_EQ(follower_filenums.size(), db_filenums.size());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string follower_name_;
|
||||
std::string db_parent_;
|
||||
std::unique_ptr<Env> follower_env_;
|
||||
std::unique_ptr<DB> follower_;
|
||||
};
|
||||
|
||||
@@ -250,277 +51,8 @@ TEST_F(DBFollowerTest, Basic) {
|
||||
std::string val;
|
||||
ASSERT_OK(follower()->Get(ReadOptions(), "k1", &val));
|
||||
ASSERT_EQ(val, "v1");
|
||||
CheckDirs();
|
||||
}
|
||||
|
||||
TEST_F(DBFollowerTest, Flush) {
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:Begin1", "Leader::Start"},
|
||||
{"Leader::Done", "DBImplFollower::TryCatchupWithLeader:Begin2"},
|
||||
{"DBImplFollower::TryCatchupWithLeader:End", "Follower::WaitForCatchup"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(OpenAsFollower());
|
||||
TEST_SYNC_POINT("Leader::Start");
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
TEST_SYNC_POINT("Leader::Done");
|
||||
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup");
|
||||
std::string val;
|
||||
ASSERT_OK(follower()->Get(ReadOptions(), "k1", &val));
|
||||
ASSERT_EQ(val, "v1");
|
||||
CheckDirs();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
// This test creates 4 L0 files, immediately followed by a compaction to L1.
|
||||
// The follower replays the 4 flush records from the MANIFEST unsuccessfully,
|
||||
// and then successfully recovers a Version from the compaction record
|
||||
TEST_F(DBFollowerTest, RetryCatchup) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
Reopen(opts);
|
||||
|
||||
ASSERT_OK(OpenAsFollower());
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:Begin1", "Leader::Start"},
|
||||
{"DBImpl::BackgroundCompaction:Start",
|
||||
"DBImplFollower::TryCatchupWithLeader:Begin2"},
|
||||
{"VersionEditHandlerPointInTime::MaybeCreateVersionBeforeApplyEdit:"
|
||||
"Begin1",
|
||||
"DBImpl::BackgroundCompaction:BeforeCompaction"},
|
||||
{"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles",
|
||||
"VersionEditHandlerPointInTime::MaybeCreateVersionBeforeApplyEdit:"
|
||||
"Begin2"},
|
||||
{"DBImplFollower::TryCatchupWithLeader:End", "Follower::WaitForCatchup"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
TEST_SYNC_POINT("Leader::Start");
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v3"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v4"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr, true));
|
||||
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup");
|
||||
ASSERT_EQ(FollowerGet("k1"), "v4");
|
||||
CheckDirs();
|
||||
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
// This test validates the same as the previous test, except there is a
|
||||
// MANIFEST rollover between the flushes and compaction. The follower
|
||||
// does not switch to a new MANIFEST in ReadAndApply. So it would require
|
||||
// another round of refresh before catching up.
|
||||
TEST_F(DBFollowerTest, RetryCatchupManifestRollover) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
Reopen(opts);
|
||||
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v3"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(OpenAsFollower());
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:Begin1", "Leader::Start"},
|
||||
{"Leader::Flushed", "DBImplFollower::TryCatchupWithLeader:Begin2"},
|
||||
{"VersionEditHandlerPointInTime::MaybeCreateVersionBeforeApplyEdit:"
|
||||
"Begin1",
|
||||
"Leader::Done"},
|
||||
{"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles",
|
||||
"VersionEditHandlerPointInTime::MaybeCreateVersionBeforeApplyEdit:"
|
||||
"Begin2"},
|
||||
{"DBImplFollower::TryCatchupWithLeader:End",
|
||||
"Follower::WaitForCatchup:1"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
TEST_SYNC_POINT("Leader::Start");
|
||||
ASSERT_OK(Put("k1", "v4"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
TEST_SYNC_POINT("Leader::Flushed");
|
||||
TEST_SYNC_POINT("Leader::Done");
|
||||
Reopen(opts);
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr, true));
|
||||
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:1");
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:End",
|
||||
"Follower::WaitForCatchup:2"},
|
||||
});
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:2");
|
||||
ASSERT_EQ(FollowerGet("k1"), "v4");
|
||||
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
// This test creates 4 L0 files and compacts them. The follower, during catchup,
|
||||
// successfully instantiates 4 Versions corresponding to the 4 files (but
|
||||
// donesn't install them yet), followed by deleting those 4 and adding a new
|
||||
// file from compaction. The test verifies that the 4 L0 files are deleted
|
||||
// correctly by the follower.
|
||||
// We use teh Barrier* functions to ensure that the follower first sees the 4
|
||||
// L0 files and is able to link them, and then sees the compaction that
|
||||
// obsoletes those L0 files (so those L0 files are intermediates that it has
|
||||
// to explicitly delete). Suppose we don't have any barriers, its possible
|
||||
// the follower reads the L0 records and compaction records from the MANIFEST
|
||||
// in one read, which means those L0 files would have already been deleted
|
||||
// by the leader and the follower cannot link to them.
|
||||
TEST_F(DBFollowerTest, IntermediateObsoleteFiles) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
Reopen(opts);
|
||||
ASSERT_OK(OpenAsFollower());
|
||||
|
||||
follower_fs()->BarrierInit(2);
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v3"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v4"));
|
||||
ASSERT_OK(Flush());
|
||||
follower_fs()->BarrierWaitAndReinit(2);
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr, true));
|
||||
follower_fs()->BarrierWait();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:End",
|
||||
"Follower::WaitForCatchup:1"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:1");
|
||||
CheckDirs();
|
||||
ASSERT_EQ(FollowerGet("k1"), "v4");
|
||||
}
|
||||
|
||||
// This test verifies a scenario where the follower can recover a Version
|
||||
// partially (i.e some of the additions cannot be found), and the files
|
||||
// that are found are obsoleted by a subsequent VersionEdit.
|
||||
TEST_F(DBFollowerTest, PartialVersionRecovery) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.sst_partitioner_factory =
|
||||
std::make_shared<DBFollowerTestSstPartitionerFactory>(1);
|
||||
Reopen(opts);
|
||||
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Put("k2", "v1"));
|
||||
ASSERT_OK(Put("k3", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
ASSERT_OK(Put("k1", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k3", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
ASSERT_OK(OpenAsFollower());
|
||||
ASSERT_OK(dbfull()->SetOptions(dbfull()->DefaultColumnFamily(),
|
||||
{{"max_compaction_bytes", "1"}}));
|
||||
|
||||
follower_fs()->BarrierInit(2);
|
||||
Slice key("k1");
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, &key, &key, nullptr, true));
|
||||
|
||||
follower_fs()->BarrierWaitAndReinit(2);
|
||||
|
||||
// The second compaction input overlaps the previous compaction outputs
|
||||
// by one file. This file is never added to VersionStorageInfo since it
|
||||
// was added and deleted before the catch up completes. We later verify that
|
||||
// the follower correctly deleted this file.
|
||||
key = Slice("k3");
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, &key, &key, nullptr, true));
|
||||
follower_fs()->BarrierWait();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:End",
|
||||
"Follower::WaitForCatchup:1"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:1");
|
||||
CheckDirs();
|
||||
ASSERT_EQ(FollowerGet("k1"), "v2");
|
||||
ASSERT_EQ(FollowerGet("k2"), "v1");
|
||||
ASSERT_EQ(FollowerGet("k3"), "v2");
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
// This test verifies a scenario similar to the PartialVersionRecovery, except
|
||||
// with a MANIFEST rollover in between. When there is a rollover, the
|
||||
// follower's attempt ends without installing a new Version. The next catch up
|
||||
// attempt will recover a full Version.
|
||||
TEST_F(DBFollowerTest, PartialVersionRecoveryWithRollover) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.sst_partitioner_factory =
|
||||
std::make_shared<DBFollowerTestSstPartitionerFactory>(1);
|
||||
Reopen(opts);
|
||||
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Put("k2", "v1"));
|
||||
ASSERT_OK(Put("k3", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
ASSERT_OK(Put("k1", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k3", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
opts.max_compaction_bytes = 1;
|
||||
Reopen(opts);
|
||||
|
||||
ASSERT_OK(OpenAsFollower());
|
||||
|
||||
follower_fs()->BarrierInit(2);
|
||||
Slice key("k1");
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, &key, &key, nullptr, true));
|
||||
|
||||
follower_fs()->BarrierWaitAndReinit(2);
|
||||
Reopen(opts);
|
||||
key = Slice("k3");
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, &key, &key, nullptr, true));
|
||||
follower_fs()->BarrierWait();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:Begin1",
|
||||
"Follower::WaitForCatchup:1"},
|
||||
{"Follower::WaitForCatchup:2",
|
||||
"DBImplFollower::TryCatchupWithLeader:Begin2"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:1");
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:2");
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:End",
|
||||
"Follower::WaitForCatchup:3"},
|
||||
});
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:3");
|
||||
CheckDirs();
|
||||
ASSERT_EQ(FollowerGet("k1"), "v2");
|
||||
ASSERT_EQ(FollowerGet("k2"), "v1");
|
||||
ASSERT_EQ(FollowerGet("k3"), "v2");
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
#endif
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ Status CompactedDBImpl::Init(const Options& options) {
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Open(const Options& options, const std::string& dbname,
|
||||
std::unique_ptr<DB>* dbptr) {
|
||||
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 = std::move(db);
|
||||
*dbptr = db.release();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class CompactedDBImpl : public DBImpl {
|
||||
~CompactedDBImpl() override;
|
||||
|
||||
static Status Open(const Options& options, const std::string& dbname,
|
||||
std::unique_ptr<DB>* dbptr);
|
||||
DB** dbptr);
|
||||
|
||||
// Implementations of the DB interface
|
||||
using DB::Get;
|
||||
|
||||
+531
-882
File diff suppressed because it is too large.
Load diff
Loaded 100 of 654 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user