mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 02:52:35 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffcfbaa584 | ||
|
|
f2cbed0cc4 | ||
|
|
e283b751a6 | ||
|
|
c15ee5a758 | ||
|
|
5f981e1145 | ||
|
|
732ab9a131 | ||
|
|
4bef82ad85 | ||
|
|
935ddf6469 | ||
|
|
2aed669e36 | ||
|
|
30a98ee159 | ||
|
|
4db32f3df9 | ||
|
|
a5f08293d8 |
No files matched your search
+87
-100
@@ -16,8 +16,7 @@ commands:
|
||||
- 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
|
||||
brew install --cask adoptopenjdk/openjdk/adoptopenjdk8
|
||||
|
||||
increase-max-open-files-on-macos:
|
||||
steps:
|
||||
@@ -54,7 +53,6 @@ commands:
|
||||
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..."
|
||||
@@ -68,10 +66,9 @@ commands:
|
||||
- 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 ..
|
||||
& $Env:CMAKE_BIN -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -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
|
||||
@@ -80,11 +77,6 @@ commands:
|
||||
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
|
||||
@@ -112,15 +104,6 @@ commands:
|
||||
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:
|
||||
@@ -143,16 +126,6 @@ commands:
|
||||
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:
|
||||
@@ -232,29 +205,25 @@ executors:
|
||||
# $ 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
|
||||
xcode: 12.5.1
|
||||
resource_class: large
|
||||
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
|
||||
- run: ulimit -S -n `ulimit -H -n` && OPT=-DCIRCLECI make V=1 J=32 -j32 all
|
||||
- post-steps
|
||||
|
||||
build-macos-cmake:
|
||||
macos:
|
||||
xcode: 14.3.1
|
||||
resource_class: macos.m1.medium.gen1
|
||||
xcode: 12.5.1
|
||||
resource_class: large
|
||||
parameters:
|
||||
run_even_tests:
|
||||
description: run even or odd tests, used to split tests to 2 groups
|
||||
@@ -270,20 +239,20 @@ jobs:
|
||||
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
|
||||
command: cd build && make V=1 -j32
|
||||
- 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
|
||||
command: ulimit -S -n `ulimit -H -n` && cd build && ctest -j32 -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
|
||||
command: ulimit -S -n `ulimit -H -n` && cd build && ctest -j32 -I 1,,2
|
||||
- post-steps
|
||||
|
||||
build-linux:
|
||||
@@ -547,7 +516,7 @@ jobs:
|
||||
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
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000 --use_io_uring=0' blackbox_crash_test_with_atomic_flush
|
||||
- post-steps
|
||||
|
||||
build-linux-crashtest-tiered-storage-bb:
|
||||
@@ -557,7 +526,7 @@ jobs:
|
||||
- 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
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800 --use_io_uring=0' blackbox_crash_test_with_tiered_storage
|
||||
no_output_timeout: 100m
|
||||
- post-steps
|
||||
|
||||
@@ -568,28 +537,10 @@ jobs:
|
||||
- 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
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800 --use_io_uring=0' 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
|
||||
@@ -598,13 +549,10 @@ jobs:
|
||||
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
|
||||
|
||||
@@ -616,18 +564,15 @@ jobs:
|
||||
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
|
||||
executor: linux-docker
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -640,29 +585,11 @@ jobs:
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Test RocksDBJava"
|
||||
command: scl enable devtoolset-7 'make V=1 J=8 -j8 jtest'
|
||||
command: 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
|
||||
executor: linux-docker
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -675,15 +602,15 @@ jobs:
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Static Library"
|
||||
command: scl enable devtoolset-7 'make V=1 J=8 -j8 rocksdbjavastatic'
|
||||
command: make V=1 J=8 -j8 rocksdbjavastatic
|
||||
- post-steps
|
||||
|
||||
build-macos-java:
|
||||
macos:
|
||||
xcode: 14.3.1
|
||||
resource_class: macos.m1.medium.gen1
|
||||
xcode: 12.5.1
|
||||
resource_class: large
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1 # jemalloc causes java 8 crash
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
@@ -705,10 +632,10 @@ jobs:
|
||||
|
||||
build-macos-java-static:
|
||||
macos:
|
||||
xcode: 14.3.1
|
||||
resource_class: macos.m1.medium.gen1
|
||||
xcode: 12.5.1
|
||||
resource_class: large
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-gflags-on-macos
|
||||
@@ -730,10 +657,10 @@ jobs:
|
||||
|
||||
build-macos-java-static-universal:
|
||||
macos:
|
||||
xcode: 14.3.1
|
||||
resource_class: macos.m1.medium.gen1
|
||||
xcode: 12.5.1
|
||||
resource_class: large
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-gflags-on-macos
|
||||
@@ -878,11 +805,66 @@ jobs:
|
||||
- perform-benchmarks
|
||||
- post-benchmarks
|
||||
|
||||
workflows: # Only jobs that haven't been successfully migrated to GitHub Actions
|
||||
workflows:
|
||||
version: 2
|
||||
jobs-linux-run-tests:
|
||||
jobs:
|
||||
- build-linux
|
||||
- build-linux-cmake-with-folly
|
||||
- build-linux-cmake-with-folly-lite-no-test
|
||||
- build-linux-gcc-7-with-folly
|
||||
- build-linux-gcc-7-with-folly-lite-no-test
|
||||
- build-linux-cmake-with-folly-coroutines
|
||||
- build-linux-cmake-with-benchmark
|
||||
- build-linux-encrypted_env-no_compression
|
||||
jobs-linux-run-tests-san:
|
||||
jobs:
|
||||
- build-linux-clang10-asan
|
||||
- build-linux-clang10-ubsan
|
||||
- build-linux-clang10-mini-tsan
|
||||
- build-linux-static_lib-alt_namespace-status_checked
|
||||
jobs-linux-no-test-run:
|
||||
jobs:
|
||||
- build-linux-release
|
||||
- build-linux-release-rtti
|
||||
- build-examples
|
||||
- build-fuzzers
|
||||
- build-linux-clang-no_test_run
|
||||
- build-linux-clang-13-no_test_run
|
||||
- build-linux-gcc-8-no_test_run
|
||||
- build-linux-gcc-10-cxx20-no_test_run
|
||||
- build-linux-gcc-11-no_test_run
|
||||
- build-linux-arm-cmake-no_test_run
|
||||
jobs-linux-other-checks:
|
||||
jobs:
|
||||
- build-linux-clang10-clang-analyze
|
||||
- build-linux-unity-and-headers
|
||||
- build-linux-mini-crashtest
|
||||
jobs-windows:
|
||||
jobs:
|
||||
- build-windows-vs2022
|
||||
- build-windows-vs2019
|
||||
- build-cmake-mingw
|
||||
jobs-java:
|
||||
jobs:
|
||||
- build-linux-java
|
||||
- build-linux-java-static
|
||||
- build-macos-java
|
||||
- build-macos-java-static
|
||||
- build-macos-java-static-universal
|
||||
jobs-macos:
|
||||
jobs:
|
||||
- build-macos
|
||||
- build-macos-cmake:
|
||||
run_even_tests: true
|
||||
- build-macos-cmake:
|
||||
run_even_tests: false
|
||||
jobs-linux-arm:
|
||||
jobs:
|
||||
- build-linux-arm
|
||||
build-fuzzers:
|
||||
jobs:
|
||||
- build-fuzzers
|
||||
benchmark-linux:
|
||||
triggers:
|
||||
- schedule:
|
||||
@@ -902,4 +884,9 @@ workflows: # Only jobs that haven't been successfully migrated to GitHub Actions
|
||||
only:
|
||||
- main
|
||||
jobs:
|
||||
- build-format-compatible
|
||||
- build-linux-arm-test-full
|
||||
- build-linux-run-microbench
|
||||
- build-linux-non-shm
|
||||
- build-linux-clang-13-asan-ubsan-with-folly
|
||||
- build-linux-valgrind
|
||||
@@ -1,7 +0,0 @@
|
||||
name: build-folly
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Build folly and dependencies
|
||||
run: make build_folly
|
||||
shell: bash
|
||||
@@ -1,8 +0,0 @@
|
||||
name: build-for-benchmarks
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Linux build for benchmarks
|
||||
run: make V=1 J=8 -j8 release
|
||||
shell: bash
|
||||
@@ -1,10 +0,0 @@
|
||||
name: increase-max-open-files-on-macos
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Increase max open files
|
||||
run: |-
|
||||
sudo sysctl -w kern.maxfiles=1048576
|
||||
sudo sysctl -w kern.maxfilesperproc=1048576
|
||||
sudo launchctl limit maxfiles 1048576
|
||||
shell: bash
|
||||
@@ -1,7 +0,0 @@
|
||||
name: install-gflags-on-macos
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install gflags on macos
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install gflags
|
||||
shell: bash
|
||||
@@ -1,7 +0,0 @@
|
||||
name: install-gflags
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install gflags
|
||||
run: sudo apt-get update -y && sudo apt-get install -y libgflags-dev
|
||||
shell: bash
|
||||
@@ -1,9 +0,0 @@
|
||||
name: install-jdk8-on-macos
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install JDK 8 on macos
|
||||
run: |-
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew tap bell-sw/liberica
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install --cask liberica-jdk8
|
||||
shell: bash
|
||||
@@ -1,11 +0,0 @@
|
||||
name: install-maven
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install Maven
|
||||
run: |
|
||||
wget --no-check-certificate https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz
|
||||
tar zxf apache-maven-3.9.6-bin.tar.gz
|
||||
echo "export M2_HOME=$(pwd)/apache-maven-3.9.6" >> $GITHUB_ENV
|
||||
echo "$(pwd)/apache-maven-3.9.6/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
@@ -1,22 +0,0 @@
|
||||
name: perform-benchmarks
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Test low-variance benchmarks
|
||||
run: "./tools/benchmark_ci.py --db_dir ${{ runner.temp }}/rocksdb-benchmark-datadir --output_dir ${{ runner.temp }}/benchmark-results --num_keys 20000000"
|
||||
env:
|
||||
LD_LIBRARY_PATH: "/usr/local/lib"
|
||||
DURATION_RO: 300
|
||||
DURATION_RW: 500
|
||||
NUM_THREADS: 1
|
||||
MAX_BACKGROUND_JOBS: 4
|
||||
CI_TESTS_ONLY: 'true'
|
||||
WRITE_BUFFER_SIZE_MB: 16
|
||||
TARGET_FILE_SIZE_BASE_MB: 16
|
||||
MAX_BYTES_FOR_LEVEL_BASE_MB: 64
|
||||
COMPRESSION_TYPE: none
|
||||
CACHE_INDEX_AND_FILTER_BLOCKS: 1
|
||||
MIN_LEVEL_TO_COMPRESS: 3
|
||||
CACHE_SIZE_MB: 10240
|
||||
MB_WRITE_PER_SEC: 2
|
||||
shell: bash
|
||||
@@ -1,17 +0,0 @@
|
||||
name: post-benchmarks
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Upload Benchmark Results artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: benchmark-results
|
||||
path: "${{ runner.temp }}/benchmark-results/**"
|
||||
if-no-files-found: error
|
||||
- name: Send benchmark report to visualisation
|
||||
run: |-
|
||||
set +e
|
||||
set +o pipefail
|
||||
./build_tools/benchmark_log_tool.py --tsvfile ${{ runner.temp }}/benchmark-results/report.tsv --esdocument https://search-rocksdb-bench-k2izhptfeap2hjfxteolsgsynm.us-west-2.es.amazonaws.com/bench_test3_rix/_doc
|
||||
true
|
||||
shell: bash
|
||||
@@ -1,38 +0,0 @@
|
||||
name: post-steps
|
||||
description: Steps that are taken after a RocksDB job
|
||||
inputs:
|
||||
artifact-prefix:
|
||||
description: Prefix to append to the name of artifacts that are uploaded
|
||||
required: true
|
||||
default: "${{ github.job }}"
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Upload Test Results artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: "${{ inputs.artifact-prefix }}-test-results"
|
||||
path: "${{ runner.temp }}/test-results/**"
|
||||
- name: Upload DB LOG file artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: "${{ inputs.artifact-prefix }}-db-log-file"
|
||||
path: LOG
|
||||
- name: Copy Test Logs (on Failure)
|
||||
if: ${{ failure() }}
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/failure-test-logs
|
||||
cp -r t/* ${{ runner.temp }}/failure-test-logs
|
||||
shell: bash
|
||||
- name: Upload Test Logs (on Failure) artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: "${{ inputs.artifact-prefix }}-failure-test-logs"
|
||||
path: ${{ runner.temp }}/failure-test-logs/**
|
||||
if-no-files-found: ignore
|
||||
- name: Upload Core Dumps artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: "${{ inputs.artifact-prefix }}-core-dumps"
|
||||
path: "core.*"
|
||||
if-no-files-found: ignore
|
||||
@@ -1,5 +0,0 @@
|
||||
name: pre-steps-macos
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -1,18 +0,0 @@
|
||||
name: pre-steps
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup Environment Variables
|
||||
run: |-
|
||||
echo "GTEST_THROW_ON_FAILURE=0" >> "$GITHUB_ENV"
|
||||
echo "GTEST_OUTPUT=\"xml:${{ runner.temp }}/test-results/\"" >> "$GITHUB_ENV"
|
||||
echo "SKIP_FORMAT_BUCK_CHECKS=1" >> "$GITHUB_ENV"
|
||||
echo "GTEST_COLOR=1" >> "$GITHUB_ENV"
|
||||
echo "CTEST_OUTPUT_ON_FAILURE=1" >> "$GITHUB_ENV"
|
||||
echo "CTEST_TEST_TIMEOUT=300" >> "$GITHUB_ENV"
|
||||
echo "ZLIB_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zlib" >> "$GITHUB_ENV"
|
||||
echo "BZIP2_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/bzip2" >> "$GITHUB_ENV"
|
||||
echo "SNAPPY_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/snappy" >> "$GITHUB_ENV"
|
||||
echo "LZ4_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/lz4" >> "$GITHUB_ENV"
|
||||
echo "ZSTD_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zstd" >> "$GITHUB_ENV"
|
||||
shell: bash
|
||||
@@ -1,7 +0,0 @@
|
||||
name: setup-folly
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Checkout folly sources
|
||||
run: make checkout_folly
|
||||
shell: bash
|
||||
@@ -1,20 +0,0 @@
|
||||
name: build-folly
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Fix repo ownership
|
||||
# Needed in some cases, as safe.directory setting doesn't take effect
|
||||
# under env -i
|
||||
run: chown `whoami` . || true
|
||||
shell: bash
|
||||
- name: Set upstream
|
||||
run: git remote add upstream https://github.com/facebook/rocksdb.git
|
||||
shell: bash
|
||||
- name: Fetch upstream
|
||||
run: git fetch upstream
|
||||
shell: bash
|
||||
- name: Git status
|
||||
# NOTE: some old branch builds under check_format_compatible.sh invoke
|
||||
# git under env -i
|
||||
run: git status && git remote -v && env -i git branch
|
||||
shell: bash
|
||||
@@ -1,54 +0,0 @@
|
||||
name: windows-build-steps
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v1.3.1
|
||||
- name: Custom steps
|
||||
env:
|
||||
THIRDPARTY_HOME: ${{ github.workspace }}/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: ${{ github.workspace }}/thirdparty/snappy-1.1.8
|
||||
SNAPPY_INCLUDE: ${{ github.workspace }}/thirdparty/snappy-1.1.8;${{ github.workspace }}/thirdparty/snappy-1.1.8/build
|
||||
SNAPPY_LIB_DEBUG: ${{ github.workspace }}/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
|
||||
run: |-
|
||||
# NOTE: if ... Exit $LASTEXITCODE lines needed to exit and report failure
|
||||
echo ===================== Install Dependencies =====================
|
||||
choco install liberica8jdk -y
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
mkdir $Env:THIRDPARTY_HOME
|
||||
cd $Env:THIRDPARTY_HOME
|
||||
echo "Building Snappy dependency..."
|
||||
curl -Lo snappy-1.1.8.zip https://github.com/google/snappy/archive/refs/tags/1.1.8.zip
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
unzip -q snappy-1.1.8.zip
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
cd snappy-1.1.8
|
||||
mkdir build
|
||||
cd build
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" ..
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
msbuild Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
echo ======================== Build RocksDB =========================
|
||||
cd ${{ github.workspace }}
|
||||
$env:Path = $env:JAVA_HOME + ";" + $env:Path
|
||||
mkdir build
|
||||
cd build
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DJNI=1 ..
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
cd ..
|
||||
echo "Building with VS version: $Env:CMAKE_GENERATOR"
|
||||
msbuild build/rocksdb.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
echo ========================= Test RocksDB =========================
|
||||
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
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
echo ======================== Test RocksJava ========================
|
||||
cd build\java
|
||||
& ctest -C Debug -j 16
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
shell: pwsh
|
||||
@@ -1,15 +0,0 @@
|
||||
name: facebook/rocksdb/benchmark-linux
|
||||
on: workflow_dispatch
|
||||
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
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/build-for-benchmarks"
|
||||
- uses: "./.github/actions/perform-benchmarks"
|
||||
- uses: "./.github/actions/post-benchmarks"
|
||||
@@ -1,18 +0,0 @@
|
||||
name: facebook/rocksdb/nightly
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
# These jobs would be in nightly but are failing or otherwise broken for
|
||||
# some reason.
|
||||
build-linux-arm-test-full:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: arm64large
|
||||
container:
|
||||
image: ubuntu-2004:202111-02
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/install-gflags"
|
||||
- run: make V=1 J=4 -j4 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
@@ -1,98 +0,0 @@
|
||||
name: facebook/rocksdb/nightly
|
||||
on:
|
||||
schedule:
|
||||
- cron: 0 9 * * *
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
build-format-compatible:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
with:
|
||||
fetch-depth: 0 # Need full repo history
|
||||
fetch-tags: true
|
||||
- uses: "./.github/actions/setup-upstream"
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: test
|
||||
run: |-
|
||||
export TEST_TMPDIR=/dev/shm/rocksdb
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
git config --global --add safe.directory /__w/rocksdb/rocksdb
|
||||
tools/check_format_compatible.sh
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-run-microbench:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: DEBUG_LEVEL=0 make -j32 run_microbench
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-non-shm:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
TEST_TMPDIR: "/tmp/rocksdb_test_tmp"
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: make V=1 -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang-13-asan-ubsan-with-folly:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/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
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-valgrind:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: PORTABLE=1 make V=1 -j32 valgrind_test
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-windows-vs2022-avx2:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: windows-2022
|
||||
env:
|
||||
CMAKE_GENERATOR: Visual Studio 17 2022
|
||||
CMAKE_PORTABLE: AVX2
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/windows-build-steps"
|
||||
build-windows-vs2022:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: windows-2022
|
||||
env:
|
||||
CMAKE_GENERATOR: Visual Studio 17 2022
|
||||
CMAKE_PORTABLE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/windows-build-steps"
|
||||
@@ -1,46 +0,0 @@
|
||||
name: facebook/rocksdb/pr-jobs-candidate
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
# These jobs would be in pr-jobs but are failing or otherwise broken for
|
||||
# some reason.
|
||||
# =========================== ARM Jobs ============================ #
|
||||
build-linux-arm:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: arm64large # GitHub hosted ARM runners do not yet exist
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/install-gflags"
|
||||
- run: ROCKSDBTESTS_PLATFORM_DEPENDENT=only make V=1 J=4 -j4 all_but_some_tests check_some
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-arm-cmake-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: arm64large # GitHub hosted ARM runners do not yet exist
|
||||
env:
|
||||
JAVA_HOME: "/usr/lib/jvm/java-8-openjdk-arm64"
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/install-gflags"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Build with cmake
|
||||
run: |-
|
||||
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
|
||||
- name: Build Java with cmake
|
||||
run: |-
|
||||
rm -rf build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DJNI=1 -DCMAKE_BUILD_TYPE=Release -DWITH_GFLAGS=1 ..
|
||||
make -j4 rocksdb rocksdbjni
|
||||
- uses: "./.github/actions/post-steps"
|
||||
@@ -1,609 +0,0 @@
|
||||
name: facebook/rocksdb/pr-jobs
|
||||
on: [push, pull_request]
|
||||
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,
|
||||
# making it easy to overlook something broken. Grouping everything into one
|
||||
# workflow minimizes the problem because it will be suspicious if there are
|
||||
# no GHA results.
|
||||
#
|
||||
# The if: ${{ github.repository_owner == 'facebook' }} lines prevent the
|
||||
# jobs from attempting to run on repo forks, because of a few problems:
|
||||
# * runs-on labels are repository (owner) specific, so the job might wait
|
||||
# for days waiting for a runner that simply isn't available.
|
||||
# * Pushes to branches on forks for pull requests (the normal process) would
|
||||
# run the workflow jobs twice: once in the pull-from fork and once for the PR
|
||||
# destination repo. This is wasteful and dumb.
|
||||
# * It is not known how to avoid copy-pasting the line to each job,
|
||||
# increasing the risk of misconfiguration, especially on forks that might
|
||||
# want to run with this GHA setup.
|
||||
#
|
||||
# DEBUGGING WITH SSH: Temporarily add this as a job step, either before the
|
||||
# step of interest without the "if:" line or after the failing step with the
|
||||
# "if:" line. Then use ssh command printed in CI output.
|
||||
# - name: Setup tmate session # TEMPORARY!
|
||||
# if: ${{ failure() }}
|
||||
# uses: mxschmitt/action-tmate@v3
|
||||
# with:
|
||||
# limit-access-to-actor: true
|
||||
|
||||
# ======================== Fast Initial Checks ====================== #
|
||||
check-format-and-targets:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
with:
|
||||
fetch-depth: 0 # Need full checkout to determine merge base
|
||||
fetch-tags: true
|
||||
- uses: "./.github/actions/setup-upstream"
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
- name: Install Dependencies
|
||||
run: python -m pip install --upgrade pip
|
||||
- name: Install argparse
|
||||
run: pip install argparse
|
||||
- name: Download clang-format-diff.py
|
||||
run: wget https://raw.githubusercontent.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
|
||||
- name: Check format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
- name: Compare buckify output
|
||||
run: make check-buck-targets
|
||||
- name: Simple source code checks
|
||||
run: make check-sources
|
||||
# ========================= Linux With Tests ======================== #
|
||||
build-linux:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: make V=1 J=32 -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-mingw:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
|
||||
- name: Build cmake-mingw
|
||||
run: |-
|
||||
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
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-folly:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/build-folly"
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make V=1 -j20 && ctest -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-folly-lite-no-test:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
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-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
|
||||
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 CC=gcc-7 CXX=g++-7 V=1 make -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
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
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- 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' }}
|
||||
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: "(mkdir build && cd build && cmake -DUSE_COROUTINES=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make V=1 -j20 && ctest -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-benchmark:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: mkdir build && cd build && cmake -DWITH_GFLAGS=1 -DWITH_BENCHMARK=1 .. && make V=1 -j20 && ctest -j20
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-encrypted_env-no_compression:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: ENCRYPTED_ENV=1 ROCKSDB_DISABLE_SNAPPY=1 ROCKSDB_DISABLE_ZLIB=1 ROCKSDB_DISABLE_BZIP=1 ROCKSDB_DISABLE_LZ4=1 ROCKSDB_DISABLE_ZSTD=1 make V=1 J=32 -j32 check
|
||||
- run: "./sst_dump --help | grep -E -q 'Supported compression types: kNoCompression$' # Verify no compiled in compression\n"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================== Linux No Test Runs ======================= #
|
||||
build-linux-release:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- run: make V=1 -j32 LIB_MODE=shared release
|
||||
- run: ls librocksdb.so
|
||||
- run: "./db_stress --version"
|
||||
- run: make clean
|
||||
- run: make V=1 -j32 release
|
||||
- run: ls librocksdb.a
|
||||
- run: "./db_stress --version"
|
||||
- run: make clean
|
||||
- run: apt-get remove -y libgflags-dev
|
||||
- run: make V=1 -j32 LIB_MODE=shared release
|
||||
- run: ls librocksdb.so
|
||||
- run: if ./db_stress --version; then false; else true; fi
|
||||
- run: make clean
|
||||
- run: make V=1 -j32 release
|
||||
- run: ls librocksdb.a
|
||||
- run: if ./db_stress --version; then false; else true; fi
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-release-rtti:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 8-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j16 static_lib tools db_bench
|
||||
- run: "./db_stress --version"
|
||||
- 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
|
||||
build-examples:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Build examples
|
||||
run: make V=1 -j4 static_lib && cd examples && make V=1 -j4
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-fuzzers:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Build rocksdb lib
|
||||
run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j4 static_lib
|
||||
- name: Build fuzzers
|
||||
run: cd fuzz && make sst_file_writer_fuzzer db_fuzzer db_map_fuzzer
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 8-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- run: CC=clang CXX=clang++ USE_CLANG=1 PORTABLE=1 make V=1 -j16 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang-13-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j32 all microbench
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-gcc-8-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: CC=gcc-8 CXX=g++-8 V=1 make -j32 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-gcc-10-cxx20-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: CC=gcc-10 CXX=g++-10 V=1 ROCKSDB_CXX_STANDARD=c++20 make -j32 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-gcc-11-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: LIB_MODE=static CC=gcc-11 CXX=g++-11 V=1 make -j32 all microbench
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================== Linux Other Checks ======================= #
|
||||
build-linux-clang10-clang-analyze:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: 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
|
||||
- uses: "./.github/actions/post-steps"
|
||||
- name: compress test report
|
||||
run: tar -cvzf scan_build_report.tar.gz scan_build_report
|
||||
if: failure()
|
||||
- uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: scan-build-report
|
||||
path: scan_build_report.tar.gz
|
||||
build-linux-unity-and-headers:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: gcc:latest
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- run: apt-get update -y && apt-get install -y libgflags-dev
|
||||
- name: Unity build
|
||||
run: make V=1 -j8 unity_test
|
||||
- run: make V=1 -j8 -k check-headers
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-mini-crashtest:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- 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
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================= Linux with Sanitizers ===================== #
|
||||
build-linux-clang10-asan:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 32-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang10-ubsan:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: 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-clang10-mini-tsan:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 32-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: COMPILE_WITH_TSAN=1 CC=clang-13 CXX=clang++-13 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-static_lib-alt_namespace-status_checked:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: 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
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ========================= MacOS build only ======================== #
|
||||
build-macos:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
env:
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 14.3.1
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Build
|
||||
run: ulimit -S -n `ulimit -H -n` && make V=1 J=16 -j16 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ========================= MacOS with Tests ======================== #
|
||||
build-macos-cmake:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
strategy:
|
||||
matrix:
|
||||
run_even_tests: [true, false]
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 14.3.1
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: cmake generate project file
|
||||
run: ulimit -S -n `ulimit -H -n` && mkdir build && cd build && cmake -DWITH_GFLAGS=1 ..
|
||||
- name: Build tests
|
||||
run: cd build && make V=1 -j16
|
||||
- name: Run even tests
|
||||
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j16 -I 0,,2
|
||||
if: ${{ matrix.run_even_tests }}
|
||||
- name: Run odd tests
|
||||
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j16 -I 1,,2
|
||||
if: ${{ ! matrix.run_even_tests }}
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================== Windows with Tests ======================= #
|
||||
# NOTE: some windows jobs are in "nightly" to save resources
|
||||
build-windows-vs2019:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: windows-2019
|
||||
env:
|
||||
CMAKE_GENERATOR: Visual Studio 16 2019
|
||||
CMAKE_PORTABLE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/windows-build-steps"
|
||||
# ============================ Java Jobs ============================ #
|
||||
build-linux-java:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: evolvedbinary/rocksjava:centos6_x64-be
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
# The docker image is intentionally based on an OS that has an older GLIBC version.
|
||||
# That GLIBC is incompatibile with GitHub's actions/checkout. Thus we implement a manual checkout step.
|
||||
- name: Checkout
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
chown `whoami` . || true
|
||||
git clone --no-checkout https://oath2:$GH_TOKEN@github.com/${{ github.repository }}.git .
|
||||
git -c protocol.version=2 fetch --update-head-ok --no-tags --prune --no-recurse-submodules --depth=1 origin +${{ github.sha }}:${{ github.ref }}
|
||||
git checkout --progress --force ${{ github.ref }}
|
||||
git log -1 --format='%H'
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Test RocksDBJava
|
||||
run: scl enable devtoolset-7 'make V=1 J=8 -j8 jtest'
|
||||
# NOTE: post-steps skipped because of compatibility issues with docker image
|
||||
build-linux-java-static:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: evolvedbinary/rocksjava:centos6_x64-be
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
# The docker image is intentionally based on an OS that has an older GLIBC version.
|
||||
# That GLIBC is incompatibile with GitHub's actions/checkout. Thus we implement a manual checkout step.
|
||||
- name: Checkout
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
chown `whoami` . || true
|
||||
git clone --no-checkout https://oath2:$GH_TOKEN@github.com/${{ github.repository }}.git .
|
||||
git -c protocol.version=2 fetch --update-head-ok --no-tags --prune --no-recurse-submodules --depth=1 origin +${{ github.sha }}:${{ github.ref }}
|
||||
git checkout --progress --force ${{ github.ref }}
|
||||
git log -1 --format='%H'
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Build RocksDBJava Static Library
|
||||
run: scl enable devtoolset-7 'make V=1 J=8 -j8 rocksdbjavastatic'
|
||||
# NOTE: post-steps skipped because of compatibility issues with docker image
|
||||
build-macos-java:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
env:
|
||||
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 14.3.1
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/install-jdk8-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Test RocksDBJava
|
||||
run: make V=1 J=16 -j16 jtest
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-macos-java-static:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
env:
|
||||
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 14.3.1
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/install-jdk8-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Build RocksDBJava x86 and ARM Static Libraries
|
||||
run: make V=1 J=16 -j16 rocksdbjavastaticosx
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-macos-java-static-universal:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
env:
|
||||
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 14.3.1
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/install-jdk8-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Build RocksDBJava Universal Binary Static Library
|
||||
run: make V=1 J=16 -j16 rocksdbjavastaticosx_ub
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-java-pmd:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: evolvedbinary/rocksjava:rockylinux8_x64-be
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/install-maven"
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: PMD RocksDBJava
|
||||
run: make V=1 J=8 -j8 jpmd
|
||||
- uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: pmd-report
|
||||
path: "${{ github.workspace }}/java/target/pmd.xml"
|
||||
- uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: maven-site
|
||||
path: "${{ github.workspace }}/java/target/site"
|
||||
@@ -0,0 +1,45 @@
|
||||
name: Check buck targets and code format
|
||||
on: [push, pull_request]
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check TARGETS file and code format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout feature branch
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Fetch from upstream
|
||||
run: |
|
||||
git remote add upstream https://github.com/facebook/rocksdb.git && git fetch upstream
|
||||
|
||||
- name: Where am I
|
||||
run: |
|
||||
echo git status && git status
|
||||
echo "git remote -v" && git remote -v
|
||||
echo git branch && git branch
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v1
|
||||
|
||||
- name: Install Dependencies
|
||||
run: python -m pip install --upgrade pip
|
||||
|
||||
- name: Install argparse
|
||||
run: pip install argparse
|
||||
|
||||
- name: Download clang-format-diff.py
|
||||
run: wget https://raw.githubusercontent.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
|
||||
|
||||
- name: Check format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
|
||||
- name: Compare buckify output
|
||||
run: make check-buck-targets
|
||||
|
||||
- name: Simple source code checks
|
||||
run: make check-sources
|
||||
@@ -85,7 +85,6 @@ fbcode/
|
||||
fbcode
|
||||
buckifier/*.pyc
|
||||
buckifier/__pycache__
|
||||
.arcconfig
|
||||
|
||||
compile_commands.json
|
||||
clang-format-diff.py
|
||||
|
||||
+19
-24
@@ -76,6 +76,11 @@ if (WITH_WINDOWS_UTF8_FILENAMES)
|
||||
endif()
|
||||
option(ROCKSDB_BUILD_SHARED "Build shared versions of the RocksDB libraries" ON)
|
||||
|
||||
if ($ENV{CIRCLECI})
|
||||
message(STATUS "Build for CircieCI env, a few tests may be disabled")
|
||||
add_definitions(-DCIRCLECI)
|
||||
endif()
|
||||
|
||||
if( NOT DEFINED CMAKE_CXX_STANDARD )
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
@@ -166,7 +171,7 @@ else()
|
||||
if(WITH_ZSTD)
|
||||
find_package(zstd REQUIRED)
|
||||
add_definitions(-DZSTD)
|
||||
include_directories(${ZSTD_INCLUDE_DIRS})
|
||||
include_directories(${ZSTD_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS zstd::zstd)
|
||||
endif()
|
||||
endif()
|
||||
@@ -249,7 +254,7 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "loongarch64")
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "loongarch64")
|
||||
|
||||
set(PORTABLE 0 CACHE STRING "Minimum CPU arch to support, or 0 = current CPU, 1 = baseline CPU")
|
||||
if(PORTABLE MATCHES "1|ON|YES|TRUE|Y")
|
||||
if(PORTABLE STREQUAL 1)
|
||||
# Usually nothing to do; compiler default is typically the most general
|
||||
if(NOT MSVC)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390x")
|
||||
@@ -259,7 +264,14 @@ if(PORTABLE MATCHES "1|ON|YES|TRUE|Y")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=loongarch64")
|
||||
endif()
|
||||
endif()
|
||||
elseif(PORTABLE MATCHES "0|OFF|NO|FALSE|N")
|
||||
elseif(PORTABLE MATCHES [^0]+)
|
||||
# Name of a CPU arch spec or feature set to require
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:${PORTABLE}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${PORTABLE}")
|
||||
endif()
|
||||
else()
|
||||
if(MSVC)
|
||||
# NOTE: No auto-detection of current CPU, but instead assume some useful
|
||||
# level of optimization is supported
|
||||
@@ -273,13 +285,6 @@ elseif(PORTABLE MATCHES "0|OFF|NO|FALSE|N")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
# Name of a CPU arch spec or feature set to require
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:${PORTABLE}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${PORTABLE}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
@@ -371,7 +376,7 @@ option(WITH_NUMA "build with NUMA policy support" OFF)
|
||||
if(WITH_NUMA)
|
||||
find_package(NUMA REQUIRED)
|
||||
add_definitions(-DNUMA)
|
||||
include_directories(${NUMA_INCLUDE_DIRS})
|
||||
include_directories(${NUMA_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS NUMA::NUMA)
|
||||
endif()
|
||||
|
||||
@@ -627,7 +632,6 @@ set(SOURCES
|
||||
cache/secondary_cache.cc
|
||||
cache/secondary_cache_adapter.cc
|
||||
cache/sharded_cache.cc
|
||||
cache/tiered_secondary_cache.cc
|
||||
db/arena_wrapped_db_iter.cc
|
||||
db/blob/blob_contents.cc
|
||||
db/blob/blob_fetcher.cc
|
||||
@@ -710,7 +714,6 @@ set(SOURCES
|
||||
db/wal_manager.cc
|
||||
db/wide/wide_column_serialization.cc
|
||||
db/wide/wide_columns.cc
|
||||
db/wide/wide_columns_helper.cc
|
||||
db/write_batch.cc
|
||||
db/write_batch_base.cc
|
||||
db/write_controller.cc
|
||||
@@ -767,7 +770,6 @@ set(SOURCES
|
||||
options/configurable.cc
|
||||
options/customizable.cc
|
||||
options/db_options.cc
|
||||
options/offpeak_time_info.cc
|
||||
options/options.cc
|
||||
options/options_helper.cc
|
||||
options/options_parser.cc
|
||||
@@ -1101,15 +1103,11 @@ set(BUILD_VERSION_CC ${CMAKE_BINARY_DIR}/build_version.cc)
|
||||
configure_file(util/build_version.cc.in ${BUILD_VERSION_CC} @ONLY)
|
||||
|
||||
add_library(${ROCKSDB_STATIC_LIB} STATIC ${SOURCES} ${BUILD_VERSION_CC})
|
||||
target_include_directories(${ROCKSDB_STATIC_LIB} PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB} PRIVATE
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(ROCKSDB_BUILD_SHARED)
|
||||
add_library(${ROCKSDB_SHARED_LIB} SHARED ${SOURCES} ${BUILD_VERSION_CC})
|
||||
target_include_directories(${ROCKSDB_SHARED_LIB} PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
|
||||
target_link_libraries(${ROCKSDB_SHARED_LIB} PRIVATE
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
@@ -1264,7 +1262,6 @@ if(WITH_TESTS)
|
||||
cache/cache_test.cc
|
||||
cache/compressed_secondary_cache_test.cc
|
||||
cache/lru_cache_test.cc
|
||||
cache/tiered_secondary_cache_test.cc
|
||||
db/blob/blob_counting_iterator_test.cc
|
||||
db/blob/blob_file_addition_test.cc
|
||||
db/blob/blob_file_builder_test.cc
|
||||
@@ -1360,7 +1357,6 @@ if(WITH_TESTS)
|
||||
db/wal_edit_test.cc
|
||||
db/wide/db_wide_basic_test.cc
|
||||
db/wide/wide_column_serialization_test.cc
|
||||
db/wide/wide_columns_helper_test.cc
|
||||
db/write_batch_test.cc
|
||||
db/write_callback_test.cc
|
||||
db/write_controller_test.cc
|
||||
@@ -1423,7 +1419,6 @@ if(WITH_TESTS)
|
||||
util/ribbon_test.cc
|
||||
util/slice_test.cc
|
||||
util/slice_transform_test.cc
|
||||
util/string_util_test.cc
|
||||
util/timer_queue_test.cc
|
||||
util/timer_test.cc
|
||||
util/thread_list_test.cc
|
||||
@@ -1473,7 +1468,7 @@ if(WITH_TESTS)
|
||||
utilities/cassandra/test_utils.cc
|
||||
)
|
||||
enable_testing()
|
||||
add_custom_target(rocksdb_check COMMAND ${CMAKE_CTEST_COMMAND})
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})
|
||||
set(TESTUTILLIB testutillib${ARTIFACT_SUFFIX})
|
||||
add_library(${TESTUTILLIB} STATIC ${TESTUTIL_SOURCE})
|
||||
target_link_libraries(${TESTUTILLIB} ${ROCKSDB_LIB} ${FOLLY_LIBS})
|
||||
@@ -1498,7 +1493,7 @@ if(WITH_TESTS)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} testharness gtest ${THIRDPARTY_LIBS} ${ROCKSDB_LIB})
|
||||
if(NOT "${exename}" MATCHES "db_sanity_test")
|
||||
gtest_discover_tests(${exename} DISCOVERY_TIMEOUT 120)
|
||||
add_dependencies(rocksdb_check ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${exename}${ARTIFACT_SUFFIX})
|
||||
endif()
|
||||
endforeach(sourcefile ${TESTS})
|
||||
|
||||
@@ -1518,7 +1513,7 @@ if(WITH_TESTS)
|
||||
add_executable(c_test db/c_test.c)
|
||||
target_link_libraries(c_test ${ROCKSDB_LIB_FOR_C} testharness)
|
||||
add_test(NAME c_test COMMAND c_test${ARTIFACT_SUFFIX})
|
||||
add_dependencies(rocksdb_check c_test)
|
||||
add_dependencies(check c_test)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
+3
-225
@@ -1,237 +1,15 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 9.0.1 (04/11/2024)
|
||||
### Bug Fixes
|
||||
* Fixed CMake Javadoc and source jar builds
|
||||
* Fixed Java `SstFileMetaData` to prevent throwing `java.lang.NoSuchMethodError`
|
||||
|
||||
## 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.
|
||||
* Add support for user-defined timestamps in APIs `DeleteFilesInRanges` and `GetPropertiesOfTablesInRange`.
|
||||
* Mark wal\_compression feature as production-ready. Currently only compatible with ZSTD compression.
|
||||
|
||||
### Public API Changes
|
||||
* Allow setting Stderr logger via C API
|
||||
* Declare one Get and one MultiGet variant as pure virtual, and make all the other variants non-overridable. The methods required to be implemented by derived classes of DB allow returning timestamps. It is up to the implementation to check and return an error if timestamps are not supported. The non-batched MultiGet APIs are reimplemented in terms of batched MultiGet, so callers might see a performance improvement.
|
||||
* 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.
|
||||
* 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".
|
||||
* 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)
|
||||
|
||||
### Behavior Changes
|
||||
* format\_version=6 is the new default setting in BlockBasedTableOptions, for more robust data integrity checking. DBs and SST files written with this setting cannot be read by RocksDB versions before 8.6.0.
|
||||
* Compactions can be scheduled in parallel in an additional scenario: multiple files are marked for compaction within a single column family
|
||||
* For leveled compaction, RocksDB will try to do intra-L0 compaction if the total L0 size is small compared to Lbase (#12214). Users with atomic_flush=true are more likely to see the impact of this change.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a data race in `DBImpl::RenameTempFileToOptionsFile`.
|
||||
* Fix some perf context statistics error in write steps. which include missing write_memtable_time in unordered_write. missing write_memtable_time in PipelineWrite when Writer stat is STATE_PARALLEL_MEMTABLE_WRITER. missing write_delay_time when calling DelayWrite in WriteImplWALOnly function.
|
||||
* Fixed a bug that can, under rare circumstances, cause MultiGet to return an incorrect result for a duplicate key in a MultiGet batch.
|
||||
* Fix a bug where older data of an ingested key can be returned for read when universal compaction is used
|
||||
|
||||
## 8.11.0 (01/19/2024)
|
||||
### New Features
|
||||
* Add new statistics: `rocksdb.sst.write.micros` measures time of each write to SST file; `rocksdb.file.write.{flush|compaction|db.open}.micros` measure time of each write to SST table (currently only block-based table format) and blob file for flush, compaction and db open.
|
||||
|
||||
### Public API Changes
|
||||
* Added another enumerator `kVerify` to enum class `FileOperationType` in listener.h. Update your `switch` statements as needed.
|
||||
* Add CompressionOptions to the CompressedSecondaryCacheOptions structure to allow users to specify library specific options when creating the compressed secondary cache.
|
||||
* Deprecated several options: `level_compaction_dynamic_file_size`, `ignore_max_compaction_bytes_for_input`, `check_flush_compaction_key_order`, `flush_verify_memtable_count`, `compaction_verify_record_count`, `fail_if_options_file_error`, and `enforce_single_del_contracts`
|
||||
* Exposed options ttl via c api.
|
||||
|
||||
### Behavior Changes
|
||||
* `rocksdb.blobdb.blob.file.write.micros` expands to also measure time writing the header and footer. Therefore the COUNT may be higher and values may be smaller than before. For stacked BlobDB, it no longer measures the time of explictly flushing blob file.
|
||||
* Files will be compacted to the next level if the data age exceeds periodic_compaction_seconds except for the last level.
|
||||
* Reduced the compaction debt ratio trigger for scheduling parallel compactions
|
||||
* For leveled compaction with default compaction pri (kMinOverlappingRatio), files marked for compaction will be prioritized over files not marked when picking a file from a level for compaction.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix bug in auto_readahead_size that combined with IndexType::kBinarySearchWithFirstKey + fails or iterator lands at a wrong key
|
||||
* Fixed some cases in which DB file corruption was detected but ignored on creating a backup with BackupEngine.
|
||||
* Fix bugs where `rocksdb.blobdb.blob.file.synced` includes blob files failed to get synced and `rocksdb.blobdb.blob.file.bytes.written` includes blob bytes failed to get written.
|
||||
* Fixed a possible memory leak or crash on a failure (such as I/O error) in automatic atomic flush of multiple column families.
|
||||
* Fixed some cases of in-memory data corruption using mmap reads with `BackupEngine`, `sst_dump`, or `ldb`.
|
||||
* Fixed issues with experimental `preclude_last_level_data_seconds` option that could interfere with expected data tiering.
|
||||
* Fixed the handling of the edge case when all existing blob files become unreferenced. Such files are now correctly deleted.
|
||||
|
||||
## 8.10.0 (12/15/2023)
|
||||
### New Features
|
||||
* Provide support for async_io to trim readahead_size by doing block cache lookup
|
||||
* Added initial wide-column support in `WriteBatchWithIndex`. This includes the `PutEntity` API and support for wide columns in the existing read APIs (`GetFromBatch`, `GetFromBatchAndDB`, `MultiGetFromBatchAndDB`, and `BaseDeltaIterator`).
|
||||
|
||||
### Public API Changes
|
||||
* Custom implementations of `TablePropertiesCollectorFactory` may now return a `nullptr` collector to decline processing a file, reducing callback overheads in such cases.
|
||||
|
||||
### Behavior Changes
|
||||
* Make ReadOptions.auto_readahead_size default true which does prefetching optimizations for forward scans if iterate_upper_bound and block_cache is also specified.
|
||||
* Compactions can be scheduled in parallel in an additional scenario: high compaction debt relative to the data size
|
||||
* HyperClockCache now has built-in protection against excessive CPU consumption under the extreme stress condition of no (or very few) evictable cache entries, which can slightly increase memory usage such conditions. New option `HyperClockCacheOptions::eviction_effort_cap` controls the space-time trade-off of the response. The default should be generally well-balanced, with no measurable affect on normal operation.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a corner case with auto_readahead_size where Prev Operation returns NOT SUPPORTED error when scans direction is changed from forward to backward.
|
||||
* Avoid destroying the periodic task scheduler's default timer in order to prevent static destruction order issues.
|
||||
* Fix double counting of BYTES_WRITTEN ticker when doing writes with transactions.
|
||||
* Fix a WRITE_STALL counter that was reporting wrong value in few cases.
|
||||
* A lookup by MultiGet in a TieredCache that goes to the local flash cache and finishes with very low latency, i.e before the subsequent call to WaitAll, is ignored, resulting in a false negative and a memory leak.
|
||||
|
||||
### Performance Improvements
|
||||
* Java API extensions to improve consistency and completeness of APIs
|
||||
1 Extended `RocksDB.get([ColumnFamilyHandle columnFamilyHandle,] ReadOptions opt, ByteBuffer key, ByteBuffer value)` which now accepts indirect buffer parameters as well as direct buffer parameters
|
||||
2 Extended `RocksDB.put( [ColumnFamilyHandle columnFamilyHandle,] WriteOptions writeOpts, final ByteBuffer key, final ByteBuffer value)` which now accepts indirect buffer parameters as well as direct buffer parameters
|
||||
3 Added `RocksDB.merge([ColumnFamilyHandle columnFamilyHandle,] WriteOptions writeOptions, ByteBuffer key, ByteBuffer value)` methods with the same parameter options as `put(...)` - direct and indirect buffers are supported
|
||||
4 Added `RocksIterator.key( byte[] key [, int offset, int len])` methods which retrieve the iterator key into the supplied buffer
|
||||
5 Added `RocksIterator.value( byte[] value [, int offset, int len])` methods which retrieve the iterator value into the supplied buffer
|
||||
6 Deprecated `get(final ColumnFamilyHandle columnFamilyHandle, final ReadOptions readOptions, byte[])` in favour of `get(final ReadOptions readOptions, final ColumnFamilyHandle columnFamilyHandle, byte[])` which has consistent parameter ordering with other methods in the same class
|
||||
7 Added `Transaction.get( ReadOptions opt, [ColumnFamilyHandle columnFamilyHandle, ] byte[] key, byte[] value)` methods which retrieve the requested value into the supplied buffer
|
||||
8 Added `Transaction.get( ReadOptions opt, [ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value)` methods which retrieve the requested value into the supplied buffer
|
||||
9 Added `Transaction.getForUpdate( ReadOptions readOptions, [ColumnFamilyHandle columnFamilyHandle, ] byte[] key, byte[] value, boolean exclusive [, boolean doValidate])` methods which retrieve the requested value into the supplied buffer
|
||||
10 Added `Transaction.getForUpdate( ReadOptions readOptions, [ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value, boolean exclusive [, boolean doValidate])` methods which retrieve the requested value into the supplied buffer
|
||||
11 Added `Transaction.getIterator()` method as a convenience which defaults the `ReadOptions` value supplied to existing `Transaction.iterator()` methods. This mirrors the existing `RocksDB.iterator()` method.
|
||||
12 Added `Transaction.put([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value [, boolean assumeTracked])` methods which supply the key, and the value to be written in a `ByteBuffer` parameter
|
||||
13 Added `Transaction.merge([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value [, boolean assumeTracked])` methods which supply the key, and the value to be written/merged in a `ByteBuffer` parameter
|
||||
14 Added `Transaction.mergeUntracked([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value)` methods which supply the key, and the value to be written/merged in a `ByteBuffer` parameter
|
||||
|
||||
|
||||
## 8.9.0 (11/17/2023)
|
||||
### New Features
|
||||
* Add GetEntity() and PutEntity() API implementation for Attribute Group support. Through the use of Column Families, AttributeGroup enables users to logically group wide-column entities.
|
||||
|
||||
### Public API Changes
|
||||
* Added rocksdb_ratelimiter_create_auto_tuned API to create an auto-tuned GenericRateLimiter.
|
||||
* Added clipColumnFamily() to the Java API to clip the entries in the CF according to the range [begin_key, end_key).
|
||||
* Make the `EnableFileDeletion` API not default to force enabling. For users that rely on this default behavior and still
|
||||
want to continue to use force enabling, they need to explicitly pass a `true` to `EnableFileDeletion`.
|
||||
* Add new Cache APIs GetSecondaryCacheCapacity() and GetSecondaryCachePinnedUsage() to return the configured capacity, and cache reservation charged to the secondary cache.
|
||||
|
||||
### Behavior Changes
|
||||
* During off-peak hours defined by `daily_offpeak_time_utc`, the compaction picker will select a larger number of files for periodic compaction. This selection will include files that are projected to expire by the next off-peak start time, ensuring that these files are not chosen for periodic compaction outside of off-peak hours.
|
||||
* If an error occurs when writing to a trace file after `DB::StartTrace()`, the subsequent trace writes are skipped to avoid writing to a file that has previously seen error. In this case, `DB::EndTrace()` will also return a non-ok status with info about the error occured previously in its status message.
|
||||
* Deleting stale files upon recovery are delegated to SstFileManger if available so they can be rate limited.
|
||||
* Make RocksDB only call `TablePropertiesCollector::Finish()` once.
|
||||
* When `WAL_ttl_seconds > 0`, we now process archived WALs for deletion at least every `WAL_ttl_seconds / 2` seconds. Previously it could be less frequent in case of small `WAL_ttl_seconds` values when size-based expiration (`WAL_size_limit_MB > 0 `) was simultaneously enabled.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a crash or assertion failure bug in experimental new HyperClockCache variant, especially when running with a SecondaryCache.
|
||||
* Fix a race between flush error recovery and db destruction that can lead to db crashing.
|
||||
* Fixed some bugs in the index builder/reader path for user-defined timestamps in Memtable only feature.
|
||||
|
||||
## 8.8.0 (10/23/2023)
|
||||
### New Features
|
||||
* Introduce AttributeGroup by adding the first AttributeGroup support API, MultiGetEntity(). Through the use of Column Families, AttributeGroup enables users to logically group wide-column entities. More APIs to support AttributeGroup will come soon, including GetEntity, PutEntity, and others.
|
||||
* Added new tickers `rocksdb.fifo.{max.size|ttl}.compactions` to count FIFO compactions that drop files for different reasons
|
||||
* Add an experimental offpeak duration awareness by setting `DBOptions::daily_offpeak_time_utc` in "HH:mm-HH:mm" format. This information will be used for resource optimization in the future
|
||||
* Users can now change the max bytes granted in a single refill period (i.e, burst) during runtime by `SetSingleBurstBytes()` for RocksDB rate limiter
|
||||
|
||||
### Public API Changes
|
||||
* The default value of `DBOptions::fail_if_options_file_error` changed from `false` to `true`. Operations that set in-memory options (e.g., `DB::Open*()`, `DB::SetOptions()`, `DB::CreateColumnFamily*()`, and `DB::DropColumnFamily()`) but fail to persist the change will now return a non-OK `Status` by default.
|
||||
|
||||
### Behavior Changes
|
||||
* For non direct IO, eliminate the file system prefetching attempt for compaction read when `Options::compaction_readahead_size` is 0
|
||||
* During a write stop, writes now block on in-progress recovery attempts
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in auto_readahead_size where first_internal_key of index blocks wasn't copied properly resulting in corruption error when first_internal_key was used for comparison.
|
||||
* Fixed a bug where compaction read under non direct IO still falls back to RocksDB internal prefetching after file system's prefetching returns non-OK status other than `Status::NotSupported()`
|
||||
* Add bounds check in WBWIIteratorImpl and make BaseDeltaIterator, WriteUnpreparedTxn and WritePreparedTxn respect the upper bound and lower bound in ReadOption. See 11680.
|
||||
* Fixed the handling of wide-column base values in the `max_successive_merges` logic.
|
||||
* Fixed a rare race bug involving a concurrent combination of Create/DropColumnFamily and/or Set(DB)Options that could lead to inconsistency between (a) the DB's reported options state, (b) the DB options in effect, and (c) the latest persisted OPTIONS file.
|
||||
* Fixed a possible underflow when computing the compressed secondary cache share of memory reservations while updating the compressed secondary to total block cache ratio.
|
||||
|
||||
### Performance Improvements
|
||||
* Improved the I/O efficiency of DB::Open a new DB with `create_missing_column_families=true` and many column families.
|
||||
|
||||
## 8.7.0 (09/22/2023)
|
||||
### New Features
|
||||
* Added an experimental new "automatic" variant of HyperClockCache that does not require a prior estimate of the average size of cache entries. This variant is activated when HyperClockCacheOptions::estimated\_entry\_charge = 0 and has essentially the same concurrency benefits as the existing HyperClockCache.
|
||||
* Add a new statistic `COMPACTION_CPU_TOTAL_TIME` that records cumulative compaction cpu time. This ticker is updated regularly while a compaction is running.
|
||||
* Add `GetEntity()` API for ReadOnly DB and Secondary DB.
|
||||
* Add a new iterator API `Iterator::Refresh(const Snapshot *)` that allows iterator to be refreshed while using the input snapshot to read.
|
||||
* Added a new read option `merge_operand_count_threshold`. When the number of merge operands applied during a successful point lookup exceeds this threshold, the query will return a special OK status with a new subcode `kMergeOperandThresholdExceeded`. Applications might use this signal to take action to reduce the number of merge operands for the affected key(s), for example by running a compaction.
|
||||
* For `NewRibbonFilterPolicy()`, made the `bloom_before_level` option mutable through the Configurable interface and the SetOptions API, allowing dynamic switching between all-Bloom and all-Ribbon configurations, and configurations in between. See comments on `NewRibbonFilterPolicy()`
|
||||
* RocksDB now allows the block cache to be stacked on top of a compressed secondary cache and a non-volatile secondary cache, thus creating a three-tier cache. To set it up, use the `NewTieredCache()` API in rocksdb/cache.h..
|
||||
* Added a new wide-column aware full merge API called `FullMergeV3` to `MergeOperator`. `FullMergeV3` supports wide columns both as base value and merge result, which enables the application to perform more general transformations during merges. For backward compatibility, the default implementation implements the earlier logic of applying the merge operation to the default column of any wide-column entities. Specifically, if there is no base value or the base value is a plain key-value, the default implementation falls back to `FullMergeV2`. If the base value is a wide-column entity, the default implementation invokes `FullMergeV2` to perform the merge on the default column, and leaves any other columns unchanged.
|
||||
* Add wide column support to ldb commands (scan, dump, idump, dump_wal) and sst_dump tool's scan command
|
||||
|
||||
### Public API Changes
|
||||
* Expose more information about input files used in table creation (if any) in `CompactionFilter::Context`. See `CompactionFilter::Context::input_start_level`,`CompactionFilter::Context::input_table_properties` for more.
|
||||
* `Options::compaction_readahead_size` 's default value is changed from 0 to 2MB.
|
||||
* When using LZ4 compression, the `acceleration` parameter is configurable by setting the negated value in `CompressionOptions::level`. For example, `CompressionOptions::level=-10` will set `acceleration=10`
|
||||
* The `NewTieredCache` API has been changed to take the total cache capacity (inclusive of both the primary and the compressed secondary cache) and the ratio of total capacity to allocate to the compressed cache. These are specified in `TieredCacheOptions`. Any capacity specified in `LRUCacheOptions`, `HyperClockCacheOptions` and `CompressedSecondaryCacheOptions` is ignored. A new API, `UpdateTieredCache` is provided to dynamically update the total capacity, ratio of compressed cache, and admission policy.
|
||||
* The `NewTieredVolatileCache()` API in rocksdb/cache.h has been renamed to `NewTieredCache()`.
|
||||
|
||||
### Behavior Changes
|
||||
* Compaction read performance will regress when `Options::compaction_readahead_size` is explicitly set to 0
|
||||
* Universal size amp compaction will conditionally exclude some of the newest L0 files when selecting input with a small negative impact to size amp. This is to prevent a large number of L0 files from being locked by a size amp compaction, potentially leading to write stop with a few more flushes.
|
||||
* Change ldb scan command delimiter from ':' to '==>'.
|
||||
|
||||
## 8.4.4 (09/01/2023)
|
||||
### Bug Fixes
|
||||
* Fix a bug where if there is an error reading from offset 0 of a file from L1+ and that the file is not the first file in the sorted run, data can be lost in compaction and read/scan can return incorrect results.
|
||||
* Fix a bug where iterator may return incorrect result for DeleteRange() users if there was an error reading from a file.
|
||||
* Fix a bug with atomic_flush=true that can cause DB to stuck after a flush fails (#11872).
|
||||
* Fix a bug where RocksDB (with atomic_flush=false) can delete output SST files of pending flushes when a previous concurrent flush fails (#11865). This can result in DB entering read-only state with error message like `IO error: No such file or directory: While open a file for random read: /tmp/rocksdbtest-501/db_flush_test_87732_4230653031040984171/000013.sst`.
|
||||
* Fix an assertion fault during seek with async_io when readahead trimming is enabled.
|
||||
* When the compressed secondary cache capacity is reduced to 0, it should be completely disabled. Before this fix, inserts and lookups would still go to the backing `LRUCache` before returning, thus incurring locking overhead. With this fix, inserts and lookups are no-ops and do not add any overhead.
|
||||
* Updating the tiered cache (cache allocated using NewTieredCache()) by calling SetCapacity() on it was not working properly. The initial creation would set the primary cache capacity to the combined primary and compressed secondary cache capacity. But SetCapacity() would just set the primary cache capacity. With this fix, the user always specifies the total budget and compressed secondary cache ratio on creation. Subsequently, SetCapacity() will distribute the new capacity across the two caches by the same ratio.
|
||||
* Fixed a bug in `MultiGet` for cleaning up SuperVersion acquired with locking db mutex.
|
||||
* Fix a bug where row cache can falsely return kNotFound even though row cache entry is hit.
|
||||
* Fixed a race condition in `GenericRateLimiter` that could cause it to stop granting requests
|
||||
* Fix a bug (Issue #10257) where DB can hang after write stall since no compaction is scheduled (#11764).
|
||||
* Add a fix for async_io where during seek, when reading a block for seeking a target key in a file without any readahead, the iterator aligned the read on a page boundary and reading more than necessary. This increased the storage read bandwidth usage.
|
||||
* Fix an issue in sst dump tool to handle bounds specified for data with user-defined timestamps.
|
||||
* When auto_readahead_size is enabled, update readahead upper bound during readahead trimming when reseek changes iterate_upper_bound dynamically.
|
||||
* Fixed a bug where `rocksdb.file.read.verify.file.checksums.micros` is not populated
|
||||
|
||||
### Performance Improvements
|
||||
* Added additional improvements in tuning readahead_size during Scans when auto_readahead_size is enabled. However it's not supported with Iterator::Prev operation and will return NotSupported error.
|
||||
* During async_io, the Seek happens in 2 phases. Phase 1 starts an asynchronous read on a block cache miss, and phase 2 waits for it to complete and finishes the seek. In both phases, it tries to lookup the block cache for the data block first before looking in the prefetch buffer. It's optimized by doing the block cache lookup only in the first phase that would save some CPU.
|
||||
|
||||
## 8.6.0 (08/18/2023)
|
||||
### New Features
|
||||
* Added enhanced data integrity checking on SST files with new format_version=6. Performance impact is very small or negligible. Previously if SST data was misplaced or re-arranged by the storage layer, it could pass block checksum with higher than 1 in 4 billion probability. With format_version=6, block checksums depend on what file they are in and location within the file. This way, misplaced SST data is no more likely to pass checksum verification than randomly corrupted data. Also in format_version=6, SST footers are checksum-protected.
|
||||
* Add a new feature to trim readahead_size during scans upto upper_bound when iterate_upper_bound is specified. It's enabled through ReadOptions.auto_readahead_size. Users must also specify ReadOptions.iterate_upper_bound.
|
||||
* RocksDB will compare the number of input keys to the number of keys processed after each compaction. Compaction will fail and report Corruption status if the verification fails. Option `compaction_verify_record_count` is introduced for this purpose and is enabled by default.
|
||||
* Add a CF option `bottommost_file_compaction_delay` to allow specifying the delay of bottommost level single-file compactions.
|
||||
* Add support to allow enabling / disabling user-defined timestamps feature for an existing column family in combination with the in-Memtable only feature.
|
||||
* Implement a new admission policy for the compressed secondary cache that admits blocks evicted from the primary cache with the hit bit set. This policy can be specified in TieredVolatileCacheOptions by setting the newly added adm_policy option.
|
||||
* Add a column family option `memtable_max_range_deletions` that limits the number of range deletions in a memtable. RocksDB will try to do an automatic flush after the limit is reached. (#11358)
|
||||
* Add PutEntity API in sst_file_writer
|
||||
* Add `timeout` in microsecond option to `WaitForCompactOptions` to allow timely termination of prolonged waiting in scenarios like recurring recoverable errors, such as out-of-space situations and continuous write streams that sustain ongoing flush and compactions
|
||||
* New statistics `rocksdb.file.read.{get|multiget|db.iterator|verify.checksum|verify.file.checksums}.micros` measure read time of block-based SST tables or blob files during db open, `Get()`, `MultiGet()`, using db iterator, `VerifyFileChecksums()` and `VerifyChecksum()`. They require stats level greater than `StatsLevel::kExceptDetailedTimers`.
|
||||
* Add close_db option to `WaitForCompactOptions` to call Close() after waiting is done.
|
||||
* Add a new compression option `CompressionOptions::checksum` for enabling ZSTD's checksum feature to detect corruption during decompression.
|
||||
|
||||
### Public API Changes
|
||||
* Mark `Options::access_hint_on_compaction_start` related APIs as deprecated. See #11631 for alternative behavior.
|
||||
|
||||
### Behavior Changes
|
||||
* Statistics `rocksdb.sst.read.micros` now includes time spent on multi read and async read into the file
|
||||
* For Universal Compaction users, periodic compaction (option `periodic_compaction_seconds`) will be set to 30 days by default if block based table is used.
|
||||
|
||||
## 8.4.3 (07/27/2023)
|
||||
### Bug Fixes
|
||||
* Fix a bug in FileTTLBooster that can cause users with a large number of levels (more than 65) to see errors like "runtime error: shift exponent .. is too large.." (#11673).
|
||||
|
||||
## 8.5.0 (07/21/2023)
|
||||
### Public API Changes
|
||||
* Removed recently added APIs `GeneralCache` and `MakeSharedGeneralCache()` as our plan changed to stop exposing a general-purpose cache interface. The old forms of these APIs, `Cache` and `NewLRUCache()`, are still available, although general-purpose caching support will be dropped eventually.
|
||||
|
||||
### Behavior Changes
|
||||
* Option `periodic_compaction_seconds` no longer supports FIFO compaction: setting it has no effect on FIFO compactions. FIFO compaction users should only set option `ttl` instead.
|
||||
* Move prefetching responsibility to page cache for compaction read for non directIO use case
|
||||
|
||||
### Performance Improvements
|
||||
* In case of direct_io, if buffer passed by callee is already aligned, RandomAccessFileRead::Read will avoid realloacting a new buffer, reducing memcpy and use already passed aligned buffer.
|
||||
* Small efficiency improvement to HyperClockCache by reducing chance of compiler-generated heap allocations
|
||||
|
||||
### Bug Fixes
|
||||
* Fix use_after_free bug in async_io MultiReads when underlying FS enabled kFSBuffer. kFSBuffer is when underlying FS pass their own buffer instead of using RocksDB scratch in FSReadRequest. Right now it's an experimental feature.
|
||||
* Fix use_after_free bug in async_io MultiReads when underlying FS enabled kFSBuffer. kFSBuffer is when underlying FS pass their own buffer instead of using RocksDB scratch in FSReadRequest.
|
||||
|
||||
## 8.4.0 (06/26/2023)
|
||||
### New Features
|
||||
|
||||
+5
-10
@@ -17,7 +17,7 @@ There are few options when compiling RocksDB:
|
||||
* `make check` will compile and run all the unit tests. `make check` will compile RocksDB in debug mode.
|
||||
|
||||
* `make all` will compile our static library, and all our tools and unit tests. Our tools
|
||||
depend on gflags 2.2.0 or newer. You will need to have gflags installed to run `make all`. This will compile RocksDB in debug mode. Don't
|
||||
depend on gflags. You will need to have gflags installed to run `make all`. This will compile RocksDB in debug mode. Don't
|
||||
use binaries compiled by `make all` in production.
|
||||
|
||||
* By default the binary we produce is optimized for the CPU you're compiling on
|
||||
@@ -77,7 +77,7 @@ most processors made since roughly 2013.
|
||||
|
||||
git clone https://github.com/gflags/gflags.git
|
||||
cd gflags
|
||||
git checkout v2.2.0
|
||||
git checkout v2.0
|
||||
./configure && make && sudo make install
|
||||
|
||||
**Notice**: Once installed, please add the include path for gflags to your `CPATH` environment variable and the
|
||||
@@ -169,26 +169,21 @@ most processors made since roughly 2013.
|
||||
|
||||
* Install the dependencies for RocksDB:
|
||||
|
||||
`pkg_add gmake gflags snappy bzip2 lz4 zstd git bash findutils gnuwatch`
|
||||
pkg_add gmake gflags snappy bzip2 lz4 zstd git jdk bash findutils gnuwatch
|
||||
|
||||
* Build RocksDB from source:
|
||||
|
||||
```bash
|
||||
cd ~
|
||||
git clone https://github.com/facebook/rocksdb.git
|
||||
cd rocksdb
|
||||
gmake static_lib
|
||||
```
|
||||
|
||||
* Build RocksJava from source (optional):
|
||||
* In OpenBSD, JDK depends on XWindows system, so please check that you installed OpenBSD with `xbase` package.
|
||||
* Install dependencies : `pkg_add -v jdk%1.8`
|
||||
```bash
|
||||
|
||||
cd rocksdb
|
||||
export JAVA_HOME=/usr/local/jdk-1.8.0
|
||||
export PATH=$PATH:/usr/local/jdk-1.8.0/bin
|
||||
gmake rocksdbjava SHA256_CMD='sha256 -q'
|
||||
```
|
||||
gmake rocksdbjava
|
||||
|
||||
* **iOS**:
|
||||
* Run: `TARGET_OS=IOS make static_lib`. When building the project which uses rocksdb iOS library, make sure to define an important pre-processing macros: `IOS_CROSS_COMPILE`.
|
||||
|
||||
@@ -88,25 +88,6 @@ endif
|
||||
|
||||
$(info $$DEBUG_LEVEL is $(DEBUG_LEVEL), $$LIB_MODE is $(LIB_MODE))
|
||||
|
||||
# Detect what platform we're building on.
|
||||
# Export some common variables that might have been passed as Make variables
|
||||
# instead of environment variables.
|
||||
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
|
||||
export CXXFLAGS="$(EXTRA_CXXFLAGS)"; \
|
||||
export LDFLAGS="$(EXTRA_LDFLAGS)"; \
|
||||
export COMPILE_WITH_ASAN="$(COMPILE_WITH_ASAN)"; \
|
||||
export COMPILE_WITH_TSAN="$(COMPILE_WITH_TSAN)"; \
|
||||
export COMPILE_WITH_UBSAN="$(COMPILE_WITH_UBSAN)"; \
|
||||
export PORTABLE="$(PORTABLE)"; \
|
||||
export ROCKSDB_NO_FBCODE="$(ROCKSDB_NO_FBCODE)"; \
|
||||
export USE_CLANG="$(USE_CLANG)"; \
|
||||
export LIB_MODE="$(LIB_MODE)"; \
|
||||
export ROCKSDB_CXX_STANDARD="$(ROCKSDB_CXX_STANDARD)"; \
|
||||
export USE_FOLLY="$(USE_FOLLY)"; \
|
||||
"$(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
|
||||
|
||||
# Figure out optimize level.
|
||||
ifneq ($(DEBUG_LEVEL), 2)
|
||||
OPTIMIZE_LEVEL ?= -O2
|
||||
@@ -242,6 +223,25 @@ am__v_AR_1 =
|
||||
AM_LINK = $(AM_V_CCLD)$(CXX) -L. $(patsubst lib%.a, -l%, $(patsubst lib%.$(PLATFORM_SHARED_EXT), -l%, $^)) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
AM_SHARE = $(AM_V_CCLD) $(CXX) $(PLATFORM_SHARED_LDFLAGS)$@ -L. $(patsubst lib%.$(PLATFORM_SHARED_EXT), -l%, $^) $(EXEC_LDFLAGS) $(LDFLAGS) -o $@
|
||||
|
||||
# Detect what platform we're building on.
|
||||
# Export some common variables that might have been passed as Make variables
|
||||
# instead of environment variables.
|
||||
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
|
||||
export CXXFLAGS="$(EXTRA_CXXFLAGS)"; \
|
||||
export LDFLAGS="$(EXTRA_LDFLAGS)"; \
|
||||
export COMPILE_WITH_ASAN="$(COMPILE_WITH_ASAN)"; \
|
||||
export COMPILE_WITH_TSAN="$(COMPILE_WITH_TSAN)"; \
|
||||
export COMPILE_WITH_UBSAN="$(COMPILE_WITH_UBSAN)"; \
|
||||
export PORTABLE="$(PORTABLE)"; \
|
||||
export ROCKSDB_NO_FBCODE="$(ROCKSDB_NO_FBCODE)"; \
|
||||
export USE_CLANG="$(USE_CLANG)"; \
|
||||
export LIB_MODE="$(LIB_MODE)"; \
|
||||
export ROCKSDB_CXX_STANDARD="$(ROCKSDB_CXX_STANDARD)"; \
|
||||
export USE_FOLLY="$(USE_FOLLY)"; \
|
||||
"$(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
|
||||
|
||||
ROCKSDB_PLUGIN_MKS = $(foreach plugin, $(ROCKSDB_PLUGINS), plugin/$(plugin)/*.mk)
|
||||
include $(ROCKSDB_PLUGIN_MKS)
|
||||
ROCKSDB_PLUGIN_PROTO =ROCKSDB_NAMESPACE::ObjectLibrary\&, const std::string\&
|
||||
@@ -539,8 +539,7 @@ endif
|
||||
|
||||
ifdef USE_CLANG
|
||||
# Used by some teams in Facebook
|
||||
WARNING_FLAGS += -Wshift-sign-overflow -Wambiguous-reversed-operator \
|
||||
-Wimplicit-fallthrough -Wreinterpret-base-class -Wundefined-reinterpret-cast
|
||||
WARNING_FLAGS += -Wshift-sign-overflow -Wambiguous-reversed-operator
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
@@ -995,7 +994,7 @@ endif
|
||||
|
||||
.PHONY: check_0
|
||||
check_0:
|
||||
@printf '%s\n' '' \
|
||||
printf '%s\n' '' \
|
||||
'To monitor subtest <duration,pass/fail,name>,' \
|
||||
' run "make watch-log" in a separate window' ''; \
|
||||
{ \
|
||||
@@ -1017,7 +1016,7 @@ valgrind-exclude-regexp = InlineSkipTest.ConcurrentInsert|TransactionStressTest.
|
||||
.PHONY: valgrind_check_0
|
||||
valgrind_check_0: test_log_prefix := valgrind_
|
||||
valgrind_check_0:
|
||||
@printf '%s\n' '' \
|
||||
printf '%s\n' '' \
|
||||
'To monitor subtest <duration,pass/fail,name>,' \
|
||||
' run "make watch-log" in a separate window' ''; \
|
||||
{ \
|
||||
@@ -1769,9 +1768,6 @@ cuckoo_table_db_test: $(OBJ_DIR)/db/cuckoo_table_db_test.o $(TEST_LIBRARY) $(LIB
|
||||
listener_test: $(OBJ_DIR)/db/listener_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
string_util_test: $(OBJ_DIR)/util/string_util_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
thread_list_test: $(OBJ_DIR)/util/thread_list_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1889,9 +1885,6 @@ compressed_secondary_cache_test: $(OBJ_DIR)/cache/compressed_secondary_cache_tes
|
||||
lru_cache_test: $(OBJ_DIR)/cache/lru_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
tiered_secondary_cache_test: $(OBJ_DIR)/cache/tiered_secondary_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
range_del_aggregator_test: $(OBJ_DIR)/db/range_del_aggregator_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1991,9 +1984,6 @@ cache_reservation_manager_test: $(OBJ_DIR)/cache/cache_reservation_manager_test.
|
||||
wide_column_serialization_test: $(OBJ_DIR)/db/wide/wide_column_serialization_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
wide_columns_helper_test: $(OBJ_DIR)/db/wide/wide_columns_helper_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
PREFIX ?= /usr/local
|
||||
@@ -2064,7 +2054,7 @@ JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
|
||||
ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
ARCH := $(shell isainfo -b)
|
||||
else ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
ifneq (,$(filter amd64 ppc64 ppc64le s390x arm64 aarch64 riscv64 sparc64 loongarch64, $(MACHINE)))
|
||||
ifneq (,$(filter amd64 ppc64 ppc64le s390x arm64 aarch64 sparc64 loongarch64, $(MACHINE)))
|
||||
ARCH := 64
|
||||
else
|
||||
ARCH := 32
|
||||
@@ -2085,7 +2075,7 @@ ifneq ($(origin JNI_LIBC), undefined)
|
||||
endif
|
||||
|
||||
ifeq (,$(ROCKSDBJNILIB))
|
||||
ifneq (,$(filter ppc% s390x arm64 aarch64 riscv64 sparc64 loongarch64, $(MACHINE)))
|
||||
ifneq (,$(filter ppc% s390x arm64 aarch64 sparc64 loongarch64, $(MACHINE)))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE)$(JNI_LIBC_POSTFIX).so
|
||||
else
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH)$(JNI_LIBC_POSTFIX).so
|
||||
@@ -2098,8 +2088,8 @@ ROCKSDB_JAVADOCS_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-javadoc.jar
|
||||
ROCKSDB_SOURCES_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-sources.jar
|
||||
SHA256_CMD = sha256sum
|
||||
|
||||
ZLIB_VER ?= 1.3.1
|
||||
ZLIB_SHA256 ?= 9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23
|
||||
ZLIB_VER ?= 1.2.13
|
||||
ZLIB_SHA256 ?= b3a24de97a8fdbc835b9833169501030b8977031bcb54b3b3ac13740f846ab30
|
||||
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.8
|
||||
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
|
||||
@@ -2107,11 +2097,11 @@ BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
|
||||
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
|
||||
LZ4_VER ?= 1.9.3
|
||||
LZ4_SHA256 ?= 030644df4611007ff7dc962d981f390361e6c97a34e5cbc393ddfbe019ffe2c1
|
||||
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
|
||||
ZSTD_VER ?= 1.5.5
|
||||
ZSTD_SHA256 ?= 98e9c3d949d1b924e28e01eccb7deed865eefebf25c2f21c702e5cd5b63b85e1
|
||||
ZSTD_VER ?= 1.4.9
|
||||
ZSTD_SHA256 ?= acf714d98e3db7b876e5b540cbf6dee298f60eb3c0723104f6d3f065cd60d6a8
|
||||
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
CURL_SSL_OPTS ?= --tlsv1
|
||||
|
||||
@@ -2350,10 +2340,6 @@ rocksdbjavastaticdockers390x:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_s390x-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerriscv64:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_riscv64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerx86musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x86-musl-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
@@ -2427,9 +2413,6 @@ jtest_run:
|
||||
jtest: rocksdbjava
|
||||
cd java;$(MAKE) sample test
|
||||
|
||||
jpmd: rocksdbjava rocksdbjavageneratepom
|
||||
cd java;$(MAKE) pmd
|
||||
|
||||
jdb_bench:
|
||||
cd java;$(MAKE) db_bench;
|
||||
|
||||
|
||||
+1
-2
@@ -5,5 +5,4 @@ This is the list of all known third-party plugins for RocksDB. If something is m
|
||||
* [ZenFS](https://github.com/westerndigitalcorporation/zenfs): a file system for zoned block devices
|
||||
* [RADOS](https://github.com/riversand963/rocksdb-rados-env): an Env used for interacting with RADOS. Migrated from RocksDB main repo.
|
||||
* [PMEM](https://github.com/pmem/pmem-rocksdb-plugin): a collection of plugins to enable Persistent Memory on RocksDB.
|
||||
* [IPPCP](https://github.com/intel/ippcp-plugin-rocksdb): a plugin to enable encryption on RocksDB based on Intel optimized open source IPP-Crypto library.
|
||||
* [encfs](https://github.com/pegasus-kv/encfs): a plugin to enable encryption on RocksDB based on OpenSSL library.
|
||||
* [IPPCP](https://github.com/intel/ippcp-plugin-rocksdb): a plugin to enable encryption on RocksDB based on Intel optimized open source IPP-Crypto library.
|
||||
@@ -3,6 +3,8 @@
|
||||
# --> DO NOT EDIT MANUALLY <--
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
#
|
||||
# @noautodeps @nocodemods
|
||||
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")
|
||||
|
||||
|
||||
@@ -19,7 +21,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"cache/secondary_cache.cc",
|
||||
"cache/secondary_cache_adapter.cc",
|
||||
"cache/sharded_cache.cc",
|
||||
"cache/tiered_secondary_cache.cc",
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
"db/blob/blob_contents.cc",
|
||||
"db/blob/blob_fetcher.cc",
|
||||
@@ -102,7 +103,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"db/wal_manager.cc",
|
||||
"db/wide/wide_column_serialization.cc",
|
||||
"db/wide/wide_columns.cc",
|
||||
"db/wide/wide_columns_helper.cc",
|
||||
"db/write_batch.cc",
|
||||
"db/write_batch_base.cc",
|
||||
"db/write_controller.cc",
|
||||
@@ -163,7 +163,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"options/configurable.cc",
|
||||
"options/customizable.cc",
|
||||
"options/db_options.cc",
|
||||
"options/offpeak_time_info.cc",
|
||||
"options/options.cc",
|
||||
"options/options_helper.cc",
|
||||
"options/options_parser.cc",
|
||||
@@ -394,7 +393,6 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
|
||||
"db_stress_tool/db_stress_stat.cc",
|
||||
"db_stress_tool/db_stress_test_base.cc",
|
||||
"db_stress_tool/db_stress_tool.cc",
|
||||
"db_stress_tool/db_stress_wide_merge_operator.cc",
|
||||
"db_stress_tool/expected_state.cc",
|
||||
"db_stress_tool/expected_value.cc",
|
||||
"db_stress_tool/multi_ops_txns_stress.cc",
|
||||
@@ -407,8 +405,6 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
|
||||
|
||||
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="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)
|
||||
|
||||
cpp_binary_wrapper(name="db_basic_bench", srcs=["microbench/db_basic_bench.cc"], deps=[], extra_preprocessor_flags=[], extra_bench_libs=True)
|
||||
@@ -5478,12 +5474,6 @@ cpp_unittest_wrapper(name="tiered_compaction_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="tiered_secondary_cache_test",
|
||||
srcs=["cache/tiered_secondary_cache_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="timer_queue_test",
|
||||
srcs=["util/timer_queue_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
@@ -5562,12 +5552,6 @@ cpp_unittest_wrapper(name="wide_column_serialization_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="wide_columns_helper_test",
|
||||
srcs=["db/wide/wide_columns_helper_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="work_queue_test",
|
||||
srcs=["util/work_queue_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
@@ -5621,5 +5605,3 @@ cpp_unittest_wrapper(name="write_unprepared_transaction_test",
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
export_file(name = "tools/db_crashtest.py")
|
||||
@@ -152,9 +152,6 @@ LzLabs is using RocksDB as a storage engine in their multi-database distributed
|
||||
## ArangoDB
|
||||
[ArangoDB](https://www.arangodb.com/) is a native multi-model database with flexible data models for documents, graphs, and key-values, for building high performance applications using a convenient SQL-like query language or JavaScript extensions. It uses RocksDB as its storage engine.
|
||||
|
||||
## Qdrant
|
||||
[Qdrant](https://qdrant.tech/) is an open source vector database, it [uses](https://qdrant.tech/documentation/concepts/storage/) RocksDB as its persistent storage.
|
||||
|
||||
## Milvus
|
||||
[Milvus](https://milvus.io/) is an open source vector database for unstructured data. It uses RocksDB not only as one of the supported kv storage engines, but also as a message queue.
|
||||
|
||||
@@ -164,9 +161,5 @@ LzLabs is using RocksDB as a storage engine in their multi-database distributed
|
||||
## Solana Labs
|
||||
[Solana](https://github.com/solana-labs/solana) is a fast, secure, scalable, and decentralized blockchain. It uses RocksDB as the underlying storage for its ledger store.
|
||||
|
||||
## Apache Kvrocks
|
||||
|
||||
[Apache Kvrocks](https://github.com/apache/kvrocks) is an open-source distributed key-value NoSQL database built on top of RocksDB. It serves as a cost-saving and capacity-increasing alternative drop-in replacement for Redis.
|
||||
|
||||
## Others
|
||||
More databases using RocksDB can be found at [dbdb.io](https://dbdb.io/browse?embeds=rocksdb).
|
||||
@@ -197,10 +197,6 @@ def generate_targets(repo_path, deps_map):
|
||||
TARGETS.add_binary(
|
||||
"db_stress", ["db_stress_tool/db_stress.cc"], [":rocksdb_stress_lib"]
|
||||
)
|
||||
# cache_bench 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]
|
||||
@@ -303,7 +299,6 @@ def generate_targets(repo_path, deps_map):
|
||||
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 TARGETS Summary:"))
|
||||
print(ColorString.info("- %d libs" % TARGETS.total_lib))
|
||||
|
||||
@@ -23,7 +23,7 @@ def pretty_list(lst, indent=8):
|
||||
return res
|
||||
|
||||
|
||||
class TARGETSBuilder:
|
||||
class TARGETSBuilder(object):
|
||||
def __init__(self, path, extra_argv):
|
||||
self.path = path
|
||||
header = targets_cfg.rocksdb_target_header_template.format(
|
||||
@@ -148,9 +148,3 @@ add_c_test_wrapper()
|
||||
).encode("utf-8")
|
||||
)
|
||||
self.total_test = self.total_test + 1
|
||||
|
||||
def export_file(self, name):
|
||||
with open(self.path, "a") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.export_file_template.format(name=name)
|
||||
)
|
||||
@@ -6,6 +6,8 @@ rocksdb_target_header_template = """# This file \100generated by:
|
||||
# --> DO NOT EDIT MANUALLY <--
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
#
|
||||
# @noautodeps @nocodemods
|
||||
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")
|
||||
|
||||
"""
|
||||
@@ -37,7 +39,3 @@ fancy_bench_template = """
|
||||
fancy_bench_wrapper(suite_name="{name}", binary_to_bench_to_metric_list_map={bench_config}, slow={slow}, expected_runtime={expected_runtime}, sl_iterations={sl_iterations}, regression_threshold={regression_threshold})
|
||||
|
||||
"""
|
||||
|
||||
export_file_template = """
|
||||
export_file(name = "{name}")
|
||||
"""
|
||||
+1
-1
@@ -14,7 +14,7 @@ import sys
|
||||
import time
|
||||
|
||||
|
||||
class ColorString:
|
||||
class ColorString(object):
|
||||
"""Generate colorful strings on terminal"""
|
||||
|
||||
HEADER = "\033[95m"
|
||||
|
||||
@@ -163,6 +163,24 @@ case "$TARGET_OS" in
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt -ldl"
|
||||
if test -z "$ROCKSDB_USE_IO_URING"; then
|
||||
ROCKSDB_USE_IO_URING=1
|
||||
fi
|
||||
if test "$ROCKSDB_USE_IO_URING" -ne 0; then
|
||||
# check for liburing
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o test.o 2>/dev/null <<EOF
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
io_uring_queue_init(1, &ring, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -luring"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_IOURING_PRESENT"
|
||||
fi
|
||||
fi
|
||||
# PORT_FILES=port/linux/linux_specific.cc
|
||||
;;
|
||||
SunOS)
|
||||
@@ -596,29 +614,11 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$ROCKSDB_USE_IO_URING"; then
|
||||
ROCKSDB_USE_IO_URING=1
|
||||
fi
|
||||
if [ "$ROCKSDB_USE_IO_URING" -ne 0 -a "$PLATFORM" = OS_LINUX ]; then
|
||||
# check for liburing
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o test.o 2>/dev/null <<EOF
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
io_uring_queue_init(1, &ring, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -luring"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_IOURING_PRESENT"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# TODO(tec): Fix -Wshorten-64-to-32 errors on FreeBSD and enable the warning.
|
||||
# -Wshorten-64-to-32 breaks compilation on FreeBSD aarch64 and i386
|
||||
if ! { [ "$TARGET_OS" = FreeBSD -o "$TARGET_OS" = OpenBSD ] && [ "$TARGET_ARCHITECTURE" = arm64 -o "$TARGET_ARCHITECTURE" = i386 ]; }; then
|
||||
if ! { [ "$TARGET_OS" = FreeBSD ] && [ "$TARGET_ARCHITECTURE" = arm64 -o "$TARGET_ARCHITECTURE" = i386 ]; }; then
|
||||
# Test whether -Wshorten-64-to-32 is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o -Wshorten-64-to-32 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
@@ -647,10 +647,8 @@ if [ "$PORTABLE" == "" ] || [ "$PORTABLE" == 0 ]; then
|
||||
fi
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
|
||||
RISC_ISA=$(cat /proc/cpuinfo | grep -E '^isa\s*:' | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
|
||||
if [ -n "${RISCV_ISA}" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
|
||||
fi
|
||||
RISC_ISA=$(cat /proc/cpuinfo | grep isa | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
|
||||
elif [ "$TARGET_OS" == "IOS" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
else
|
||||
@@ -662,7 +660,8 @@ else
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=rv64gc"
|
||||
RISC_ISA=$(cat /proc/cpuinfo | grep isa | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
|
||||
elif test "$USE_SSE"; then
|
||||
# USE_SSE is DEPRECATED
|
||||
# This is a rough approximation of the old USE_SSE behavior
|
||||
@@ -675,13 +674,13 @@ else
|
||||
fi
|
||||
|
||||
if [[ "${PLATFORM}" == "OS_MACOSX" ]]; then
|
||||
# For portability compile for macOS 10.14 (2018) or newer
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mmacosx-version-min=10.14"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -mmacosx-version-min=10.14"
|
||||
# For portability compile for macOS 10.13 (2017) or newer
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mmacosx-version-min=10.13"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -mmacosx-version-min=10.13"
|
||||
# -mmacosx-version-min must come first here.
|
||||
PLATFORM_SHARED_LDFLAGS="-mmacosx-version-min=10.14 $PLATFORM_SHARED_LDFLAGS"
|
||||
PLATFORM_CMAKE_FLAGS="-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14"
|
||||
JAVA_STATIC_DEPS_COMMON_FLAGS="-mmacosx-version-min=10.14"
|
||||
PLATFORM_SHARED_LDFLAGS="-mmacosx-version-min=10.13 $PLATFORM_SHARED_LDFLAGS"
|
||||
PLATFORM_CMAKE_FLAGS="-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13"
|
||||
JAVA_STATIC_DEPS_COMMON_FLAGS="-mmacosx-version-min=10.13"
|
||||
JAVA_STATIC_DEPS_LDFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
JAVA_STATIC_DEPS_CCFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
JAVA_STATIC_DEPS_CXXFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# The file is generated using update_dependencies.sh.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/62de5a92e5f23c661c3d4b9f322e04eb14e7a5bd/11.x/centos8-native/886b5eb
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/1f6edd1ff15c99c861afc8f3cd69054cd974dd64/15/platform010/72a2ff8
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/fed6e93d87571fb162734c86636119d45a398963/2.34/platform010/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/31a346126a1f3b64812c362511cb04cc1bd40855/1.1.8/platform010/76ebdda
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/0c65c05468b5a38cef1a106a1f526463e120c8dd/1.2.8/platform010/76ebdda
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/e40bde78650fa91b8405a857e3f10bf336633fb0/11.x/centos7-native/886b5eb
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/2043340983c032915adbb6f78903dc855b65aee8/12/platform010/9520e0f
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/c00dcc6a3e4125c7e8b248e9a79c14b78ac9e0ca/11.x/platform010/5684a5a
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/0b9c8e4b060eda62f3bc1c6127bbe1256697569b/2.34/platform010/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/bc9647f7912b131315827d65cb6189c21f381d05/1.1.3/platform010/76ebdda
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/a6f5f3f1d063d2d00cd02fc12f0f05fc3ab3a994/1.2.11/platform010/76ebdda
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/09703139cfc376bd8a82642385a0e97726b28287/1.0.6/platform010/76ebdda
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/ff23d17b932725cc1734a14896a8b67c518ba169/1.9.4/platform010/76ebdda
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/576397d8b1d9cea7306ad1e454d5e55caaa2ff1c/1.4.x/platform010/64091f4
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/fecac07861cb829f5e60dbeff0503d3272db73c0/2.2.0/platform010/76ebdda
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/0bb3f5756788ce26e2e16a1cb2f2af2c59b51abe/master/platform010/f57cc4a
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/5b602edd46fda54cdd7ea45f77dbe4061206e174/2.0.11/platform010/76ebdda
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/97cac22a149c2e202917e05d44e87e516b68216f/1.4/platform010/5074a48
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/53953ebc4e3eda85ad6fc3e429ba146035e97b90/2018_U5/platform010/76ebdda
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/60220d6a5bf7722b9cc239a1368c596619b12060/1.9.1/platform010/76ebdda
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/50eace8143eaaea9473deae1f3283e0049e05633/1.4.x/platform010/64091f4
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/5d27e5919771603da06000a027b12f799e58a4f7/2.2.0/platform010/76ebdda
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/b62912d333ef33f9760efa6219dbe3fe6abb3b0e/master/platform010/f57cc4a
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/6b412770957aa3c8a87e5e0dcd8cc2f45f393bc0/2.0.11/platform010/76ebdda
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/52f69816e936e147664ad717eb71a1a0e9dc973a/1.4/platform010/5074a48
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/c9cc192099fa84c0dcd0ffeedd44a373ad6e4925/2018_U5/platform010/76ebdda
|
||||
LIBURING_BASE=/mnt/gvfs/third-party2/liburing/a98e2d137007e3ebf7f33bd6f99c2c56bdaf8488/20210212/platform010/76ebdda
|
||||
BENCHMARK_BASE=/mnt/gvfs/third-party2/benchmark/780c7a0f9cf0967961e69ad08e61cddd85d61821/trunk/platform010/76ebdda
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/624a2f8f6c93c3c1df8aa4a6255d8202631a6c80/fb/platform010/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/39579e8603b48b3540f8b0633f43adf29acccb8b/2.37/centos8-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/cd9cc656d49ecb53797ce4d055e49fde29fd57ff/3.19.0/platform010/76ebdda
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/02d9f76aaaba580611cf75e741753c800c7fdc12/fb/platform010/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/938dc3f064ef3a48c0446f5b11d788d50b3eb5ee/2.37/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/429a6b3203eb415f1599bd15183659153129188e/3.15.0/platform010/76ebdda
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/363787fa5cac2a8aa20638909210443278fa138e/5.3.4/platform010/9079c97
|
||||
@@ -15,7 +15,7 @@ import re
|
||||
import sys
|
||||
|
||||
|
||||
class ErrorParserBase:
|
||||
class ErrorParserBase(object):
|
||||
def parse_error(self, line):
|
||||
"""Parses a line of test output. If it contains an error, returns a
|
||||
formatted message describing the error; otherwise, returns None.
|
||||
|
||||
@@ -137,11 +137,11 @@ then
|
||||
# should be relevant for formatting fixes.
|
||||
FORMAT_UPSTREAM_MERGE_BASE="$(git merge-base "$FORMAT_UPSTREAM" HEAD)"
|
||||
# Get the differences
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1)
|
||||
echo "Checking format of changes not yet in $FORMAT_UPSTREAM..."
|
||||
else
|
||||
# Check the format of uncommitted lines,
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1)
|
||||
echo "Checking format of uncommitted changes..."
|
||||
fi
|
||||
|
||||
@@ -149,9 +149,6 @@ if [ -z "$diffs" ]
|
||||
then
|
||||
echo "Nothing needs to be reformatted!"
|
||||
exit 0
|
||||
elif [ $? -ne 1 ]; then
|
||||
# CLANG_FORMAT_DIFF will exit on 1 while there is suggested changes.
|
||||
exit $?
|
||||
elif [ $CHECK_ONLY ]
|
||||
then
|
||||
echo "Your change has unformatted code. Please run make format!"
|
||||
|
||||
@@ -1561,7 +1561,7 @@ sub save_stdin_stdout_stderr {
|
||||
::die_bug("Can't dup STDERR: $!");
|
||||
open $Global::original_stdin, "<&", "STDIN" or
|
||||
::die_bug("Can't dup STDIN: $!");
|
||||
$Global::is_terminal = (-t $Global::original_stderr) && !$ENV{'CIRCLECI'}&& !$ENV{'GITHUB_ACTIONS'} && !$ENV{'TRAVIS'};
|
||||
$Global::is_terminal = (-t $Global::original_stderr) && !$ENV{'CIRCLECI'} && !$ENV{'TRAVIS'};
|
||||
}
|
||||
|
||||
sub enough_file_handles {
|
||||
|
||||
@@ -75,8 +75,8 @@ touch "$OUTPUT"
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos8-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/15/platform010/*/`
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/12/platform010/*/`
|
||||
|
||||
log_header
|
||||
log_variable GCC_BASE
|
||||
@@ -86,7 +86,7 @@ log_variable CLANG_BASE
|
||||
get_lib_base libgcc 11.x platform010
|
||||
get_lib_base glibc 2.34 platform010
|
||||
get_lib_base snappy LATEST platform010
|
||||
get_lib_base zlib 1.2.8 platform010
|
||||
get_lib_base zlib LATEST platform010
|
||||
get_lib_base bzip2 LATEST platform010
|
||||
get_lib_base lz4 LATEST platform010
|
||||
get_lib_base zstd LATEST platform010
|
||||
@@ -99,7 +99,7 @@ get_lib_base liburing LATEST platform010
|
||||
get_lib_base benchmark LATEST platform010
|
||||
|
||||
get_lib_base kernel-headers fb platform010
|
||||
get_lib_base binutils LATEST centos8-native
|
||||
get_lib_base binutils LATEST centos7-native
|
||||
get_lib_base valgrind LATEST platform010
|
||||
get_lib_base lua 5.3.4 platform010
|
||||
|
||||
|
||||
Vendored
-35
@@ -66,41 +66,6 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionTypeFlags::kMutable}},
|
||||
};
|
||||
|
||||
namespace {
|
||||
static void NoopDelete(Cache::ObjectPtr /*obj*/,
|
||||
MemoryAllocator* /*allocator*/) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
static size_t SliceSize(Cache::ObjectPtr obj) {
|
||||
return static_cast<Slice*>(obj)->size();
|
||||
}
|
||||
|
||||
static Status SliceSaveTo(Cache::ObjectPtr from_obj, size_t from_offset,
|
||||
size_t length, char* out) {
|
||||
const Slice& slice = *static_cast<Slice*>(from_obj);
|
||||
std::memcpy(out, slice.data() + from_offset, length);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static Status NoopCreate(const Slice& /*data*/, CompressionType /*type*/,
|
||||
CacheTier /*source*/, Cache::CreateContext* /*ctx*/,
|
||||
MemoryAllocator* /*allocator*/,
|
||||
Cache::ObjectPtr* /*out_obj*/,
|
||||
size_t* /*out_charge*/) {
|
||||
assert(false);
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
static Cache::CacheItemHelper kBasicCacheItemHelper(CacheEntryRole::kMisc,
|
||||
&NoopDelete);
|
||||
} // namespace
|
||||
|
||||
const Cache::CacheItemHelper kSliceCacheItemHelper{
|
||||
CacheEntryRole::kMisc, &NoopDelete, &SliceSize,
|
||||
&SliceSaveTo, &NoopCreate, &kBasicCacheItemHelper,
|
||||
};
|
||||
|
||||
Status SecondaryCache::CreateFromString(
|
||||
const ConfigOptions& config_options, const std::string& value,
|
||||
std::shared_ptr<SecondaryCache>* result) {
|
||||
|
||||
Vendored
+109
-317
@@ -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 "cache_key.h"
|
||||
#ifdef GFLAGS
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
@@ -12,12 +13,9 @@
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
#include "cache/cache_key.h"
|
||||
#include "cache/sharded_cache.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "monitoring/histogram.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -33,7 +31,6 @@
|
||||
#include "util/hash.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/random.h"
|
||||
#include "util/stderr_logger.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
@@ -46,43 +43,21 @@ static constexpr uint64_t GiB = MiB << 10;
|
||||
DEFINE_uint32(threads, 16, "Number of concurrent threads to run.");
|
||||
DEFINE_uint64(cache_size, 1 * GiB,
|
||||
"Number of bytes to use as a cache of uncompressed data.");
|
||||
DEFINE_int32(num_shard_bits, -1,
|
||||
"ShardedCacheOptions::shard_bits. Default = auto");
|
||||
DEFINE_int32(
|
||||
eviction_effort_cap,
|
||||
ROCKSDB_NAMESPACE::HyperClockCacheOptions(1, 1).eviction_effort_cap,
|
||||
"HyperClockCacheOptions::eviction_effort_cap");
|
||||
DEFINE_uint32(num_shard_bits, 6, "shard_bits.");
|
||||
|
||||
DEFINE_double(resident_ratio, 0.25,
|
||||
"Ratio of keys fitting in cache to keyspace.");
|
||||
DEFINE_uint64(ops_per_thread, 2000000U, "Number of operations per thread.");
|
||||
DEFINE_uint32(value_bytes, 8 * KiB, "Size of each value added.");
|
||||
DEFINE_uint32(value_bytes_estimate, 0,
|
||||
"If > 0, overrides estimated_entry_charge or "
|
||||
"min_avg_entry_charge depending on cache_type.");
|
||||
|
||||
DEFINE_int32(
|
||||
degenerate_hash_bits, 0,
|
||||
"With HCC, fix this many hash bits to increase table hash collisions");
|
||||
DEFINE_uint32(skew, 5, "Degree of skew in key selection. 0 = no skew");
|
||||
DEFINE_uint32(skew, 5, "Degree of skew in key selection");
|
||||
DEFINE_bool(populate_cache, true, "Populate cache before operations");
|
||||
|
||||
DEFINE_double(pinned_ratio, 0.25,
|
||||
"Keep roughly this portion of entries pinned in cache.");
|
||||
DEFINE_double(
|
||||
vary_capacity_ratio, 0.0,
|
||||
"If greater than 0.0, will periodically vary the capacity between this "
|
||||
"ratio less than full size and full size. If vary_capacity_ratio + "
|
||||
"pinned_ratio is close to or exceeds 1.0, the cache might thrash.");
|
||||
|
||||
DEFINE_uint32(lookup_insert_percent, 82,
|
||||
DEFINE_uint32(lookup_insert_percent, 87,
|
||||
"Ratio of lookup (+ insert on not found) to total workload "
|
||||
"(expressed as a percentage)");
|
||||
DEFINE_uint32(insert_percent, 2,
|
||||
"Ratio of insert to total workload (expressed as a percentage)");
|
||||
DEFINE_uint32(blind_insert_percent, 5,
|
||||
"Ratio of insert without keeping handle to total workload "
|
||||
"(expressed as a percentage)");
|
||||
DEFINE_uint32(lookup_percent, 10,
|
||||
"Ratio of lookup to total workload (expressed as a percentage)");
|
||||
DEFINE_uint32(erase_percent, 1,
|
||||
@@ -96,40 +71,18 @@ DEFINE_uint32(
|
||||
|
||||
DEFINE_uint32(gather_stats_entries_per_lock, 256,
|
||||
"For Cache::ApplyToAllEntries");
|
||||
|
||||
DEFINE_uint32(usleep, 0, "Sleep up to this many microseconds after each op.");
|
||||
DEFINE_bool(skewed, false, "If true, skew the key access distribution");
|
||||
|
||||
DEFINE_bool(lean, false,
|
||||
"If true, no additional computation is performed besides cache "
|
||||
"operations.");
|
||||
|
||||
DEFINE_bool(early_exit, false,
|
||||
"Exit before deallocating most memory. Good for malloc stats, e.g."
|
||||
"MALLOC_CONF=\"stats_print:true\"");
|
||||
|
||||
DEFINE_bool(histograms, true,
|
||||
"Whether to track and print histogram statistics.");
|
||||
|
||||
DEFINE_bool(report_problems, true, "Whether to ReportProblems() at the end.");
|
||||
|
||||
DEFINE_uint32(seed, 0, "Hashing/random seed to use. 0 = choose at random");
|
||||
|
||||
DEFINE_string(secondary_cache_uri, "",
|
||||
"Full URI for creating a custom secondary cache object");
|
||||
static class std::shared_ptr<ROCKSDB_NAMESPACE::SecondaryCache> secondary_cache;
|
||||
|
||||
DEFINE_string(cache_type, "lru_cache", "Type of block cache.");
|
||||
|
||||
DEFINE_bool(use_jemalloc_no_dump_allocator, false,
|
||||
"Whether to use JemallocNoDumpAllocator");
|
||||
|
||||
DEFINE_uint32(jemalloc_no_dump_allocator_num_arenas,
|
||||
ROCKSDB_NAMESPACE::JemallocAllocatorOptions().num_arenas,
|
||||
"JemallocNodumpAllocator::num_arenas");
|
||||
|
||||
DEFINE_bool(jemalloc_no_dump_allocator_limit_tcache_size,
|
||||
ROCKSDB_NAMESPACE::JemallocAllocatorOptions().limit_tcache_size,
|
||||
"JemallocNodumpAllocator::limit_tcache_size");
|
||||
|
||||
// ## BEGIN stress_cache_key sub-tool options ##
|
||||
// See class StressCacheKey below.
|
||||
DEFINE_bool(stress_cache_key, false,
|
||||
@@ -192,6 +145,9 @@ class SharedState {
|
||||
public:
|
||||
explicit SharedState(CacheBench* cache_bench)
|
||||
: cv_(&mu_),
|
||||
num_initialized_(0),
|
||||
start_(false),
|
||||
num_done_(0),
|
||||
cache_bench_(cache_bench) {}
|
||||
|
||||
~SharedState() {}
|
||||
@@ -214,31 +170,15 @@ class SharedState {
|
||||
|
||||
bool Started() const { return start_; }
|
||||
|
||||
void AddLookupStats(uint64_t hits, uint64_t misses, size_t pinned_count) {
|
||||
MutexLock l(&mu_);
|
||||
lookup_count_ += hits + misses;
|
||||
lookup_hits_ += hits;
|
||||
pinned_count_ += pinned_count;
|
||||
}
|
||||
|
||||
double GetLookupHitRatio() const {
|
||||
return 1.0 * lookup_hits_ / lookup_count_;
|
||||
}
|
||||
|
||||
size_t GetPinnedCount() const { return pinned_count_; }
|
||||
|
||||
private:
|
||||
port::Mutex mu_;
|
||||
port::CondVar cv_;
|
||||
|
||||
CacheBench* cache_bench_;
|
||||
uint64_t num_initialized_;
|
||||
bool start_;
|
||||
uint64_t num_done_;
|
||||
|
||||
uint64_t num_initialized_ = 0;
|
||||
bool start_ = false;
|
||||
uint64_t num_done_ = 0;
|
||||
uint64_t lookup_count_ = 0;
|
||||
uint64_t lookup_hits_ = 0;
|
||||
size_t pinned_count_ = 0;
|
||||
CacheBench* cache_bench_;
|
||||
};
|
||||
|
||||
// Per-thread state for concurrent executions of the same benchmark.
|
||||
@@ -250,32 +190,26 @@ struct ThreadState {
|
||||
uint64_t duration_us = 0;
|
||||
|
||||
ThreadState(uint32_t index, SharedState* _shared)
|
||||
: tid(index), rnd(FLAGS_seed + 1 + index), shared(_shared) {}
|
||||
: tid(index), rnd(1000 + index), shared(_shared) {}
|
||||
};
|
||||
|
||||
struct KeyGen {
|
||||
char key_data[27];
|
||||
|
||||
Slice GetRand(Random64& rnd, uint64_t max_key, uint32_t skew) {
|
||||
uint64_t raw = rnd.Next();
|
||||
// Skew according to setting
|
||||
for (uint32_t i = 0; i < skew; ++i) {
|
||||
raw = std::min(raw, rnd.Next());
|
||||
}
|
||||
uint64_t key = FastRange64(raw, max_key);
|
||||
if (FLAGS_degenerate_hash_bits) {
|
||||
uint64_t key_hash =
|
||||
Hash64(reinterpret_cast<const char*>(&key), sizeof(key));
|
||||
// HCC uses the high 64 bits and a lower bit mask for starting probe
|
||||
// location, so we fix hash bits starting at the bottom of that word.
|
||||
auto hi_hash = uint64_t{0x9e3779b97f4a7c13U} ^
|
||||
(key_hash << 1 << (FLAGS_degenerate_hash_bits - 1));
|
||||
uint64_t un_hi, un_lo;
|
||||
BijectiveUnhash2x64(hi_hash, key_hash, &un_hi, &un_lo);
|
||||
un_lo ^= BitwiseAnd(FLAGS_seed, INT32_MAX);
|
||||
EncodeFixed64(key_data, un_lo);
|
||||
EncodeFixed64(key_data + 8, un_hi);
|
||||
return Slice(key_data, kCacheKeySize);
|
||||
Slice GetRand(Random64& rnd, uint64_t max_key, int max_log) {
|
||||
uint64_t key = 0;
|
||||
if (!FLAGS_skewed) {
|
||||
uint64_t raw = rnd.Next();
|
||||
// Skew according to setting
|
||||
for (uint32_t i = 0; i < FLAGS_skew; ++i) {
|
||||
raw = std::min(raw, rnd.Next());
|
||||
}
|
||||
key = FastRange64(raw, max_key);
|
||||
} else {
|
||||
key = rnd.Skewed(max_log);
|
||||
if (key > max_key) {
|
||||
key -= max_key;
|
||||
}
|
||||
}
|
||||
// Variable size and alignment
|
||||
size_t off = key % 8;
|
||||
@@ -290,8 +224,8 @@ struct KeyGen {
|
||||
}
|
||||
};
|
||||
|
||||
Cache::ObjectPtr createValue(Random64& rnd, MemoryAllocator* alloc) {
|
||||
char* rv = AllocateBlock(FLAGS_value_bytes, alloc).release();
|
||||
Cache::ObjectPtr createValue(Random64& rnd) {
|
||||
char* rv = new char[FLAGS_value_bytes];
|
||||
// Fill with some filler data, and take some CPU time
|
||||
for (uint32_t i = 0; i < FLAGS_value_bytes; i += 8) {
|
||||
EncodeFixed64(rv + i, rnd.Next());
|
||||
@@ -308,8 +242,7 @@ Status SaveToFn(Cache::ObjectPtr from_obj, size_t /*from_offset*/,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CreateFn(const Slice& data, CompressionType /*type*/,
|
||||
CacheTier /*source*/, Cache::CreateContext* /*context*/,
|
||||
Status CreateFn(const Slice& data, Cache::CreateContext* /*context*/,
|
||||
MemoryAllocator* /*allocator*/, Cache::ObjectPtr* out_obj,
|
||||
size_t* out_charge) {
|
||||
*out_obj = new char[data.size()];
|
||||
@@ -318,8 +251,8 @@ Status CreateFn(const Slice& data, CompressionType /*type*/,
|
||||
return Status::OK();
|
||||
};
|
||||
|
||||
void DeleteFn(Cache::ObjectPtr value, MemoryAllocator* alloc) {
|
||||
CustomDeleter{alloc}(static_cast<char*>(value));
|
||||
void DeleteFn(Cache::ObjectPtr value, MemoryAllocator* /*alloc*/) {
|
||||
delete[] static_cast<char*>(value);
|
||||
}
|
||||
|
||||
Cache::CacheItemHelper helper1_wos(CacheEntryRole::kDataBlock, DeleteFn);
|
||||
@@ -331,28 +264,6 @@ Cache::CacheItemHelper helper2(CacheEntryRole::kIndexBlock, DeleteFn, SizeFn,
|
||||
Cache::CacheItemHelper helper3_wos(CacheEntryRole::kFilterBlock, DeleteFn);
|
||||
Cache::CacheItemHelper helper3(CacheEntryRole::kFilterBlock, DeleteFn, SizeFn,
|
||||
SaveToFn, CreateFn, &helper3_wos);
|
||||
|
||||
void ConfigureSecondaryCache(ShardedCacheOptions& opts) {
|
||||
if (!FLAGS_secondary_cache_uri.empty()) {
|
||||
std::shared_ptr<SecondaryCache> secondary_cache;
|
||||
Status s = SecondaryCache::CreateFromString(
|
||||
ConfigOptions(), FLAGS_secondary_cache_uri, &secondary_cache);
|
||||
if (secondary_cache == nullptr) {
|
||||
fprintf(stderr,
|
||||
"No secondary cache registered matching string: %s status=%s\n",
|
||||
FLAGS_secondary_cache_uri.c_str(), s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
opts.secondary_cache = secondary_cache;
|
||||
}
|
||||
}
|
||||
|
||||
ShardedCacheBase* AsShardedCache(Cache* c) {
|
||||
if (!FLAGS_secondary_cache_uri.empty()) {
|
||||
c = static_cast_with_check<CacheWrapper>(c)->GetTarget().get();
|
||||
}
|
||||
return static_cast_with_check<ShardedCacheBase>(c);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class CacheBench {
|
||||
@@ -367,60 +278,50 @@ class CacheBench {
|
||||
FLAGS_lookup_insert_percent),
|
||||
insert_threshold_(lookup_insert_threshold_ +
|
||||
kHundredthUint64 * FLAGS_insert_percent),
|
||||
blind_insert_threshold_(insert_threshold_ +
|
||||
kHundredthUint64 * FLAGS_blind_insert_percent),
|
||||
lookup_threshold_(blind_insert_threshold_ +
|
||||
lookup_threshold_(insert_threshold_ +
|
||||
kHundredthUint64 * FLAGS_lookup_percent),
|
||||
erase_threshold_(lookup_threshold_ +
|
||||
kHundredthUint64 * FLAGS_erase_percent) {
|
||||
kHundredthUint64 * FLAGS_erase_percent),
|
||||
skewed_(FLAGS_skewed) {
|
||||
if (erase_threshold_ != 100U * kHundredthUint64) {
|
||||
fprintf(stderr, "Percentages must add to 100.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::shared_ptr<MemoryAllocator> allocator;
|
||||
if (FLAGS_use_jemalloc_no_dump_allocator) {
|
||||
JemallocAllocatorOptions opts;
|
||||
opts.num_arenas = FLAGS_jemalloc_no_dump_allocator_num_arenas;
|
||||
opts.limit_tcache_size =
|
||||
FLAGS_jemalloc_no_dump_allocator_limit_tcache_size;
|
||||
Status s = NewJemallocNodumpAllocator(opts, &allocator);
|
||||
assert(s.ok());
|
||||
max_log_ = 0;
|
||||
if (skewed_) {
|
||||
uint64_t max_key = max_key_;
|
||||
while (max_key >>= 1) max_log_++;
|
||||
if (max_key > (static_cast<uint64_t>(1) << max_log_)) max_log_++;
|
||||
}
|
||||
|
||||
if (FLAGS_cache_type == "clock_cache") {
|
||||
fprintf(stderr, "Old clock cache implementation has been removed.\n");
|
||||
exit(1);
|
||||
} else if (EndsWith(FLAGS_cache_type, "hyper_clock_cache")) {
|
||||
HyperClockCacheOptions opts(
|
||||
FLAGS_cache_size, /*estimated_entry_charge=*/0, FLAGS_num_shard_bits);
|
||||
opts.hash_seed = BitwiseAnd(FLAGS_seed, INT32_MAX);
|
||||
opts.memory_allocator = allocator;
|
||||
opts.eviction_effort_cap = FLAGS_eviction_effort_cap;
|
||||
if (FLAGS_cache_type == "fixed_hyper_clock_cache" ||
|
||||
FLAGS_cache_type == "hyper_clock_cache") {
|
||||
opts.estimated_entry_charge = FLAGS_value_bytes_estimate > 0
|
||||
? FLAGS_value_bytes_estimate
|
||||
: FLAGS_value_bytes;
|
||||
} else if (FLAGS_cache_type == "auto_hyper_clock_cache") {
|
||||
if (FLAGS_value_bytes_estimate > 0) {
|
||||
opts.min_avg_entry_charge = FLAGS_value_bytes_estimate;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Cache type not supported.\n");
|
||||
exit(1);
|
||||
}
|
||||
ConfigureSecondaryCache(opts);
|
||||
cache_ = opts.MakeSharedCache();
|
||||
} else if (FLAGS_cache_type == "hyper_clock_cache") {
|
||||
cache_ = HyperClockCacheOptions(FLAGS_cache_size, FLAGS_value_bytes,
|
||||
FLAGS_num_shard_bits)
|
||||
.MakeSharedCache();
|
||||
} else if (FLAGS_cache_type == "lru_cache") {
|
||||
LRUCacheOptions opts(FLAGS_cache_size, FLAGS_num_shard_bits,
|
||||
false /* strict_capacity_limit */,
|
||||
0.5 /* high_pri_pool_ratio */);
|
||||
opts.hash_seed = BitwiseAnd(FLAGS_seed, INT32_MAX);
|
||||
opts.memory_allocator = allocator;
|
||||
ConfigureSecondaryCache(opts);
|
||||
if (!FLAGS_secondary_cache_uri.empty()) {
|
||||
Status s = SecondaryCache::CreateFromString(
|
||||
ConfigOptions(), FLAGS_secondary_cache_uri, &secondary_cache);
|
||||
if (secondary_cache == nullptr) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"No secondary cache registered matching string: %s status=%s\n",
|
||||
FLAGS_secondary_cache_uri.c_str(), s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
opts.secondary_cache = secondary_cache;
|
||||
}
|
||||
|
||||
cache_ = NewLRUCache(opts);
|
||||
} else {
|
||||
fprintf(stderr, "Cache type not supported.\n");
|
||||
fprintf(stderr, "Cache type not supported.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -428,51 +329,13 @@ class CacheBench {
|
||||
~CacheBench() {}
|
||||
|
||||
void PopulateCache() {
|
||||
Random64 rnd(FLAGS_seed);
|
||||
Random64 rnd(1);
|
||||
KeyGen keygen;
|
||||
size_t max_occ = 0;
|
||||
size_t inserts_since_max_occ_increase = 0;
|
||||
size_t keys_since_last_not_found = 0;
|
||||
|
||||
// Avoid redundant insertions by checking Lookup before Insert.
|
||||
// Loop until insertions consistently fail to increase max occupancy or
|
||||
// it becomes difficult to find keys not already inserted.
|
||||
while (inserts_since_max_occ_increase < 100 &&
|
||||
keys_since_last_not_found < 100) {
|
||||
Slice key = keygen.GetRand(rnd, max_key_, FLAGS_skew);
|
||||
|
||||
Cache::Handle* handle = cache_->Lookup(key);
|
||||
if (handle != nullptr) {
|
||||
cache_->Release(handle);
|
||||
++keys_since_last_not_found;
|
||||
continue;
|
||||
}
|
||||
keys_since_last_not_found = 0;
|
||||
|
||||
Status s =
|
||||
cache_->Insert(key, createValue(rnd, cache_->memory_allocator()),
|
||||
&helper1, FLAGS_value_bytes);
|
||||
for (uint64_t i = 0; i < 2 * FLAGS_cache_size; i += FLAGS_value_bytes) {
|
||||
Status s = cache_->Insert(keygen.GetRand(rnd, max_key_, max_log_),
|
||||
createValue(rnd), &helper1, FLAGS_value_bytes);
|
||||
assert(s.ok());
|
||||
|
||||
handle = cache_->Lookup(key);
|
||||
if (!handle) {
|
||||
fprintf(stderr, "Failed to lookup key just inserted.\n");
|
||||
assert(false);
|
||||
exit(42);
|
||||
} else {
|
||||
cache_->Release(handle);
|
||||
}
|
||||
|
||||
size_t occ = cache_->GetOccupancyCount();
|
||||
if (occ > max_occ) {
|
||||
max_occ = occ;
|
||||
inserts_since_max_occ_increase = 0;
|
||||
} else {
|
||||
++inserts_since_max_occ_increase;
|
||||
}
|
||||
}
|
||||
printf("Population complete (%zu entries, %g average charge)\n", max_occ,
|
||||
1.0 * FLAGS_cache_size / max_occ);
|
||||
}
|
||||
|
||||
bool Run() {
|
||||
@@ -531,35 +394,19 @@ class CacheBench {
|
||||
FLAGS_ops_per_thread / elapsed_secs);
|
||||
printf("Thread ops/sec = %u\n", ops_per_sec);
|
||||
|
||||
printf("Lookup hit ratio: %g\n", shared.GetLookupHitRatio());
|
||||
printf("\nOperation latency (ns):\n");
|
||||
HistogramImpl combined;
|
||||
for (uint32_t i = 0; i < FLAGS_threads; i++) {
|
||||
combined.Merge(threads[i]->latency_ns_hist);
|
||||
}
|
||||
printf("%s", combined.ToString().c_str());
|
||||
|
||||
size_t occ = cache_->GetOccupancyCount();
|
||||
size_t slot = cache_->GetTableAddressCount();
|
||||
printf("Final load factor: %g (%zu / %zu)\n", 1.0 * occ / slot, occ, slot);
|
||||
|
||||
printf("Final pinned count: %zu\n", shared.GetPinnedCount());
|
||||
|
||||
if (FLAGS_histograms) {
|
||||
printf("\nOperation latency (ns):\n");
|
||||
HistogramImpl combined;
|
||||
for (uint32_t i = 0; i < FLAGS_threads; i++) {
|
||||
combined.Merge(threads[i]->latency_ns_hist);
|
||||
}
|
||||
printf("%s", combined.ToString().c_str());
|
||||
|
||||
if (FLAGS_gather_stats) {
|
||||
printf("\nGather stats latency (us):\n");
|
||||
printf("%s", stats_hist.ToString().c_str());
|
||||
}
|
||||
if (FLAGS_gather_stats) {
|
||||
printf("\nGather stats latency (us):\n");
|
||||
printf("%s", stats_hist.ToString().c_str());
|
||||
}
|
||||
|
||||
if (FLAGS_report_problems) {
|
||||
printf("\n");
|
||||
std::shared_ptr<Logger> logger =
|
||||
std::make_shared<StderrLogger>(InfoLogLevel::DEBUG_LEVEL);
|
||||
cache_->ReportProblems(logger);
|
||||
}
|
||||
printf("%s", stats_report.c_str());
|
||||
printf("\n%s", stats_report.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -570,9 +417,10 @@ class CacheBench {
|
||||
// Cumulative thresholds in the space of a random uint64_t
|
||||
const uint64_t lookup_insert_threshold_;
|
||||
const uint64_t insert_threshold_;
|
||||
const uint64_t blind_insert_threshold_;
|
||||
const uint64_t lookup_threshold_;
|
||||
const uint64_t erase_threshold_;
|
||||
const bool skewed_;
|
||||
int max_log_;
|
||||
|
||||
// A benchmark version of gathering stats on an active block cache by
|
||||
// iterating over it. The primary purpose is to measure the impact of
|
||||
@@ -605,7 +453,7 @@ class CacheBench {
|
||||
for (;;) {
|
||||
if (shared->AllDone()) {
|
||||
std::ostringstream ostr;
|
||||
ostr << "\nMost recent cache entry stats:\n"
|
||||
ostr << "Most recent cache entry stats:\n"
|
||||
<< "Number of entries: " << total_entry_count << "\n"
|
||||
<< "Table occupancy: " << table_occupancy << " / "
|
||||
<< table_size << " = "
|
||||
@@ -642,17 +490,13 @@ class CacheBench {
|
||||
// Something slightly more expensive as in stats by category
|
||||
helpers.insert(helper);
|
||||
};
|
||||
if (FLAGS_histograms) {
|
||||
timer.Start();
|
||||
}
|
||||
timer.Start();
|
||||
Cache::ApplyToAllEntriesOptions opts;
|
||||
opts.average_entries_per_lock = FLAGS_gather_stats_entries_per_lock;
|
||||
shared->GetCacheBench()->cache_->ApplyToAllEntries(fn, opts);
|
||||
table_occupancy = shared->GetCacheBench()->cache_->GetOccupancyCount();
|
||||
table_size = shared->GetCacheBench()->cache_->GetTableAddressCount();
|
||||
if (FLAGS_histograms) {
|
||||
stats_hist->Add(timer.ElapsedNanos() / 1000);
|
||||
}
|
||||
stats_hist->Add(timer.ElapsedNanos() / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,89 +527,62 @@ class CacheBench {
|
||||
void OperateCache(ThreadState* thread) {
|
||||
// To use looked-up values
|
||||
uint64_t result = 0;
|
||||
uint64_t lookup_misses = 0;
|
||||
uint64_t lookup_hits = 0;
|
||||
// To hold handles for a non-trivial amount of time
|
||||
std::deque<Cache::Handle*> pinned;
|
||||
size_t total_pin_count = static_cast<size_t>(
|
||||
(FLAGS_cache_size * FLAGS_pinned_ratio) / FLAGS_value_bytes + 0.999999);
|
||||
// For this thread. Some round up, some round down, as appropriate
|
||||
size_t pin_count = (total_pin_count + thread->tid) / FLAGS_threads;
|
||||
|
||||
Cache::Handle* handle = nullptr;
|
||||
KeyGen gen;
|
||||
const auto clock = SystemClock::Default().get();
|
||||
uint64_t start_time = clock->NowMicros();
|
||||
StopWatchNano timer(clock);
|
||||
auto system_clock = SystemClock::Default();
|
||||
size_t steps_to_next_capacity_change = 0;
|
||||
|
||||
for (uint64_t i = 0; i < FLAGS_ops_per_thread; i++) {
|
||||
Slice key = gen.GetRand(thread->rnd, max_key_, FLAGS_skew);
|
||||
Slice key = gen.GetRand(thread->rnd, max_key_, max_log_);
|
||||
uint64_t random_op = thread->rnd.Next();
|
||||
|
||||
if (FLAGS_vary_capacity_ratio > 0.0 && thread->tid == 0) {
|
||||
if (steps_to_next_capacity_change == 0) {
|
||||
double cut_ratio = static_cast<double>(thread->rnd.Next()) /
|
||||
static_cast<double>(UINT64_MAX) *
|
||||
FLAGS_vary_capacity_ratio;
|
||||
cache_->SetCapacity(FLAGS_cache_size * (1.0 - cut_ratio));
|
||||
steps_to_next_capacity_change =
|
||||
static_cast<size_t>(FLAGS_ops_per_thread / 100);
|
||||
} else {
|
||||
--steps_to_next_capacity_change;
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAGS_histograms) {
|
||||
timer.Start();
|
||||
}
|
||||
timer.Start();
|
||||
|
||||
if (random_op < lookup_insert_threshold_) {
|
||||
// do lookup
|
||||
auto handle = cache_->Lookup(key, &helper2, /*context*/ nullptr,
|
||||
Cache::Priority::LOW);
|
||||
if (handle) {
|
||||
++lookup_hits;
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
// do lookup
|
||||
handle = cache_->Lookup(key, &helper2, /*context*/ nullptr,
|
||||
Cache::Priority::LOW);
|
||||
if (handle) {
|
||||
if (!FLAGS_lean) {
|
||||
// do something with the data
|
||||
result += NPHash64(static_cast<char*>(cache_->Value(handle)),
|
||||
FLAGS_value_bytes);
|
||||
}
|
||||
pinned.push_back(handle);
|
||||
} else {
|
||||
++lookup_misses;
|
||||
// do insert
|
||||
Status s = cache_->Insert(
|
||||
key, createValue(thread->rnd, cache_->memory_allocator()),
|
||||
&helper2, FLAGS_value_bytes, &pinned.emplace_back());
|
||||
Status s = cache_->Insert(key, createValue(thread->rnd), &helper2,
|
||||
FLAGS_value_bytes, &handle);
|
||||
assert(s.ok());
|
||||
}
|
||||
} else if (random_op < insert_threshold_) {
|
||||
if (handle) {
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
// do insert
|
||||
Status s = cache_->Insert(
|
||||
key, createValue(thread->rnd, cache_->memory_allocator()), &helper3,
|
||||
FLAGS_value_bytes, &pinned.emplace_back());
|
||||
assert(s.ok());
|
||||
} else if (random_op < blind_insert_threshold_) {
|
||||
// insert without keeping a handle
|
||||
Status s = cache_->Insert(
|
||||
key, createValue(thread->rnd, cache_->memory_allocator()), &helper3,
|
||||
FLAGS_value_bytes);
|
||||
Status s = cache_->Insert(key, createValue(thread->rnd), &helper3,
|
||||
FLAGS_value_bytes, &handle);
|
||||
assert(s.ok());
|
||||
} else if (random_op < lookup_threshold_) {
|
||||
// do lookup
|
||||
auto handle = cache_->Lookup(key, &helper2, /*context*/ nullptr,
|
||||
Cache::Priority::LOW);
|
||||
if (handle) {
|
||||
++lookup_hits;
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
// do lookup
|
||||
handle = cache_->Lookup(key, &helper2, /*context*/ nullptr,
|
||||
Cache::Priority::LOW);
|
||||
if (handle) {
|
||||
if (!FLAGS_lean) {
|
||||
// do something with the data
|
||||
result += NPHash64(static_cast<char*>(cache_->Value(handle)),
|
||||
FLAGS_value_bytes);
|
||||
}
|
||||
pinned.push_back(handle);
|
||||
} else {
|
||||
++lookup_misses;
|
||||
}
|
||||
} else if (random_op < erase_threshold_) {
|
||||
// do erase
|
||||
@@ -774,27 +591,9 @@ class CacheBench {
|
||||
// Should be extremely unlikely (noop)
|
||||
assert(random_op >= kHundredthUint64 * 100U);
|
||||
}
|
||||
if (FLAGS_histograms) {
|
||||
thread->latency_ns_hist.Add(timer.ElapsedNanos());
|
||||
}
|
||||
if (FLAGS_usleep > 0) {
|
||||
unsigned us =
|
||||
static_cast<unsigned>(thread->rnd.Uniform(FLAGS_usleep + 1));
|
||||
if (us > 0) {
|
||||
system_clock->SleepForMicroseconds(us);
|
||||
}
|
||||
}
|
||||
while (pinned.size() > pin_count) {
|
||||
cache_->Release(pinned.front());
|
||||
pinned.pop_front();
|
||||
}
|
||||
thread->latency_ns_hist.Add(timer.ElapsedNanos());
|
||||
}
|
||||
if (FLAGS_early_exit) {
|
||||
MutexLock l(thread->shared->GetMutex());
|
||||
exit(0);
|
||||
}
|
||||
thread->shared->AddLookupStats(lookup_hits, lookup_misses, pinned.size());
|
||||
for (auto handle : pinned) {
|
||||
if (handle) {
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
@@ -814,16 +613,13 @@ class CacheBench {
|
||||
#ifndef NDEBUG
|
||||
printf("WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
|
||||
#endif
|
||||
printf("----------------------------\n");
|
||||
printf("RocksDB version : %d.%d\n", kMajorVersion, kMinorVersion);
|
||||
printf("Cache impl name : %s\n", cache_->Name());
|
||||
printf("DMutex impl name : %s\n", DMutex::kName());
|
||||
printf("Number of threads : %u\n", FLAGS_threads);
|
||||
printf("Ops per thread : %" PRIu64 "\n", FLAGS_ops_per_thread);
|
||||
printf("Cache size : %s\n",
|
||||
BytesToHumanString(FLAGS_cache_size).c_str());
|
||||
printf("Num shard bits : %d\n",
|
||||
AsShardedCache(cache_.get())->GetNumShardBits());
|
||||
printf("Num shard bits : %u\n", FLAGS_num_shard_bits);
|
||||
printf("Max key : %" PRIu64 "\n", max_key_);
|
||||
printf("Resident ratio : %g\n", FLAGS_resident_ratio);
|
||||
printf("Skew degree : %u\n", FLAGS_skew);
|
||||
@@ -1143,7 +939,6 @@ class StressCacheKey {
|
||||
};
|
||||
|
||||
int cache_bench_tool(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
if (FLAGS_stress_cache_key) {
|
||||
@@ -1157,14 +952,11 @@ int cache_bench_tool(int argc, char** argv) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (FLAGS_seed == 0) {
|
||||
FLAGS_seed = static_cast<uint32_t>(port::GetProcessID());
|
||||
printf("Using seed = %" PRIu32 "\n", FLAGS_seed);
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::CacheBench bench;
|
||||
if (FLAGS_populate_cache) {
|
||||
bench.PopulateCache();
|
||||
printf("Population complete\n");
|
||||
printf("----------------------------\n");
|
||||
}
|
||||
if (bench.Run()) {
|
||||
return 0;
|
||||
|
||||
Vendored
+1
-2
@@ -25,8 +25,7 @@ Status WarmInCache(Cache* cache, const Slice& key, const Slice& saved,
|
||||
assert(helper->create_cb);
|
||||
Cache::ObjectPtr value;
|
||||
size_t charge;
|
||||
Status st = helper->create_cb(saved, CompressionType::kNoCompression,
|
||||
CacheTier::kVolatileTier, create_context,
|
||||
Status st = helper->create_cb(saved, create_context,
|
||||
cache->memory_allocator(), &value, &charge);
|
||||
if (st.ok()) {
|
||||
st =
|
||||
|
||||
Vendored
+3
-4
@@ -273,10 +273,9 @@ class ConcurrentCacheReservationManager
|
||||
std::size_t total_mem_used = cache_res_mgr_->GetTotalMemoryUsed();
|
||||
Status s;
|
||||
if (!increase) {
|
||||
s = cache_res_mgr_->UpdateCacheReservation(
|
||||
(total_mem_used > memory_used_delta)
|
||||
? (total_mem_used - memory_used_delta)
|
||||
: 0);
|
||||
assert(total_mem_used >= memory_used_delta);
|
||||
s = cache_res_mgr_->UpdateCacheReservation(total_mem_used -
|
||||
memory_used_delta);
|
||||
} else {
|
||||
s = cache_res_mgr_->UpdateCacheReservation(total_mem_used +
|
||||
memory_used_delta);
|
||||
|
||||
Vendored
+95
-58
@@ -70,11 +70,18 @@ const Cache::CacheItemHelper kDumbHelper{
|
||||
CacheEntryRole::kMisc,
|
||||
[](Cache::ObjectPtr /*value*/, MemoryAllocator* /*alloc*/) {}};
|
||||
|
||||
const Cache::CacheItemHelper kInvokeOnDeleteHelper{
|
||||
const Cache::CacheItemHelper kEraseOnDeleteHelper1{
|
||||
CacheEntryRole::kMisc,
|
||||
[](Cache::ObjectPtr value, MemoryAllocator* /*alloc*/) {
|
||||
auto& fn = *static_cast<std::function<void()>*>(value);
|
||||
fn();
|
||||
Cache* cache = static_cast<Cache*>(value);
|
||||
cache->Erase("foo");
|
||||
}};
|
||||
|
||||
const Cache::CacheItemHelper kEraseOnDeleteHelper2{
|
||||
CacheEntryRole::kMisc,
|
||||
[](Cache::ObjectPtr value, MemoryAllocator* /*alloc*/) {
|
||||
Cache* cache = static_cast<Cache*>(value);
|
||||
cache->Erase(EncodeKey16Bytes(1234));
|
||||
}};
|
||||
} // anonymous namespace
|
||||
|
||||
@@ -113,7 +120,8 @@ class CacheTest : public testing::Test,
|
||||
// Currently, HyperClockCache requires keys to be 16B long, whereas
|
||||
// LRUCache doesn't, so the encoding depends on the cache type.
|
||||
std::string EncodeKey(int k) {
|
||||
if (IsHyperClock()) {
|
||||
auto type = GetParam();
|
||||
if (type == kHyperClock) {
|
||||
return EncodeKey16Bytes(k);
|
||||
} else {
|
||||
return EncodeKey32Bits(k);
|
||||
@@ -121,7 +129,8 @@ class CacheTest : public testing::Test,
|
||||
}
|
||||
|
||||
int DecodeKey(const Slice& k) {
|
||||
if (IsHyperClock()) {
|
||||
auto type = GetParam();
|
||||
if (type == kHyperClock) {
|
||||
return DecodeKey16Bytes(k);
|
||||
} else {
|
||||
return DecodeKey32Bits(k);
|
||||
@@ -173,13 +182,15 @@ std::string CacheTest::type_;
|
||||
class LRUCacheTest : public CacheTest {};
|
||||
|
||||
TEST_P(CacheTest, UsageTest) {
|
||||
auto type = GetParam();
|
||||
|
||||
// cache is std::shared_ptr and will be automatically cleaned up.
|
||||
const size_t kCapacity = 100000;
|
||||
auto cache = NewCache(kCapacity, 6, false, kDontChargeCacheMetadata);
|
||||
auto cache = NewCache(kCapacity, 8, false, kDontChargeCacheMetadata);
|
||||
auto precise_cache = NewCache(kCapacity, 0, false, kFullChargeCacheMetadata);
|
||||
ASSERT_EQ(0, cache->GetUsage());
|
||||
size_t baseline_meta_usage = precise_cache->GetUsage();
|
||||
if (!IsHyperClock()) {
|
||||
if (type != kHyperClock) {
|
||||
ASSERT_EQ(0, baseline_meta_usage);
|
||||
}
|
||||
|
||||
@@ -187,19 +198,20 @@ TEST_P(CacheTest, UsageTest) {
|
||||
char value[10] = "abcdef";
|
||||
// make sure everything will be cached
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
std::string key = EncodeKey(i);
|
||||
std::string key;
|
||||
if (type == kLRU) {
|
||||
key = std::string(i, 'a');
|
||||
} else {
|
||||
key = EncodeKey(i);
|
||||
}
|
||||
auto kv_size = key.size() + 5;
|
||||
ASSERT_OK(cache->Insert(key, value, &kDumbHelper, kv_size));
|
||||
ASSERT_OK(precise_cache->Insert(key, value, &kDumbHelper, kv_size));
|
||||
usage += kv_size;
|
||||
ASSERT_EQ(usage, cache->GetUsage());
|
||||
if (GetParam() == kFixedHyperClock) {
|
||||
if (type == kHyperClock) {
|
||||
ASSERT_EQ(baseline_meta_usage + usage, precise_cache->GetUsage());
|
||||
} else {
|
||||
// AutoHyperClockCache meta usage grows in proportion to lifetime
|
||||
// max number of entries. LRUCache in proportion to resident number of
|
||||
// entries, though there is an untracked component proportional to
|
||||
// lifetime max number of entries.
|
||||
ASSERT_LT(usage, precise_cache->GetUsage());
|
||||
}
|
||||
}
|
||||
@@ -207,15 +219,16 @@ TEST_P(CacheTest, UsageTest) {
|
||||
cache->EraseUnRefEntries();
|
||||
precise_cache->EraseUnRefEntries();
|
||||
ASSERT_EQ(0, cache->GetUsage());
|
||||
if (GetParam() != kAutoHyperClock) {
|
||||
// NOTE: AutoHyperClockCache meta usage grows in proportion to lifetime
|
||||
// max number of entries.
|
||||
ASSERT_EQ(baseline_meta_usage, precise_cache->GetUsage());
|
||||
}
|
||||
ASSERT_EQ(baseline_meta_usage, precise_cache->GetUsage());
|
||||
|
||||
// make sure the cache will be overloaded
|
||||
for (size_t i = 1; i < kCapacity; ++i) {
|
||||
std::string key = EncodeKey(static_cast<int>(1000 + i));
|
||||
std::string key;
|
||||
if (type == kLRU) {
|
||||
key = std::to_string(i);
|
||||
} else {
|
||||
key = EncodeKey(static_cast<int>(1000 + i));
|
||||
}
|
||||
ASSERT_OK(cache->Insert(key, value, &kDumbHelper, key.size() + 5));
|
||||
ASSERT_OK(precise_cache->Insert(key, value, &kDumbHelper, key.size() + 5));
|
||||
}
|
||||
@@ -224,7 +237,7 @@ TEST_P(CacheTest, UsageTest) {
|
||||
ASSERT_GT(kCapacity, cache->GetUsage());
|
||||
ASSERT_GT(kCapacity, precise_cache->GetUsage());
|
||||
ASSERT_LT(kCapacity * 0.95, cache->GetUsage());
|
||||
if (!IsHyperClock()) {
|
||||
if (type != kHyperClock) {
|
||||
ASSERT_LT(kCapacity * 0.95, precise_cache->GetUsage());
|
||||
} else {
|
||||
// estimated value size of 1 is weird for clock cache, because
|
||||
@@ -235,20 +248,22 @@ TEST_P(CacheTest, UsageTest) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This test takes longer than expected on FixedHyperClockCache.
|
||||
// This is because the values size estimate at construction is too sloppy.
|
||||
// TODO: This test takes longer than expected on ClockCache. This is
|
||||
// because the values size estimate at construction is too sloppy.
|
||||
// Fix this.
|
||||
// Why is it so slow? The cache is constructed with an estimate of 1, but
|
||||
// then the charge is claimed to be 21. This will cause the hash table
|
||||
// to be extremely sparse, which in turn means clock needs to scan too
|
||||
// many slots to find victims.
|
||||
TEST_P(CacheTest, PinnedUsageTest) {
|
||||
auto type = GetParam();
|
||||
|
||||
// cache is std::shared_ptr and will be automatically cleaned up.
|
||||
const size_t kCapacity = 200000;
|
||||
auto cache = NewCache(kCapacity, 8, false, kDontChargeCacheMetadata);
|
||||
auto precise_cache = NewCache(kCapacity, 8, false, kFullChargeCacheMetadata);
|
||||
size_t baseline_meta_usage = precise_cache->GetUsage();
|
||||
if (!IsHyperClock()) {
|
||||
if (type != kHyperClock) {
|
||||
ASSERT_EQ(0, baseline_meta_usage);
|
||||
}
|
||||
|
||||
@@ -261,7 +276,12 @@ TEST_P(CacheTest, PinnedUsageTest) {
|
||||
// Add entries. Unpin some of them after insertion. Then, pin some of them
|
||||
// again. Check GetPinnedUsage().
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
std::string key = EncodeKey(i);
|
||||
std::string key;
|
||||
if (type == kLRU) {
|
||||
key = std::string(i, 'a');
|
||||
} else {
|
||||
key = EncodeKey(i);
|
||||
}
|
||||
auto kv_size = key.size() + 5;
|
||||
Cache::Handle* handle;
|
||||
Cache::Handle* handle_in_precise_cache;
|
||||
@@ -302,7 +322,12 @@ TEST_P(CacheTest, PinnedUsageTest) {
|
||||
|
||||
// check that overloading the cache does not change the pinned usage
|
||||
for (size_t i = 1; i < 2 * kCapacity; ++i) {
|
||||
std::string key = EncodeKey(static_cast<int>(1000 + i));
|
||||
std::string key;
|
||||
if (type == kLRU) {
|
||||
key = std::to_string(i);
|
||||
} else {
|
||||
key = EncodeKey(static_cast<int>(1000 + i));
|
||||
}
|
||||
ASSERT_OK(cache->Insert(key, value, &kDumbHelper, key.size() + 5));
|
||||
ASSERT_OK(precise_cache->Insert(key, value, &kDumbHelper, key.size() + 5));
|
||||
}
|
||||
@@ -326,11 +351,7 @@ TEST_P(CacheTest, PinnedUsageTest) {
|
||||
cache->EraseUnRefEntries();
|
||||
precise_cache->EraseUnRefEntries();
|
||||
ASSERT_EQ(0, cache->GetUsage());
|
||||
if (GetParam() != kAutoHyperClock) {
|
||||
// NOTE: AutoHyperClockCache meta usage grows in proportion to lifetime
|
||||
// max number of entries.
|
||||
ASSERT_EQ(baseline_meta_usage, precise_cache->GetUsage());
|
||||
}
|
||||
ASSERT_EQ(baseline_meta_usage, precise_cache->GetUsage());
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, HitAndMiss) {
|
||||
@@ -347,7 +368,7 @@ TEST_P(CacheTest, HitAndMiss) {
|
||||
ASSERT_EQ(-1, Lookup(300));
|
||||
|
||||
Insert(100, 102);
|
||||
if (IsHyperClock()) {
|
||||
if (GetParam() == kHyperClock) {
|
||||
// ClockCache usually doesn't overwrite on Insert
|
||||
ASSERT_EQ(101, Lookup(100));
|
||||
} else {
|
||||
@@ -357,7 +378,7 @@ TEST_P(CacheTest, HitAndMiss) {
|
||||
ASSERT_EQ(-1, Lookup(300));
|
||||
|
||||
ASSERT_EQ(1U, deleted_values_.size());
|
||||
if (IsHyperClock()) {
|
||||
if (GetParam() == kHyperClock) {
|
||||
ASSERT_EQ(102, deleted_values_[0]);
|
||||
} else {
|
||||
ASSERT_EQ(101, deleted_values_[0]);
|
||||
@@ -365,7 +386,7 @@ TEST_P(CacheTest, HitAndMiss) {
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, InsertSameKey) {
|
||||
if (IsHyperClock()) {
|
||||
if (GetParam() == kHyperClock) {
|
||||
ROCKSDB_GTEST_BYPASS(
|
||||
"ClockCache doesn't guarantee Insert overwrite same key.");
|
||||
return;
|
||||
@@ -394,7 +415,7 @@ TEST_P(CacheTest, Erase) {
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, EntriesArePinned) {
|
||||
if (IsHyperClock()) {
|
||||
if (GetParam() == kHyperClock) {
|
||||
ROCKSDB_GTEST_BYPASS(
|
||||
"ClockCache doesn't guarantee Insert overwrite same key.");
|
||||
return;
|
||||
@@ -458,7 +479,7 @@ TEST_P(CacheTest, ExternalRefPinsEntries) {
|
||||
Insert(1000 + j, 2000 + j);
|
||||
}
|
||||
// Clock cache is even more stateful and needs more churn to evict
|
||||
if (IsHyperClock()) {
|
||||
if (GetParam() == kHyperClock) {
|
||||
for (int j = 0; j < kCacheSize; j++) {
|
||||
Insert(11000 + j, 11000 + j);
|
||||
}
|
||||
@@ -496,20 +517,20 @@ TEST_P(CacheTest, EvictionPolicyRef) {
|
||||
// Check whether the entries inserted in the beginning
|
||||
// are evicted. Ones without extra ref are evicted and
|
||||
// those with are not.
|
||||
EXPECT_EQ(-1, Lookup(100));
|
||||
EXPECT_EQ(-1, Lookup(101));
|
||||
EXPECT_EQ(-1, Lookup(102));
|
||||
EXPECT_EQ(-1, Lookup(103));
|
||||
ASSERT_EQ(-1, Lookup(100));
|
||||
ASSERT_EQ(-1, Lookup(101));
|
||||
ASSERT_EQ(-1, Lookup(102));
|
||||
ASSERT_EQ(-1, Lookup(103));
|
||||
|
||||
EXPECT_EQ(-1, Lookup(300));
|
||||
EXPECT_EQ(-1, Lookup(301));
|
||||
EXPECT_EQ(-1, Lookup(302));
|
||||
EXPECT_EQ(-1, Lookup(303));
|
||||
ASSERT_EQ(-1, Lookup(300));
|
||||
ASSERT_EQ(-1, Lookup(301));
|
||||
ASSERT_EQ(-1, Lookup(302));
|
||||
ASSERT_EQ(-1, Lookup(303));
|
||||
|
||||
EXPECT_EQ(101, Lookup(200));
|
||||
EXPECT_EQ(102, Lookup(201));
|
||||
EXPECT_EQ(103, Lookup(202));
|
||||
EXPECT_EQ(104, Lookup(203));
|
||||
ASSERT_EQ(101, Lookup(200));
|
||||
ASSERT_EQ(102, Lookup(201));
|
||||
ASSERT_EQ(103, Lookup(202));
|
||||
ASSERT_EQ(104, Lookup(203));
|
||||
|
||||
// Cleaning up all the handles
|
||||
cache_->Release(h201);
|
||||
@@ -519,22 +540,37 @@ TEST_P(CacheTest, EvictionPolicyRef) {
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, EvictEmptyCache) {
|
||||
auto type = GetParam();
|
||||
|
||||
// Insert item large than capacity to trigger eviction on empty cache.
|
||||
auto cache = NewCache(1, 0, false);
|
||||
ASSERT_OK(cache->Insert(EncodeKey(1000), nullptr, &kDumbHelper, 10));
|
||||
if (type == kLRU) {
|
||||
ASSERT_OK(cache->Insert("foo", nullptr, &kDumbHelper, 10));
|
||||
} else {
|
||||
ASSERT_OK(cache->Insert(EncodeKey(1000), nullptr, &kDumbHelper, 10));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, EraseFromDeleter) {
|
||||
auto type = GetParam();
|
||||
|
||||
// Have deleter which will erase item from cache, which will re-enter
|
||||
// the cache at that point.
|
||||
std::shared_ptr<Cache> cache = NewCache(10, 0, false);
|
||||
std::string foo = EncodeKey(1234);
|
||||
std::string bar = EncodeKey(5678);
|
||||
|
||||
std::function<void()> erase_fn = [&]() { cache->Erase(foo); };
|
||||
std::string foo, bar;
|
||||
const Cache::CacheItemHelper* erase_helper;
|
||||
if (type == kLRU) {
|
||||
foo = "foo";
|
||||
bar = "bar";
|
||||
erase_helper = &kEraseOnDeleteHelper1;
|
||||
} else {
|
||||
foo = EncodeKey(1234);
|
||||
bar = EncodeKey(5678);
|
||||
erase_helper = &kEraseOnDeleteHelper2;
|
||||
}
|
||||
|
||||
ASSERT_OK(cache->Insert(foo, nullptr, &kDumbHelper, 1));
|
||||
ASSERT_OK(cache->Insert(bar, &erase_fn, &kInvokeOnDeleteHelper, 1));
|
||||
ASSERT_OK(cache->Insert(bar, cache.get(), erase_helper, 1));
|
||||
|
||||
cache->Erase(bar);
|
||||
ASSERT_EQ(nullptr, cache->Lookup(foo));
|
||||
@@ -642,10 +678,10 @@ using TypedHandle = SharedCache::TypedHandle;
|
||||
} // namespace
|
||||
|
||||
TEST_P(CacheTest, SetCapacity) {
|
||||
if (IsHyperClock()) {
|
||||
// TODO: update test & code for limited supoort
|
||||
auto type = GetParam();
|
||||
if (type == kHyperClock) {
|
||||
ROCKSDB_GTEST_BYPASS(
|
||||
"HyperClockCache doesn't support arbitrary capacity "
|
||||
"FastLRUCache and HyperClockCache don't support arbitrary capacity "
|
||||
"adjustments.");
|
||||
return;
|
||||
}
|
||||
@@ -775,7 +811,7 @@ TEST_P(CacheTest, OverCapacity) {
|
||||
cache.Release(handles[i]);
|
||||
}
|
||||
|
||||
if (IsHyperClock()) {
|
||||
if (GetParam() == kHyperClock) {
|
||||
// Make sure eviction is triggered.
|
||||
ASSERT_OK(cache.Insert(EncodeKey(-1), nullptr, 1, &handles[0]));
|
||||
|
||||
@@ -887,7 +923,8 @@ TEST_P(CacheTest, DefaultShardBits) {
|
||||
// Prevent excessive allocation (to save time & space)
|
||||
estimated_value_size_ = 100000;
|
||||
// Implementations use different minimum shard sizes
|
||||
size_t min_shard_size = (IsHyperClock() ? 32U * 1024U : 512U) * 1024U;
|
||||
size_t min_shard_size =
|
||||
(GetParam() == kHyperClock ? 32U * 1024U : 512U) * 1024U;
|
||||
|
||||
std::shared_ptr<Cache> cache = NewCache(32U * min_shard_size);
|
||||
ShardedCacheBase* sc = dynamic_cast<ShardedCacheBase*>(cache.get());
|
||||
|
||||
Vendored
+2
-4
@@ -19,10 +19,8 @@ ChargedCache::ChargedCache(std::shared_ptr<Cache> cache,
|
||||
|
||||
Status ChargedCache::Insert(const Slice& key, ObjectPtr obj,
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
Handle** handle, Priority priority,
|
||||
const Slice& compressed_val, CompressionType type) {
|
||||
Status s = target_->Insert(key, obj, helper, charge, handle, priority,
|
||||
compressed_val, type);
|
||||
Handle** handle, Priority priority) {
|
||||
Status s = target_->Insert(key, obj, helper, charge, handle, priority);
|
||||
if (s.ok()) {
|
||||
// Insert may cause the cache entry eviction if the cache is full. So we
|
||||
// directly call the reservation manager to update the total memory used
|
||||
|
||||
Vendored
+3
-5
@@ -22,11 +22,9 @@ class ChargedCache : public CacheWrapper {
|
||||
ChargedCache(std::shared_ptr<Cache> cache,
|
||||
std::shared_ptr<Cache> block_cache);
|
||||
|
||||
Status Insert(
|
||||
const Slice& key, ObjectPtr obj, const CacheItemHelper* helper,
|
||||
size_t charge, Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW, const Slice& compressed_val = Slice(),
|
||||
CompressionType type = CompressionType::kNoCompression) override;
|
||||
Status Insert(const Slice& key, ObjectPtr obj, const CacheItemHelper* helper,
|
||||
size_t charge, Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override;
|
||||
|
||||
Cache::Handle* Lookup(const Slice& key, const CacheItemHelper* helper,
|
||||
CreateContext* create_context,
|
||||
|
||||
Vendored
+535
-2710
File diff suppressed because it is too large.
Load diff
Vendored
+167
-625
@@ -11,7 +11,6 @@
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -21,20 +20,16 @@
|
||||
#include "cache/sharded_cache.h"
|
||||
#include "port/lang.h"
|
||||
#include "port/malloc.h"
|
||||
#include "port/mmap.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "util/atomic.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/math.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace clock_cache {
|
||||
|
||||
// Forward declaration of friend class.
|
||||
template <class ClockCache>
|
||||
class ClockCacheTest;
|
||||
|
||||
// HyperClockCache is an alternative to LRUCache specifically tailored for
|
||||
@@ -42,31 +37,24 @@ class ClockCacheTest;
|
||||
//
|
||||
// Benefits
|
||||
// --------
|
||||
// * Lock/wait free (no waits or spins) for efficiency under high concurrency
|
||||
// * Fixed version (estimated_entry_charge > 0) is fully lock/wait free
|
||||
// * Automatic version (estimated_entry_charge = 0) has rare waits among
|
||||
// certain insertion or erase operations that involve the same very small
|
||||
// set of entries.
|
||||
// * Fully lock free (no waits or spins) for efficiency under high concurrency
|
||||
// * Optimized for hot path reads. For concurrency control, most Lookup() and
|
||||
// essentially all Release() are a single atomic add operation.
|
||||
// * Eviction on insertion is fully parallel.
|
||||
// * Eviction on insertion is fully parallel and lock-free.
|
||||
// * Uses a generalized + aging variant of CLOCK eviction that might outperform
|
||||
// LRU in some cases. (For background, see
|
||||
// https://en.wikipedia.org/wiki/Page_replacement_algorithm)
|
||||
//
|
||||
// Costs
|
||||
// -----
|
||||
// * FixedHyperClockCache (estimated_entry_charge > 0) - Hash table is not
|
||||
// resizable (for lock-free efficiency) so capacity is not dynamically
|
||||
// changeable. Rely on an estimated average value (block) size for
|
||||
// * Hash table is not resizable (for lock-free efficiency) so capacity is not
|
||||
// dynamically changeable. Rely on an estimated average value (block) size for
|
||||
// space+time efficiency. (See estimated_entry_charge option details.)
|
||||
// EXPERIMENTAL - This limitation is fixed in AutoHyperClockCache, activated
|
||||
// with estimated_entry_charge == 0.
|
||||
// * Insert usually does not (but might) overwrite a previous entry associated
|
||||
// with a cache key. This is OK for RocksDB uses of Cache, though it does mess
|
||||
// up our REDUNDANT block cache insertion statistics.
|
||||
// with a cache key. This is OK for RocksDB uses of Cache.
|
||||
// * Only supports keys of exactly 16 bytes, which is what RocksDB uses for
|
||||
// block cache (but not row cache or table cache).
|
||||
// block cache (not row cache or table cache).
|
||||
// * SecondaryCache is not supported.
|
||||
// * Cache priorities are less aggressively enforced. Unlike LRUCache, enough
|
||||
// transient LOW or BOTTOM priority items can evict HIGH priority entries that
|
||||
// are not referenced recently (or often) enough.
|
||||
@@ -149,8 +137,7 @@ class ClockCacheTest;
|
||||
// * Empty - slot is not in use and unowned. All other metadata and data is
|
||||
// in an undefined state.
|
||||
// * Construction - slot is exclusively owned by one thread, the thread
|
||||
// successfully entering this state, for populating or freeing data
|
||||
// (de-construction, same state marker).
|
||||
// successfully entering this state, for populating or freeing data.
|
||||
// * Shareable (group) - slot holds an entry with counted references for
|
||||
// pinning and reading, including
|
||||
// * Visible - slot holds an entry that can be returned by Lookup
|
||||
@@ -198,19 +185,15 @@ class ClockCacheTest;
|
||||
// know from our "redundant" stats that overwrites are very rare for the block
|
||||
// cache, so we should not spend much to make them effective.
|
||||
//
|
||||
// FixedHyperClockCache: Instead we Insert as soon as we find an empty slot in
|
||||
// the probing sequence without seeing an existing (visible) entry for the same
|
||||
// key. This way we only insert if we can improve the probing performance, and
|
||||
// we don't need to probe beyond our insert position, assuming we are willing
|
||||
// to let the previous entry for the same key die of old age (eventual eviction
|
||||
// from not being used). We can reach a similar state with concurrent
|
||||
// insertions, where one will pass over the other while it is "under
|
||||
// construction." This temporary duplication is acceptable for RocksDB block
|
||||
// cache because we know redundant insertion is rare.
|
||||
// AutoHyperClockCache: Similar, except we only notice and return an existing
|
||||
// match if it is found in the search for a suitable empty slot (starting with
|
||||
// the same slot as the head pointer), not by following the existing chain of
|
||||
// entries. Insertions are always made to the head of the chain.
|
||||
// So instead we Insert as soon as we find an empty slot in the probing
|
||||
// sequence without seeing an existing (visible) entry for the same key. This
|
||||
// way we only insert if we can improve the probing performance, and we don't
|
||||
// need to probe beyond our insert position, assuming we are willing to let
|
||||
// the previous entry for the same key die of old age (eventual eviction from
|
||||
// not being used). We can reach a similar state with concurrent insertions,
|
||||
// where one will pass over the other while it is "under construction."
|
||||
// This temporary duplication is acceptable for RocksDB block cache because
|
||||
// we know redundant insertion is rare.
|
||||
//
|
||||
// Another problem to solve is what to return to the caller when we find an
|
||||
// existing entry whose probing position we cannot improve on, or when the
|
||||
@@ -298,7 +281,30 @@ class ClockCacheTest;
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
struct ClockHandleBasicData : public Cache::Handle {
|
||||
// The load factor p is a real number in (0, 1) such that at all
|
||||
// times at most a fraction p of all slots, without counting tombstones,
|
||||
// are occupied by elements. This means that the probability that a random
|
||||
// probe hits an occupied slot is at most p, and thus at most 1/p probes
|
||||
// are required on average. For example, p = 70% implies that between 1 and 2
|
||||
// probes are needed on average (bear in mind that this reasoning doesn't
|
||||
// consider the effects of clustering over time, which should be negligible
|
||||
// with double hashing).
|
||||
// Because the size of the hash table is always rounded up to the next
|
||||
// power of 2, p is really an upper bound on the actual load factor---the
|
||||
// actual load factor is anywhere between p/2 and p. This is a bit wasteful,
|
||||
// but bear in mind that slots only hold metadata, not actual values.
|
||||
// Since space cost is dominated by the values (the LSM blocks),
|
||||
// overprovisioning the table with metadata only increases the total cache space
|
||||
// usage by a tiny fraction.
|
||||
constexpr double kLoadFactor = 0.7;
|
||||
|
||||
// The user can exceed kLoadFactor if the sizes of the inserted values don't
|
||||
// match estimated_value_size, or in some rare cases with
|
||||
// strict_capacity_limit == false. To avoid degenerate performance, we set a
|
||||
// strict upper bound on the load factor.
|
||||
constexpr double kStrictLoadFactor = 0.84;
|
||||
|
||||
struct ClockHandleBasicData {
|
||||
Cache::ObjectPtr value = nullptr;
|
||||
const Cache::CacheItemHelper* helper = nullptr;
|
||||
// A lossless, reversible hash of the fixed-size (16 byte) cache key. This
|
||||
@@ -320,7 +326,7 @@ struct ClockHandle : public ClockHandleBasicData {
|
||||
// state of the handle. The meta word looks like this:
|
||||
// low bits high bits
|
||||
// -----------------------------------------------------------------------
|
||||
// | acquire counter | release counter | hit bit | state marker |
|
||||
// | acquire counter | release counter | state marker |
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
// For reading or updating counters in meta word.
|
||||
@@ -334,12 +340,8 @@ struct ClockHandle : public ClockHandleBasicData {
|
||||
static constexpr uint64_t kReleaseIncrement = uint64_t{1}
|
||||
<< kReleaseCounterShift;
|
||||
|
||||
// For setting the hit bit
|
||||
static constexpr uint8_t kHitBitShift = 2U * kCounterNumBits;
|
||||
static constexpr uint64_t kHitBitMask = uint64_t{1} << kHitBitShift;
|
||||
|
||||
// For reading or updating the state marker in meta word
|
||||
static constexpr uint8_t kStateShift = kHitBitShift + 1;
|
||||
static constexpr uint8_t kStateShift = 2U * kCounterNumBits;
|
||||
|
||||
// Bits contribution to state marker.
|
||||
// Occupied means any state other than empty
|
||||
@@ -369,163 +371,21 @@ struct ClockHandle : public ClockHandleBasicData {
|
||||
static constexpr uint8_t kMaxCountdown = kHighCountdown;
|
||||
// TODO: make these coundown values tuning parameters for eviction?
|
||||
|
||||
// See above. Mutable for read reference counting.
|
||||
mutable AcqRelAtomic<uint64_t> meta{};
|
||||
// See above
|
||||
std::atomic<uint64_t> meta{};
|
||||
|
||||
// Anticipating use for SecondaryCache support
|
||||
void* reserved_for_future_use = nullptr;
|
||||
}; // struct ClockHandle
|
||||
|
||||
class BaseClockTable {
|
||||
public:
|
||||
struct BaseOpts {
|
||||
explicit BaseOpts(int _eviction_effort_cap)
|
||||
: eviction_effort_cap(_eviction_effort_cap) {}
|
||||
explicit BaseOpts(const HyperClockCacheOptions& opts)
|
||||
: BaseOpts(opts.eviction_effort_cap) {}
|
||||
int eviction_effort_cap;
|
||||
};
|
||||
|
||||
BaseClockTable(CacheMetadataChargePolicy metadata_charge_policy,
|
||||
MemoryAllocator* allocator,
|
||||
const Cache::EvictionCallback* eviction_callback,
|
||||
const uint32_t* hash_seed)
|
||||
: metadata_charge_policy_(metadata_charge_policy),
|
||||
allocator_(allocator),
|
||||
eviction_callback_(*eviction_callback),
|
||||
hash_seed_(*hash_seed) {}
|
||||
|
||||
template <class Table>
|
||||
typename Table::HandleImpl* CreateStandalone(ClockHandleBasicData& proto,
|
||||
size_t capacity,
|
||||
uint32_t eec_and_scl,
|
||||
bool allow_uncharged);
|
||||
|
||||
template <class Table>
|
||||
Status Insert(const ClockHandleBasicData& proto,
|
||||
typename Table::HandleImpl** handle, Cache::Priority priority,
|
||||
size_t capacity, uint32_t eec_and_scl);
|
||||
|
||||
void Ref(ClockHandle& handle);
|
||||
|
||||
size_t GetOccupancy() const { return occupancy_.LoadRelaxed(); }
|
||||
|
||||
size_t GetUsage() const { return usage_.LoadRelaxed(); }
|
||||
|
||||
size_t GetStandaloneUsage() const { return standalone_usage_.LoadRelaxed(); }
|
||||
|
||||
uint32_t GetHashSeed() const { return hash_seed_; }
|
||||
|
||||
uint64_t GetYieldCount() const { return yield_count_.LoadRelaxed(); }
|
||||
|
||||
uint64_t GetEvictionEffortExceededCount() const {
|
||||
return eviction_effort_exceeded_count_.LoadRelaxed();
|
||||
}
|
||||
|
||||
struct EvictionData {
|
||||
size_t freed_charge = 0;
|
||||
size_t freed_count = 0;
|
||||
size_t seen_pinned_count = 0;
|
||||
};
|
||||
|
||||
void TrackAndReleaseEvictedEntry(ClockHandle* h);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Acquire N references
|
||||
void TEST_RefN(ClockHandle& handle, size_t n);
|
||||
// Helper for TEST_ReleaseN
|
||||
void TEST_ReleaseNMinus1(ClockHandle* handle, size_t n);
|
||||
#endif
|
||||
|
||||
private: // fns
|
||||
// Creates a "standalone" handle for returning from an Insert operation that
|
||||
// cannot be completed by actually inserting into the table.
|
||||
// Updates `standalone_usage_` but not `usage_` nor `occupancy_`.
|
||||
template <class HandleImpl>
|
||||
HandleImpl* StandaloneInsert(const ClockHandleBasicData& proto);
|
||||
|
||||
// Helper for updating `usage_` for new entry with given `total_charge`
|
||||
// and evicting if needed under strict_capacity_limit=true rules. This
|
||||
// means the operation might fail with Status::MemoryLimit. If
|
||||
// `need_evict_for_occupancy`, then eviction of at least one entry is
|
||||
// required, and the operation should fail if not possible.
|
||||
// NOTE: Otherwise, occupancy_ is not managed in this function
|
||||
template <class Table>
|
||||
Status ChargeUsageMaybeEvictStrict(size_t total_charge, size_t capacity,
|
||||
bool need_evict_for_occupancy,
|
||||
uint32_t eviction_effort_cap,
|
||||
typename Table::InsertState& state);
|
||||
|
||||
// Helper for updating `usage_` for new entry with given `total_charge`
|
||||
// and evicting if needed under strict_capacity_limit=false rules. This
|
||||
// means that updating `usage_` always succeeds even if forced to exceed
|
||||
// capacity. If `need_evict_for_occupancy`, then eviction of at least one
|
||||
// entry is required, and the operation should return false if such eviction
|
||||
// is not possible. `usage_` is not updated in that case. Otherwise, returns
|
||||
// true, indicating success.
|
||||
// NOTE: occupancy_ is not managed in this function
|
||||
template <class Table>
|
||||
bool ChargeUsageMaybeEvictNonStrict(size_t total_charge, size_t capacity,
|
||||
bool need_evict_for_occupancy,
|
||||
uint32_t eviction_effort_cap,
|
||||
typename Table::InsertState& state);
|
||||
|
||||
protected: // data
|
||||
// We partition the following members into different cache lines
|
||||
// to avoid false sharing among Lookup, Release, Erase and Insert
|
||||
// operations in ClockCacheShard.
|
||||
|
||||
// Clock algorithm sweep pointer.
|
||||
// (Relaxed: only needs to be consistent with itself.)
|
||||
RelaxedAtomic<uint64_t> clock_pointer_{};
|
||||
|
||||
// Counter for number of times we yield to wait on another thread.
|
||||
// It is normal for this to occur rarely in normal operation.
|
||||
// (Relaxed: a simple stat counter.)
|
||||
RelaxedAtomic<uint64_t> yield_count_{};
|
||||
|
||||
// Counter for number of times eviction effort cap is exceeded.
|
||||
// It is normal for this to occur rarely in normal operation.
|
||||
// (Relaxed: a simple stat counter.)
|
||||
RelaxedAtomic<uint64_t> eviction_effort_exceeded_count_{};
|
||||
|
||||
// TODO: is this separation needed if we don't do background evictions?
|
||||
ALIGN_AS(CACHE_LINE_SIZE)
|
||||
// Number of elements in the table.
|
||||
AcqRelAtomic<size_t> occupancy_{};
|
||||
|
||||
// Memory usage by entries tracked by the cache (including standalone)
|
||||
AcqRelAtomic<size_t> usage_{};
|
||||
|
||||
// Part of usage by standalone entries (not in table)
|
||||
AcqRelAtomic<size_t> standalone_usage_{};
|
||||
|
||||
ALIGN_AS(CACHE_LINE_SIZE)
|
||||
const CacheMetadataChargePolicy metadata_charge_policy_;
|
||||
|
||||
// From Cache, for deleter
|
||||
MemoryAllocator* const allocator_;
|
||||
|
||||
// A reference to Cache::eviction_callback_
|
||||
const Cache::EvictionCallback& eviction_callback_;
|
||||
|
||||
// A reference to ShardedCacheBase::hash_seed_
|
||||
const uint32_t& hash_seed_;
|
||||
};
|
||||
|
||||
// Hash table for cache entries with size determined at creation time.
|
||||
// Uses open addressing and double hashing. Since entries cannot be moved,
|
||||
// the "displacements" count ensures probing sequences find entries even when
|
||||
// entries earlier in the probing sequence have been removed.
|
||||
class FixedHyperClockTable : public BaseClockTable {
|
||||
class HyperClockTable {
|
||||
public:
|
||||
// Target size to be exactly a common cache line size (see static_assert in
|
||||
// clock_cache.cc)
|
||||
struct ALIGN_AS(64U) HandleImpl : public ClockHandle {
|
||||
// The number of elements that hash to this slot or a lower one, but wind
|
||||
// up in this slot or a higher one.
|
||||
// (Relaxed: within a Cache op, does not need consistency with entries
|
||||
// inserted/removed during that op. For example, a Lookup() that
|
||||
// happens-after an Insert() will see an appropriate displacements value
|
||||
// for the entry to be in a published state.)
|
||||
RelaxedAtomic<uint32_t> displacements{};
|
||||
std::atomic<uint32_t> displacements{};
|
||||
|
||||
// Whether this is a "deteched" handle that is independently allocated
|
||||
// with `new` (so must be deleted with `delete`).
|
||||
@@ -539,108 +399,89 @@ class FixedHyperClockTable : public BaseClockTable {
|
||||
inline void SetStandalone() { standalone = true; }
|
||||
}; // struct HandleImpl
|
||||
|
||||
struct Opts : public BaseOpts {
|
||||
explicit Opts(size_t _estimated_value_size, int _eviction_effort_cap)
|
||||
: BaseOpts(_eviction_effort_cap),
|
||||
estimated_value_size(_estimated_value_size) {}
|
||||
explicit Opts(const HyperClockCacheOptions& opts)
|
||||
: BaseOpts(opts.eviction_effort_cap) {
|
||||
assert(opts.estimated_entry_charge > 0);
|
||||
estimated_value_size = opts.estimated_entry_charge;
|
||||
}
|
||||
struct Opts {
|
||||
size_t estimated_value_size;
|
||||
};
|
||||
|
||||
FixedHyperClockTable(size_t capacity,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
MemoryAllocator* allocator,
|
||||
const Cache::EvictionCallback* eviction_callback,
|
||||
const uint32_t* hash_seed, const Opts& opts);
|
||||
~FixedHyperClockTable();
|
||||
HyperClockTable(size_t capacity, bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
MemoryAllocator* allocator,
|
||||
const Cache::EvictionCallback* eviction_callback,
|
||||
const uint32_t* hash_seed, const Opts& opts);
|
||||
~HyperClockTable();
|
||||
|
||||
// For BaseClockTable::Insert
|
||||
struct InsertState {};
|
||||
Status Insert(const ClockHandleBasicData& proto, HandleImpl** handle,
|
||||
Cache::Priority priority, size_t capacity,
|
||||
bool strict_capacity_limit);
|
||||
|
||||
void StartInsert(InsertState& state);
|
||||
|
||||
// Returns true iff there is room for the proposed number of entries.
|
||||
bool GrowIfNeeded(size_t new_occupancy, InsertState& state);
|
||||
|
||||
HandleImpl* DoInsert(const ClockHandleBasicData& proto,
|
||||
uint64_t initial_countdown, bool take_ref,
|
||||
InsertState& state);
|
||||
|
||||
// Runs the clock eviction algorithm trying to reclaim at least
|
||||
// requested_charge. Returns how much is evicted, which could be less
|
||||
// if it appears impossible to evict the requested amount without blocking.
|
||||
void Evict(size_t requested_charge, InsertState& state, EvictionData* data,
|
||||
uint32_t eviction_effort_cap);
|
||||
HandleImpl* CreateStandalone(ClockHandleBasicData& proto, size_t capacity,
|
||||
bool strict_capacity_limit,
|
||||
bool allow_uncharged);
|
||||
|
||||
HandleImpl* Lookup(const UniqueId64x2& hashed_key);
|
||||
|
||||
bool Release(HandleImpl* handle, bool useful, bool erase_if_last_ref);
|
||||
|
||||
void Ref(HandleImpl& handle);
|
||||
|
||||
void Erase(const UniqueId64x2& hashed_key);
|
||||
|
||||
void ConstApplyToEntriesRange(std::function<void(const HandleImpl&)> func,
|
||||
size_t index_begin, size_t index_end,
|
||||
bool apply_if_will_be_deleted) const;
|
||||
|
||||
void EraseUnRefEntries();
|
||||
|
||||
size_t GetTableSize() const { return size_t{1} << length_bits_; }
|
||||
|
||||
size_t GetOccupancyLimit() const { return occupancy_limit_; }
|
||||
int GetLengthBits() const { return length_bits_; }
|
||||
|
||||
const HandleImpl* HandlePtr(size_t idx) const { return &array_[idx]; }
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t& TEST_MutableOccupancyLimit() {
|
||||
return const_cast<size_t&>(occupancy_limit_);
|
||||
size_t GetOccupancy() const {
|
||||
return occupancy_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// Release N references
|
||||
size_t GetOccupancyLimit() const { return occupancy_limit_; }
|
||||
|
||||
size_t GetUsage() const { return usage_.load(std::memory_order_relaxed); }
|
||||
|
||||
size_t GetStandaloneUsage() const {
|
||||
return standalone_usage_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
uint32_t GetHashSeed() const { return hash_seed_; }
|
||||
|
||||
// Acquire/release N references
|
||||
void TEST_RefN(HandleImpl& handle, size_t n);
|
||||
void TEST_ReleaseN(HandleImpl* handle, size_t n);
|
||||
#endif
|
||||
|
||||
// The load factor p is a real number in (0, 1) such that at all
|
||||
// times at most a fraction p of all slots, without counting tombstones,
|
||||
// are occupied by elements. This means that the probability that a random
|
||||
// probe hits an occupied slot is at most p, and thus at most 1/p probes
|
||||
// are required on average. For example, p = 70% implies that between 1 and 2
|
||||
// probes are needed on average (bear in mind that this reasoning doesn't
|
||||
// consider the effects of clustering over time, which should be negligible
|
||||
// with double hashing).
|
||||
// Because the size of the hash table is always rounded up to the next
|
||||
// power of 2, p is really an upper bound on the actual load factor---the
|
||||
// actual load factor is anywhere between p/2 and p. This is a bit wasteful,
|
||||
// but bear in mind that slots only hold metadata, not actual values.
|
||||
// Since space cost is dominated by the values (the LSM blocks),
|
||||
// overprovisioning the table with metadata only increases the total cache
|
||||
// space usage by a tiny fraction.
|
||||
static constexpr double kLoadFactor = 0.7;
|
||||
|
||||
// The user can exceed kLoadFactor if the sizes of the inserted values don't
|
||||
// match estimated_value_size, or in some rare cases with
|
||||
// strict_capacity_limit == false. To avoid degenerate performance, we set a
|
||||
// strict upper bound on the load factor.
|
||||
static constexpr double kStrictLoadFactor = 0.84;
|
||||
|
||||
private: // functions
|
||||
// Returns x mod 2^{length_bits_}.
|
||||
inline size_t ModTableSize(uint64_t x) {
|
||||
return BitwiseAnd(x, length_bits_mask_);
|
||||
return static_cast<size_t>(x) & length_bits_mask_;
|
||||
}
|
||||
|
||||
// Returns the first slot in the probe sequence with a handle e such that
|
||||
// match_fn(e) is true. At every step, the function first tests whether
|
||||
// match_fn(e) holds. If this is false, it evaluates abort_fn(e) to decide
|
||||
// whether the search should be aborted, and if so, FindSlot immediately
|
||||
// returns nullptr. For every handle e that is not a match and not aborted,
|
||||
// FindSlot runs update_fn(e, is_last) where is_last is set to true iff that
|
||||
// slot will be the last probed because the next would cycle back to the first
|
||||
// slot probed. This function uses templates instead of std::function to
|
||||
// minimize the risk of heap-allocated closures being created.
|
||||
template <typename MatchFn, typename AbortFn, typename UpdateFn>
|
||||
// Runs the clock eviction algorithm trying to reclaim at least
|
||||
// requested_charge. Returns how much is evicted, which could be less
|
||||
// if it appears impossible to evict the requested amount without blocking.
|
||||
inline void Evict(size_t requested_charge, size_t* freed_charge,
|
||||
size_t* freed_count);
|
||||
|
||||
// Returns the first slot in the probe sequence, starting from the given
|
||||
// probe number, with a handle e such that match(e) is true. At every
|
||||
// step, the function first tests whether match(e) holds. If this is false,
|
||||
// it evaluates abort(e) to decide whether the search should be aborted,
|
||||
// and in the affirmative returns -1. For every handle e probed except
|
||||
// the last one, the function runs update(e).
|
||||
// The probe parameter is modified as follows. We say a probe to a handle
|
||||
// e is aborting if match(e) is false and abort(e) is true. Then the final
|
||||
// value of probe is one more than the last non-aborting probe during the
|
||||
// call. This is so that that the variable can be used to keep track of
|
||||
// progress across consecutive calls to FindSlot.
|
||||
inline HandleImpl* FindSlot(const UniqueId64x2& hashed_key,
|
||||
const MatchFn& match_fn, const AbortFn& abort_fn,
|
||||
const UpdateFn& update_fn);
|
||||
std::function<bool(HandleImpl*)> match,
|
||||
std::function<bool(HandleImpl*)> stop,
|
||||
std::function<void(HandleImpl*)> update,
|
||||
size_t& probe);
|
||||
|
||||
// Re-decrement all displacements in probe path starting from beginning
|
||||
// until (not including) the given handle
|
||||
@@ -653,6 +494,33 @@ class FixedHyperClockTable : public BaseClockTable {
|
||||
// before releasing it so that it can be provided to this function.
|
||||
inline void ReclaimEntryUsage(size_t total_charge);
|
||||
|
||||
// Helper for updating `usage_` for new entry with given `total_charge`
|
||||
// and evicting if needed under strict_capacity_limit=true rules. This
|
||||
// means the operation might fail with Status::MemoryLimit. If
|
||||
// `need_evict_for_occupancy`, then eviction of at least one entry is
|
||||
// required, and the operation should fail if not possible.
|
||||
// NOTE: Otherwise, occupancy_ is not managed in this function
|
||||
inline Status ChargeUsageMaybeEvictStrict(size_t total_charge,
|
||||
size_t capacity,
|
||||
bool need_evict_for_occupancy);
|
||||
|
||||
// Helper for updating `usage_` for new entry with given `total_charge`
|
||||
// and evicting if needed under strict_capacity_limit=false rules. This
|
||||
// means that updating `usage_` always succeeds even if forced to exceed
|
||||
// capacity. If `need_evict_for_occupancy`, then eviction of at least one
|
||||
// entry is required, and the operation should return false if such eviction
|
||||
// is not possible. `usage_` is not updated in that case. Otherwise, returns
|
||||
// true, indicating success.
|
||||
// NOTE: occupancy_ is not managed in this function
|
||||
inline bool ChargeUsageMaybeEvictNonStrict(size_t total_charge,
|
||||
size_t capacity,
|
||||
bool need_evict_for_occupancy);
|
||||
|
||||
// Creates a "standalone" handle for returning from an Insert operation that
|
||||
// cannot be completed by actually inserting into the table.
|
||||
// Updates `standalone_usage_` but not `usage_` nor `occupancy_`.
|
||||
inline HandleImpl* StandaloneInsert(const ClockHandleBasicData& proto);
|
||||
|
||||
MemoryAllocator* GetAllocator() const { return allocator_; }
|
||||
|
||||
// Returns the number of bits used to hash an element in the hash
|
||||
@@ -673,329 +541,39 @@ class FixedHyperClockTable : public BaseClockTable {
|
||||
|
||||
// Array of slots comprising the hash table.
|
||||
const std::unique_ptr<HandleImpl[]> array_;
|
||||
}; // class FixedHyperClockTable
|
||||
|
||||
// Hash table for cache entries that resizes automatically based on occupancy.
|
||||
// However, it depends on a contiguous memory region to grow into
|
||||
// incrementally, using linear hashing, so uses an anonymous mmap so that
|
||||
// only the used portion of the memory region is mapped to physical memory
|
||||
// (part of RSS).
|
||||
//
|
||||
// This table implementation uses the same "low-level protocol" for managing
|
||||
// the contens of an entry slot as FixedHyperClockTable does, captured in the
|
||||
// ClockHandle struct. The provides most of the essential data safety, but
|
||||
// AutoHyperClockTable is another "high-level protocol" for organizing entries
|
||||
// into a hash table, with automatic resizing.
|
||||
//
|
||||
// This implementation is not fully wait-free but we can call it "essentially
|
||||
// wait-free," and here's why. First, like FixedHyperClockCache, there is no
|
||||
// locking nor other forms of waiting at the cache or shard level. Also like
|
||||
// FixedHCC there is essentially an entry-level read-write lock implemented
|
||||
// with atomics, but our relaxed atomicity/consistency guarantees (e.g.
|
||||
// duplicate inserts are possible) mean we do not need to wait for entry
|
||||
// locking. Lookups, non-erasing Releases, and non-evicting non-growing Inserts
|
||||
// are all fully wait-free. Of course, these waits are not dependent on any
|
||||
// external factors such as I/O.
|
||||
//
|
||||
// For operations that remove entries from a chain or grow the table by
|
||||
// splitting a chain, there is a chain-level locking mechanism that we call a
|
||||
// "rewrite" lock, and the only waits are for these locks. On average, each
|
||||
// chain lock is relevant to < 2 entries each. (The average would be less than
|
||||
// one entry each, but we do not lock when there's no entry to remove or
|
||||
// migrate.) And a given thread can only hold two such chain locks at a time,
|
||||
// more typically just one. So in that sense alone, the waiting that does exist
|
||||
// is very localized.
|
||||
//
|
||||
// If we look closer at the operations utilizing that locking mechanism, we
|
||||
// can see why it's "essentially wait-free."
|
||||
// * Grow operations to increase the size of the table: each operation splits
|
||||
// an existing chain into two, and chains for splitting are chosen in table
|
||||
// order. Grow operations are fully parallel except for the chain locking, but
|
||||
// for one Grow operation to wait on another, it has to be feeding into the
|
||||
// other, which means the table has doubled in size already from other Grow
|
||||
// operations without the original one finishing. So Grow operations are very
|
||||
// low latency (unlike LRUCache doubling the table size in one operation) and
|
||||
// very parallelizeable. (We use some tricks to break up dependencies in
|
||||
// updating metadata on the usable size of the table.) And obviously Grow
|
||||
// operations are very rare after the initial population of the table.
|
||||
// * Evict operations (part of many Inserts): clock updates and evictions
|
||||
// sweep through the structure in table order, so like Grow operations,
|
||||
// parallel Evict can only wait on each other if an Evict has lingered (slept)
|
||||
// long enough that the clock pointer has wrapped around the entire structure.
|
||||
// * Random erasures (Erase, Release with erase_if_last_ref, etc.): these
|
||||
// operations are rare and not really considered performance critical.
|
||||
// Currently they're mostly used for removing placeholder cache entries, e.g.
|
||||
// for memory tracking, though that could use standalone entries instead to
|
||||
// avoid potential contention in table operations. It's possible that future
|
||||
// enhancements could pro-actively remove cache entries from obsolete files,
|
||||
// but that's not yet implemented.
|
||||
class AutoHyperClockTable : public BaseClockTable {
|
||||
public:
|
||||
// Target size to be exactly a common cache line size (see static_assert in
|
||||
// clock_cache.cc)
|
||||
struct ALIGN_AS(64U) HandleImpl : public ClockHandle {
|
||||
// To orgainize AutoHyperClockTable entries into a hash table while
|
||||
// allowing the table size to grow without existing entries being moved,
|
||||
// a version of chaining is used. Rather than being heap allocated (and
|
||||
// incurring overheads to ensure memory safety) entries must go into
|
||||
// Handles ("slots") in the pre-allocated array. To improve CPU cache
|
||||
// locality, the chain head pointers are interleved with the entries;
|
||||
// specifically, a Handle contains
|
||||
// * A head pointer for a chain of entries with this "home" location.
|
||||
// * A ClockHandle, for an entry that may or may not be in the chain
|
||||
// starting from that head (but for performance ideally is on that
|
||||
// chain).
|
||||
// * A next pointer for the continuation of the chain containing this
|
||||
// entry.
|
||||
//
|
||||
// The pointers are not raw pointers, but are indices into the array,
|
||||
// and are decorated in two ways to help detect and recover from
|
||||
// relevant concurrent modifications during Lookup, so that Lookup is
|
||||
// fully wait-free:
|
||||
// * Each "with_shift" pointer contains a shift count that indicates
|
||||
// how many hash bits were used in chosing the home address for the
|
||||
// chain--specifically the next entry in the chain.
|
||||
// * The end of a chain is given a special "end" marker and refers back
|
||||
// to the head of the chain.
|
||||
//
|
||||
// Why do we need shift on each pointer? To make Lookup wait-free, we need
|
||||
// to be able to query a chain without missing anything, and preferably
|
||||
// avoid synchronously double-checking the length_info. Without the shifts,
|
||||
// there is a risk that we start down a chain and while paused on an entry
|
||||
// that goes to a new home, we then follow the rest of the
|
||||
// partially-migrated chain to see the shared ending with the old home, but
|
||||
// for a time were following the chain for the new home, missing some
|
||||
// entries for the old home.
|
||||
//
|
||||
// Why do we need the end of the chain to loop back? If Lookup pauses
|
||||
// at an "under construction" entry, and sees that "next" is null after
|
||||
// waking up, we need something to tell whether the "under construction"
|
||||
// entry was freed and reused for another chain. Otherwise, we could
|
||||
// miss entries still on the original chain due in the presence of a
|
||||
// concurrent modification. Until an entry is fully erased from a chain,
|
||||
// it is normal to see "under construction" entries on the chain, and it
|
||||
// is not safe to read their hashed key without either a read reference
|
||||
// on the entry or a rewrite lock on the chain.
|
||||
// From Cache, for deleter
|
||||
MemoryAllocator* const allocator_;
|
||||
|
||||
// Marker in a "with_shift" head pointer for some thread owning writes
|
||||
// to the chain structure (except for inserts), but only if not an
|
||||
// "end" pointer. Also called the "rewrite lock."
|
||||
static constexpr uint64_t kHeadLocked = uint64_t{1} << 7;
|
||||
// A reference to Cache::eviction_callback_
|
||||
const Cache::EvictionCallback& eviction_callback_;
|
||||
|
||||
// Marker in a "with_shift" pointer for the end of a chain. Must also
|
||||
// point back to the head of the chain (with end marker removed).
|
||||
// Also includes the "locked" bit so that attempting to lock an empty
|
||||
// chain has no effect (not needed, as the lock is only needed for
|
||||
// removals).
|
||||
static constexpr uint64_t kNextEndFlags = (uint64_t{1} << 6) | kHeadLocked;
|
||||
// A reference to ShardedCacheBase::hash_seed_
|
||||
const uint32_t& hash_seed_;
|
||||
|
||||
static inline bool IsEnd(uint64_t next_with_shift) {
|
||||
// Assuming certain values never used, suffices to check this one bit
|
||||
constexpr auto kCheckBit = kNextEndFlags ^ kHeadLocked;
|
||||
return next_with_shift & kCheckBit;
|
||||
}
|
||||
// We partition the following members into different cache lines
|
||||
// to avoid false sharing among Lookup, Release, Erase and Insert
|
||||
// operations in ClockCacheShard.
|
||||
|
||||
// Bottom bits to right shift away to get an array index from a
|
||||
// "with_shift" pointer.
|
||||
static constexpr int kNextShift = 8;
|
||||
ALIGN_AS(CACHE_LINE_SIZE)
|
||||
// Clock algorithm sweep pointer.
|
||||
std::atomic<uint64_t> clock_pointer_{};
|
||||
|
||||
// A bit mask for the "shift" associated with each "with_shift" pointer.
|
||||
// Always bottommost bits.
|
||||
static constexpr int kShiftMask = 63;
|
||||
ALIGN_AS(CACHE_LINE_SIZE)
|
||||
// Number of elements in the table.
|
||||
std::atomic<size_t> occupancy_{};
|
||||
|
||||
// A marker for head_next_with_shift that indicates this HandleImpl is
|
||||
// heap allocated (standalone) rather than in the table.
|
||||
static constexpr uint64_t kStandaloneMarker = UINT64_MAX;
|
||||
// Memory usage by entries tracked by the cache (including standalone)
|
||||
std::atomic<size_t> usage_{};
|
||||
|
||||
// A marker for head_next_with_shift indicating the head is not yet part
|
||||
// of the usable table, or for chain_next_with_shift indicating that the
|
||||
// entry is not present or is not yet part of a chain (must not be
|
||||
// "shareable" state).
|
||||
static constexpr uint64_t kUnusedMarker = 0;
|
||||
|
||||
// See above. The head pointer is logically independent of the rest of
|
||||
// the entry, including the chain next pointer.
|
||||
AcqRelAtomic<uint64_t> head_next_with_shift{kUnusedMarker};
|
||||
AcqRelAtomic<uint64_t> chain_next_with_shift{kUnusedMarker};
|
||||
|
||||
// For supporting CreateStandalone and some fallback cases.
|
||||
inline bool IsStandalone() const {
|
||||
return head_next_with_shift.Load() == kStandaloneMarker;
|
||||
}
|
||||
|
||||
inline void SetStandalone() {
|
||||
head_next_with_shift.Store(kStandaloneMarker);
|
||||
}
|
||||
}; // struct HandleImpl
|
||||
|
||||
struct Opts : public BaseOpts {
|
||||
explicit Opts(size_t _min_avg_value_size, int _eviction_effort_cap)
|
||||
: BaseOpts(_eviction_effort_cap),
|
||||
min_avg_value_size(_min_avg_value_size) {}
|
||||
|
||||
explicit Opts(const HyperClockCacheOptions& opts)
|
||||
: BaseOpts(opts.eviction_effort_cap) {
|
||||
assert(opts.estimated_entry_charge == 0);
|
||||
min_avg_value_size = opts.min_avg_entry_charge;
|
||||
}
|
||||
size_t min_avg_value_size;
|
||||
};
|
||||
|
||||
AutoHyperClockTable(size_t capacity,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
MemoryAllocator* allocator,
|
||||
const Cache::EvictionCallback* eviction_callback,
|
||||
const uint32_t* hash_seed, const Opts& opts);
|
||||
~AutoHyperClockTable();
|
||||
|
||||
// For BaseClockTable::Insert
|
||||
struct InsertState {
|
||||
uint64_t saved_length_info = 0;
|
||||
size_t likely_empty_slot = 0;
|
||||
};
|
||||
|
||||
void StartInsert(InsertState& state);
|
||||
|
||||
// Does initial check for whether there's hash table room for another
|
||||
// inserted entry, possibly growing if needed. Returns true iff (after
|
||||
// the call) there is room for the proposed number of entries.
|
||||
bool GrowIfNeeded(size_t new_occupancy, InsertState& state);
|
||||
|
||||
HandleImpl* DoInsert(const ClockHandleBasicData& proto,
|
||||
uint64_t initial_countdown, bool take_ref,
|
||||
InsertState& state);
|
||||
|
||||
// Runs the clock eviction algorithm trying to reclaim at least
|
||||
// requested_charge. Returns how much is evicted, which could be less
|
||||
// if it appears impossible to evict the requested amount without blocking.
|
||||
void Evict(size_t requested_charge, InsertState& state, EvictionData* data,
|
||||
uint32_t eviction_effort_cap);
|
||||
|
||||
HandleImpl* Lookup(const UniqueId64x2& hashed_key);
|
||||
|
||||
bool Release(HandleImpl* handle, bool useful, bool erase_if_last_ref);
|
||||
|
||||
void Erase(const UniqueId64x2& hashed_key);
|
||||
|
||||
void EraseUnRefEntries();
|
||||
|
||||
size_t GetTableSize() const;
|
||||
|
||||
size_t GetOccupancyLimit() const;
|
||||
|
||||
const HandleImpl* HandlePtr(size_t idx) const { return &array_[idx]; }
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t& TEST_MutableOccupancyLimit() {
|
||||
return *reinterpret_cast<size_t*>(&occupancy_limit_);
|
||||
}
|
||||
|
||||
// Release N references
|
||||
void TEST_ReleaseN(HandleImpl* handle, size_t n);
|
||||
#endif
|
||||
|
||||
// Maximum ratio of number of occupied slots to number of usable slots. The
|
||||
// actual load factor should float pretty close to this number, which should
|
||||
// be a nice space/time trade-off, though large swings in WriteBufferManager
|
||||
// memory could lead to low (but very much safe) load factors (only after
|
||||
// seeing high load factors). Linear hashing along with (modified) linear
|
||||
// probing to find an available slot increases potential risks of high
|
||||
// load factors, so are disallowed.
|
||||
static constexpr double kMaxLoadFactor = 0.60;
|
||||
|
||||
private: // functions
|
||||
// Returns true iff increased usable length. Due to load factor
|
||||
// considerations, GrowIfNeeded might call this more than once to make room
|
||||
// for one more entry.
|
||||
bool Grow(InsertState& state);
|
||||
|
||||
// Operational details of splitting a chain into two for Grow().
|
||||
void SplitForGrow(size_t grow_home, size_t old_home, int old_shift);
|
||||
|
||||
// Takes an "under construction" entry and ensures it is no longer connected
|
||||
// to its home chain (in preparaion for completing erasure and freeing the
|
||||
// slot). Note that previous operations might have already noticed it being
|
||||
// "under (de)construction" and removed it from its chain.
|
||||
void Remove(HandleImpl* h);
|
||||
|
||||
// Try to take ownership of an entry and erase+remove it from the table.
|
||||
// Returns true if successful. Could fail if
|
||||
// * There are other references to the entry
|
||||
// * Some other thread has exclusive ownership or has freed it.
|
||||
bool TryEraseHandle(HandleImpl* h, bool holding_ref, bool mark_invisible);
|
||||
|
||||
// Calculates the appropriate maximum table size, for creating the memory
|
||||
// mapping.
|
||||
static size_t CalcMaxUsableLength(
|
||||
size_t capacity, size_t min_avg_value_size,
|
||||
CacheMetadataChargePolicy metadata_charge_policy);
|
||||
|
||||
// Shared helper function that implements removing entries from a chain
|
||||
// with proper handling to ensure all existing data is seen even in the
|
||||
// presence of concurrent insertions, etc. (See implementation.)
|
||||
template <class OpData>
|
||||
void PurgeImpl(OpData* op_data, size_t home = SIZE_MAX,
|
||||
EvictionData* data = nullptr);
|
||||
|
||||
// An RAII wrapper for locking a chain of entries for removals. See
|
||||
// implementation.
|
||||
class ChainRewriteLock;
|
||||
|
||||
// Helper function for PurgeImpl while holding a ChainRewriteLock. See
|
||||
// implementation.
|
||||
template <class OpData>
|
||||
void PurgeImplLocked(OpData* op_data, ChainRewriteLock& rewrite_lock,
|
||||
size_t home, EvictionData* data);
|
||||
|
||||
// Update length_info_ as much as possible without waiting, given a known
|
||||
// usable (ready for inserts and lookups) grow_home. (Previous grow_homes
|
||||
// might not be usable yet, but we can check if they are by looking at
|
||||
// the corresponding old home.)
|
||||
void CatchUpLengthInfoNoWait(size_t known_usable_grow_home);
|
||||
|
||||
private: // data
|
||||
// mmaped area holding handles
|
||||
const TypedMemMapping<HandleImpl> array_;
|
||||
|
||||
// Metadata for table size under linear hashing.
|
||||
//
|
||||
// Lowest 8 bits are the minimum number of lowest hash bits to use
|
||||
// ("min shift"). The upper 56 bits are a threshold. If that minumum number
|
||||
// of bits taken from a hash value is < this threshold, then one more bit of
|
||||
// hash value is taken and used.
|
||||
//
|
||||
// Other mechanisms (shift amounts on pointers) ensure complete availability
|
||||
// of data already in the table even if a reader only sees a completely
|
||||
// out-of-date version of this value. In the worst case, it could take
|
||||
// log time to find the correct chain, but normally this value enables
|
||||
// readers to find the correct chain on the first try.
|
||||
//
|
||||
// To maximize parallelization of Grow() operations, this field is only
|
||||
// updated opportunistically after Grow() operations and in DoInsert() where
|
||||
// it is found to be out-of-date. See CatchUpLengthInfoNoWait().
|
||||
AcqRelAtomic<uint64_t> length_info_;
|
||||
|
||||
// An already-computed version of the usable length times the max load
|
||||
// factor. Could be slightly out of date but GrowIfNeeded()/Grow() handle
|
||||
// that internally.
|
||||
// (Relaxed: allowed to lag behind length_info_ by a little)
|
||||
RelaxedAtomic<size_t> occupancy_limit_;
|
||||
|
||||
// The next index to use from array_ upon the next Grow(). Might be ahead of
|
||||
// length_info_.
|
||||
// (Relaxed: self-contained source of truth for next grow home)
|
||||
RelaxedAtomic<size_t> grow_frontier_;
|
||||
|
||||
// See explanation in AutoHyperClockTable::Evict
|
||||
// (Relaxed: allowed to lag behind clock_pointer_ and length_info_ state)
|
||||
RelaxedAtomic<size_t> clock_pointer_mask_;
|
||||
}; // class AutoHyperClockTable
|
||||
// Part of usage by standalone entries (not in table)
|
||||
std::atomic<size_t> standalone_usage_{};
|
||||
}; // class HyperClockTable
|
||||
|
||||
// A single shard of sharded cache.
|
||||
template <class TableT>
|
||||
template <class Table>
|
||||
class ALIGN_AS(CACHE_LINE_SIZE) ClockCacheShard final : public CacheShardBase {
|
||||
public:
|
||||
using Table = TableT;
|
||||
ClockCacheShard(size_t capacity, bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
MemoryAllocator* allocator,
|
||||
@@ -1088,39 +666,31 @@ class ALIGN_AS(CACHE_LINE_SIZE) ClockCacheShard final : public CacheShardBase {
|
||||
return Lookup(key, hashed_key);
|
||||
}
|
||||
|
||||
Table& GetTable() { return table_; }
|
||||
const Table& GetTable() const { return table_; }
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t& TEST_MutableOccupancyLimit() {
|
||||
return table_.TEST_MutableOccupancyLimit();
|
||||
}
|
||||
// Acquire/release N references
|
||||
void TEST_RefN(HandleImpl* handle, size_t n);
|
||||
void TEST_ReleaseN(HandleImpl* handle, size_t n);
|
||||
#endif
|
||||
|
||||
private: // data
|
||||
Table table_;
|
||||
|
||||
// Maximum total charge of all elements stored in the table.
|
||||
// (Relaxed: eventual consistency/update is OK)
|
||||
RelaxedAtomic<size_t> capacity_;
|
||||
std::atomic<size_t> capacity_;
|
||||
|
||||
// Encodes eviction_effort_cap (bottom 31 bits) and strict_capacity_limit
|
||||
// (top bit). See HyperClockCacheOptions::eviction_effort_cap etc.
|
||||
// (Relaxed: eventual consistency/update is OK)
|
||||
RelaxedAtomic<uint32_t> eec_and_scl_;
|
||||
// Whether to reject insertion if cache reaches its full capacity.
|
||||
std::atomic<bool> strict_capacity_limit_;
|
||||
}; // class ClockCacheShard
|
||||
|
||||
template <class Table>
|
||||
class BaseHyperClockCache : public ShardedCache<ClockCacheShard<Table>> {
|
||||
class HyperClockCache
|
||||
#ifdef NDEBUG
|
||||
final
|
||||
#endif
|
||||
: public ShardedCache<ClockCacheShard<HyperClockTable>> {
|
||||
public:
|
||||
using Shard = ClockCacheShard<Table>;
|
||||
using Handle = Cache::Handle;
|
||||
using CacheItemHelper = Cache::CacheItemHelper;
|
||||
using Shard = ClockCacheShard<HyperClockTable>;
|
||||
|
||||
explicit BaseHyperClockCache(const HyperClockCacheOptions& opts);
|
||||
explicit HyperClockCache(const HyperClockCacheOptions& opts);
|
||||
|
||||
const char* Name() const override { return "HyperClockCache"; }
|
||||
|
||||
Cache::ObjectPtr Value(Handle* handle) override;
|
||||
|
||||
@@ -1130,35 +700,7 @@ class BaseHyperClockCache : public ShardedCache<ClockCacheShard<Table>> {
|
||||
|
||||
void ReportProblems(
|
||||
const std::shared_ptr<Logger>& /*info_log*/) const override;
|
||||
};
|
||||
|
||||
class FixedHyperClockCache
|
||||
#ifdef NDEBUG
|
||||
final
|
||||
#endif
|
||||
: public BaseHyperClockCache<FixedHyperClockTable> {
|
||||
public:
|
||||
using BaseHyperClockCache::BaseHyperClockCache;
|
||||
|
||||
const char* Name() const override { return "FixedHyperClockCache"; }
|
||||
|
||||
void ReportProblems(
|
||||
const std::shared_ptr<Logger>& /*info_log*/) const override;
|
||||
}; // class FixedHyperClockCache
|
||||
|
||||
class AutoHyperClockCache
|
||||
#ifdef NDEBUG
|
||||
final
|
||||
#endif
|
||||
: public BaseHyperClockCache<AutoHyperClockTable> {
|
||||
public:
|
||||
using BaseHyperClockCache::BaseHyperClockCache;
|
||||
|
||||
const char* Name() const override { return "AutoHyperClockCache"; }
|
||||
|
||||
void ReportProblems(
|
||||
const std::shared_ptr<Logger>& /*info_log*/) const override;
|
||||
}; // class AutoHyperClockCache
|
||||
}; // class HyperClockCache
|
||||
|
||||
} // namespace clock_cache
|
||||
|
||||
|
||||
Vendored
+48
-144
@@ -11,7 +11,6 @@
|
||||
|
||||
#include "memory/memory_allocator_impl.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
@@ -23,24 +22,17 @@ CompressedSecondaryCache::CompressedSecondaryCache(
|
||||
cache_options_(opts),
|
||||
cache_res_mgr_(std::make_shared<ConcurrentCacheReservationManager>(
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache_))),
|
||||
disable_cache_(opts.capacity == 0) {}
|
||||
cache_))) {}
|
||||
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() {}
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() {
|
||||
assert(cache_res_mgr_->GetTotalReservedCacheSize() == 0);
|
||||
}
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool /*wait*/, bool advise_erase,
|
||||
Statistics* stats, bool& kept_in_sec_cache) {
|
||||
bool& kept_in_sec_cache) {
|
||||
assert(helper);
|
||||
// This is a minor optimization. Its ok to skip it in TSAN in order to
|
||||
// avoid a false positive.
|
||||
#ifndef __SANITIZE_THREAD__
|
||||
if (disable_cache_) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle;
|
||||
kept_in_sec_cache = false;
|
||||
Cache::Handle* lru_handle = cache_->Lookup(key);
|
||||
@@ -51,72 +43,46 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
void* handle_value = cache_->Value(lru_handle);
|
||||
if (handle_value == nullptr) {
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
|
||||
RecordTick(stats, COMPRESSED_SECONDARY_CACHE_DUMMY_HITS);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CacheAllocationPtr* ptr{nullptr};
|
||||
CacheAllocationPtr merged_value;
|
||||
size_t handle_value_charge{0};
|
||||
const char* data_ptr = nullptr;
|
||||
CacheTier source = CacheTier::kVolatileCompressedTier;
|
||||
CompressionType type = cache_options_.compression_type;
|
||||
if (cache_options_.enable_custom_split_merge) {
|
||||
CacheValueChunk* value_chunk_ptr =
|
||||
reinterpret_cast<CacheValueChunk*>(handle_value);
|
||||
merged_value = MergeChunksIntoValue(value_chunk_ptr, handle_value_charge);
|
||||
ptr = &merged_value;
|
||||
data_ptr = ptr->get();
|
||||
} else {
|
||||
uint32_t type_32 = static_cast<uint32_t>(type);
|
||||
uint32_t source_32 = static_cast<uint32_t>(source);
|
||||
ptr = reinterpret_cast<CacheAllocationPtr*>(handle_value);
|
||||
handle_value_charge = cache_->GetCharge(lru_handle);
|
||||
data_ptr = ptr->get();
|
||||
data_ptr = GetVarint32Ptr(data_ptr, data_ptr + 1,
|
||||
static_cast<uint32_t*>(&type_32));
|
||||
type = static_cast<CompressionType>(type_32);
|
||||
data_ptr = GetVarint32Ptr(data_ptr, data_ptr + 1,
|
||||
static_cast<uint32_t*>(&source_32));
|
||||
source = static_cast<CacheTier>(source_32);
|
||||
handle_value_charge -= (data_ptr - ptr->get());
|
||||
}
|
||||
MemoryAllocator* allocator = cache_options_.memory_allocator.get();
|
||||
|
||||
Status s;
|
||||
Cache::ObjectPtr value{nullptr};
|
||||
size_t charge{0};
|
||||
if (source == CacheTier::kVolatileCompressedTier) {
|
||||
if (cache_options_.compression_type == kNoCompression ||
|
||||
cache_options_.do_not_compress_roles.Contains(helper->role)) {
|
||||
s = helper->create_cb(Slice(data_ptr, handle_value_charge),
|
||||
kNoCompression, CacheTier::kVolatileTier,
|
||||
create_context, allocator, &value, &charge);
|
||||
} else {
|
||||
UncompressionContext uncompression_context(
|
||||
cache_options_.compression_type);
|
||||
UncompressionInfo uncompression_info(uncompression_context,
|
||||
UncompressionDict::GetEmptyDict(),
|
||||
cache_options_.compression_type);
|
||||
|
||||
size_t uncompressed_size{0};
|
||||
CacheAllocationPtr uncompressed =
|
||||
UncompressData(uncompression_info, (char*)data_ptr,
|
||||
handle_value_charge, &uncompressed_size,
|
||||
cache_options_.compress_format_version, allocator);
|
||||
|
||||
if (!uncompressed) {
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/true);
|
||||
return nullptr;
|
||||
}
|
||||
s = helper->create_cb(Slice(uncompressed.get(), uncompressed_size),
|
||||
kNoCompression, CacheTier::kVolatileTier,
|
||||
create_context, allocator, &value, &charge);
|
||||
}
|
||||
if (cache_options_.compression_type == kNoCompression ||
|
||||
cache_options_.do_not_compress_roles.Contains(helper->role)) {
|
||||
s = helper->create_cb(Slice(ptr->get(), handle_value_charge),
|
||||
create_context, allocator, &value, &charge);
|
||||
} else {
|
||||
// The item was not compressed by us. Let the helper create_cb
|
||||
// uncompress it
|
||||
s = helper->create_cb(Slice(data_ptr, handle_value_charge), type, source,
|
||||
UncompressionContext uncompression_context(cache_options_.compression_type);
|
||||
UncompressionInfo uncompression_info(uncompression_context,
|
||||
UncompressionDict::GetEmptyDict(),
|
||||
cache_options_.compression_type);
|
||||
|
||||
size_t uncompressed_size{0};
|
||||
CacheAllocationPtr uncompressed = UncompressData(
|
||||
uncompression_info, (char*)ptr->get(), handle_value_charge,
|
||||
&uncompressed_size, cache_options_.compress_format_version, allocator);
|
||||
|
||||
if (!uncompressed) {
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/true);
|
||||
return nullptr;
|
||||
}
|
||||
s = helper->create_cb(Slice(uncompressed.get(), uncompressed_size),
|
||||
create_context, allocator, &value, &charge);
|
||||
}
|
||||
|
||||
@@ -138,67 +104,47 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
|
||||
}
|
||||
handle.reset(new CompressedSecondaryCacheResultHandle(value, charge));
|
||||
RecordTick(stats, COMPRESSED_SECONDARY_CACHE_HITS);
|
||||
return handle;
|
||||
}
|
||||
|
||||
bool CompressedSecondaryCache::MaybeInsertDummy(const Slice& key) {
|
||||
auto internal_helper = GetHelper(cache_options_.enable_custom_split_merge);
|
||||
Status CompressedSecondaryCache::Insert(const Slice& key,
|
||||
Cache::ObjectPtr value,
|
||||
const Cache::CacheItemHelper* helper) {
|
||||
if (value == nullptr) {
|
||||
return Status::InvalidArgument();
|
||||
}
|
||||
|
||||
Cache::Handle* lru_handle = cache_->Lookup(key);
|
||||
auto internal_helper = GetHelper(cache_options_.enable_custom_split_merge);
|
||||
if (lru_handle == nullptr) {
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_insert_dummy_count, 1);
|
||||
// Insert a dummy handle if the handle is evicted for the first time.
|
||||
cache_->Insert(key, /*obj=*/nullptr, internal_helper, /*charge=*/0)
|
||||
.PermitUncheckedError();
|
||||
return true;
|
||||
return cache_->Insert(key, /*obj=*/nullptr, internal_helper,
|
||||
/*charge=*/0);
|
||||
} else {
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Status CompressedSecondaryCache::InsertInternal(
|
||||
const Slice& key, Cache::ObjectPtr value,
|
||||
const Cache::CacheItemHelper* helper, CompressionType type,
|
||||
CacheTier source) {
|
||||
if (source != CacheTier::kVolatileCompressedTier &&
|
||||
cache_options_.enable_custom_split_merge) {
|
||||
// We don't support custom split/merge for the tiered case
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
auto internal_helper = GetHelper(cache_options_.enable_custom_split_merge);
|
||||
char header[10];
|
||||
char* payload = header;
|
||||
payload = EncodeVarint32(payload, static_cast<uint32_t>(type));
|
||||
payload = EncodeVarint32(payload, static_cast<uint32_t>(source));
|
||||
|
||||
size_t header_size = payload - header;
|
||||
size_t data_size = (*helper->size_cb)(value);
|
||||
size_t total_size = data_size + header_size;
|
||||
size_t size = (*helper->size_cb)(value);
|
||||
CacheAllocationPtr ptr =
|
||||
AllocateBlock(total_size, cache_options_.memory_allocator.get());
|
||||
char* data_ptr = ptr.get() + header_size;
|
||||
AllocateBlock(size, cache_options_.memory_allocator.get());
|
||||
|
||||
Status s = (*helper->saveto_cb)(value, 0, data_size, data_ptr);
|
||||
Status s = (*helper->saveto_cb)(value, 0, size, ptr.get());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
Slice val(data_ptr, data_size);
|
||||
Slice val(ptr.get(), size);
|
||||
|
||||
std::string compressed_val;
|
||||
if (cache_options_.compression_type != kNoCompression &&
|
||||
type == kNoCompression &&
|
||||
!cache_options_.do_not_compress_roles.Contains(helper->role)) {
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_uncompressed_bytes, data_size);
|
||||
CompressionContext compression_context(cache_options_.compression_type,
|
||||
cache_options_.compression_opts);
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_uncompressed_bytes, size);
|
||||
CompressionOptions compression_opts;
|
||||
CompressionContext compression_context(cache_options_.compression_type);
|
||||
uint64_t sample_for_compression{0};
|
||||
CompressionInfo compression_info(
|
||||
cache_options_.compression_opts, compression_context,
|
||||
CompressionDict::GetEmptyDict(), cache_options_.compression_type,
|
||||
sample_for_compression);
|
||||
compression_opts, compression_context, CompressionDict::GetEmptyDict(),
|
||||
cache_options_.compression_type, sample_for_compression);
|
||||
|
||||
bool success =
|
||||
CompressData(val, compression_info,
|
||||
@@ -209,14 +155,12 @@ Status CompressedSecondaryCache::InsertInternal(
|
||||
}
|
||||
|
||||
val = Slice(compressed_val);
|
||||
data_size = compressed_val.size();
|
||||
total_size = header_size + data_size;
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_compressed_bytes, data_size);
|
||||
size = compressed_val.size();
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_compressed_bytes, size);
|
||||
|
||||
if (!cache_options_.enable_custom_split_merge) {
|
||||
ptr = AllocateBlock(total_size, cache_options_.memory_allocator.get());
|
||||
data_ptr = ptr.get() + header_size;
|
||||
memcpy(data_ptr, compressed_val.data(), data_size);
|
||||
ptr = AllocateBlock(size, cache_options_.memory_allocator.get());
|
||||
memcpy(ptr.get(), compressed_val.data(), size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,52 +171,17 @@ Status CompressedSecondaryCache::InsertInternal(
|
||||
SplitValueIntoChunks(val, cache_options_.compression_type, charge);
|
||||
return cache_->Insert(key, value_chunks_head, internal_helper, charge);
|
||||
} else {
|
||||
std::memcpy(ptr.get(), header, header_size);
|
||||
CacheAllocationPtr* buf = new CacheAllocationPtr(std::move(ptr));
|
||||
return cache_->Insert(key, buf, internal_helper, total_size);
|
||||
return cache_->Insert(key, buf, internal_helper, size);
|
||||
}
|
||||
}
|
||||
|
||||
Status CompressedSecondaryCache::Insert(const Slice& key,
|
||||
Cache::ObjectPtr value,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
bool force_insert) {
|
||||
if (value == nullptr) {
|
||||
return Status::InvalidArgument();
|
||||
}
|
||||
|
||||
if (!force_insert && MaybeInsertDummy(key)) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
return InsertInternal(key, value, helper, kNoCompression,
|
||||
CacheTier::kVolatileCompressedTier);
|
||||
}
|
||||
|
||||
Status CompressedSecondaryCache::InsertSaved(
|
||||
const Slice& key, const Slice& saved, CompressionType type = kNoCompression,
|
||||
CacheTier source = CacheTier::kVolatileTier) {
|
||||
if (type == kNoCompression) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
auto slice_helper = &kSliceCacheItemHelper;
|
||||
if (MaybeInsertDummy(key)) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
return InsertInternal(
|
||||
key, static_cast<Cache::ObjectPtr>(const_cast<Slice*>(&saved)),
|
||||
slice_helper, type, source);
|
||||
}
|
||||
|
||||
void CompressedSecondaryCache::Erase(const Slice& key) { cache_->Erase(key); }
|
||||
|
||||
Status CompressedSecondaryCache::SetCapacity(size_t capacity) {
|
||||
MutexLock l(&capacity_mutex_);
|
||||
cache_options_.capacity = capacity;
|
||||
cache_->SetCapacity(capacity);
|
||||
disable_cache_ = capacity == 0;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -291,11 +200,6 @@ std::string CompressedSecondaryCache::GetPrintableOptions() const {
|
||||
snprintf(buffer, kBufferSize, " compression_type : %s\n",
|
||||
CompressionTypeToString(cache_options_.compression_type).c_str());
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " compression_opts : %s\n",
|
||||
CompressionOptionsToString(
|
||||
const_cast<CompressionOptions&>(cache_options_.compression_opts))
|
||||
.c_str());
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " compress_format_version : %d\n",
|
||||
cache_options_.compress_format_version);
|
||||
ret.append(buffer);
|
||||
|
||||
Vendored
+2
-13
@@ -77,16 +77,12 @@ class CompressedSecondaryCache : public SecondaryCache {
|
||||
const char* Name() const override { return "CompressedSecondaryCache"; }
|
||||
|
||||
Status Insert(const Slice& key, Cache::ObjectPtr value,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
bool force_insert) override;
|
||||
|
||||
Status InsertSaved(const Slice& key, const Slice& saved, CompressionType type,
|
||||
CacheTier source) override;
|
||||
const Cache::CacheItemHelper* helper) override;
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool /*wait*/, bool advise_erase,
|
||||
Statistics* stats, bool& kept_in_sec_cache) override;
|
||||
bool& kept_in_sec_cache) override;
|
||||
|
||||
bool SupportForceErase() const override { return true; }
|
||||
|
||||
@@ -133,19 +129,12 @@ class CompressedSecondaryCache : public SecondaryCache {
|
||||
CacheAllocationPtr MergeChunksIntoValue(const void* chunks_head,
|
||||
size_t& charge);
|
||||
|
||||
bool MaybeInsertDummy(const Slice& key);
|
||||
|
||||
Status InsertInternal(const Slice& key, Cache::ObjectPtr value,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
CompressionType type, CacheTier source);
|
||||
|
||||
// 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_;
|
||||
CompressedSecondaryCacheOptions cache_options_;
|
||||
mutable port::Mutex capacity_mutex_;
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr_;
|
||||
bool disable_cache_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+41
-344
@@ -12,7 +12,6 @@
|
||||
|
||||
#include "cache/secondary_cache_adapter.h"
|
||||
#include "memory/jemalloc_nodump_allocator.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "test_util/secondary_cache_test_util.h"
|
||||
#include "test_util/testharness.h"
|
||||
@@ -44,7 +43,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
// Lookup an non-existent key.
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle0 =
|
||||
sec_cache->Lookup(key0, GetHelper(), this, true, /*advise_erase=*/true,
|
||||
/*stats=*/nullptr, kept_in_sec_cache);
|
||||
kept_in_sec_cache);
|
||||
ASSERT_EQ(handle0, nullptr);
|
||||
|
||||
Random rnd(301);
|
||||
@@ -52,23 +51,23 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
std::string str1(rnd.RandomString(1000));
|
||||
TestItem item1(str1.data(), str1.length());
|
||||
// A dummy handle is inserted if the item is inserted for the first time.
|
||||
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
|
||||
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper()));
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 1);
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
|
||||
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
|
||||
/*stats=*/nullptr, kept_in_sec_cache);
|
||||
kept_in_sec_cache);
|
||||
ASSERT_EQ(handle1_1, nullptr);
|
||||
|
||||
// Insert and Lookup the item k1 for the second time and advise erasing it.
|
||||
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
|
||||
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper()));
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 1);
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1_2 =
|
||||
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/true,
|
||||
/*stats=*/nullptr, kept_in_sec_cache);
|
||||
kept_in_sec_cache);
|
||||
ASSERT_NE(handle1_2, nullptr);
|
||||
ASSERT_FALSE(kept_in_sec_cache);
|
||||
if (sec_cache_is_compressed) {
|
||||
@@ -89,20 +88,20 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
// Lookup the item k1 again.
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1_3 =
|
||||
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/true,
|
||||
/*stats=*/nullptr, kept_in_sec_cache);
|
||||
kept_in_sec_cache);
|
||||
ASSERT_EQ(handle1_3, nullptr);
|
||||
|
||||
// Insert and Lookup the item k2.
|
||||
std::string str2(rnd.RandomString(1000));
|
||||
TestItem item2(str2.data(), str2.length());
|
||||
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
|
||||
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper()));
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 2);
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle2_1 =
|
||||
sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/false,
|
||||
/*stats=*/nullptr, kept_in_sec_cache);
|
||||
kept_in_sec_cache);
|
||||
ASSERT_EQ(handle2_1, nullptr);
|
||||
|
||||
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
|
||||
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper()));
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 2);
|
||||
if (sec_cache_is_compressed) {
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes,
|
||||
@@ -115,7 +114,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
}
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle2_2 =
|
||||
sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/false,
|
||||
/*stats=*/nullptr, kept_in_sec_cache);
|
||||
kept_in_sec_cache);
|
||||
ASSERT_NE(handle2_2, nullptr);
|
||||
std::unique_ptr<TestItem> val2 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle2_2->Value()));
|
||||
@@ -184,26 +183,26 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
std::string str1(rnd.RandomString(1000));
|
||||
TestItem item1(str1.data(), str1.length());
|
||||
// Insert a dummy handle.
|
||||
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
|
||||
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper()));
|
||||
// Insert k1.
|
||||
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
|
||||
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper()));
|
||||
|
||||
// Insert and Lookup the second item.
|
||||
std::string str2(rnd.RandomString(200));
|
||||
TestItem item2(str2.data(), str2.length());
|
||||
// Insert a dummy handle, k1 is not evicted.
|
||||
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
|
||||
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper()));
|
||||
bool kept_in_sec_cache{false};
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1 =
|
||||
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
|
||||
/*stats=*/nullptr, kept_in_sec_cache);
|
||||
kept_in_sec_cache);
|
||||
ASSERT_EQ(handle1, nullptr);
|
||||
|
||||
// Insert k2 and k1 is evicted.
|
||||
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
|
||||
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper()));
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle2 =
|
||||
sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/false,
|
||||
/*stats=*/nullptr, kept_in_sec_cache);
|
||||
kept_in_sec_cache);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
std::unique_ptr<TestItem> val2 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle2->Value()));
|
||||
@@ -211,26 +210,26 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
ASSERT_EQ(memcmp(val2->Buf(), item2.Buf(), item2.Size()), 0);
|
||||
|
||||
// Insert k1 again and a dummy handle is inserted.
|
||||
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
|
||||
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper()));
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
|
||||
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
|
||||
/*stats=*/nullptr, kept_in_sec_cache);
|
||||
kept_in_sec_cache);
|
||||
ASSERT_EQ(handle1_1, nullptr);
|
||||
|
||||
// Create Fails.
|
||||
SetFailCreate(true);
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle2_1 =
|
||||
sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/true,
|
||||
/*stats=*/nullptr, kept_in_sec_cache);
|
||||
kept_in_sec_cache);
|
||||
ASSERT_EQ(handle2_1, nullptr);
|
||||
|
||||
// Save Fails.
|
||||
std::string str3 = rnd.RandomString(10);
|
||||
TestItem item3(str3.data(), str3.length());
|
||||
// The Status is OK because a dummy handle is inserted.
|
||||
ASSERT_OK(sec_cache->Insert(key3, &item3, GetHelperFail(), false));
|
||||
ASSERT_NOK(sec_cache->Insert(key3, &item3, GetHelperFail(), false));
|
||||
ASSERT_OK(sec_cache->Insert(key3, &item3, GetHelperFail()));
|
||||
ASSERT_NOK(sec_cache->Insert(key3, &item3, GetHelperFail()));
|
||||
|
||||
sec_cache.reset();
|
||||
}
|
||||
@@ -738,7 +737,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
class CompressedSecondaryCacheTest
|
||||
: public CompressedSecondaryCacheTestBase,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
const std::string& Type() const override { return GetParam(); }
|
||||
const std::string& Type() override { return GetParam(); }
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CompressedSecondaryCacheTest,
|
||||
@@ -753,7 +752,7 @@ class CompressedSecCacheTestWithCompressAndAllocatorParam
|
||||
sec_cache_is_compressed_ = std::get<0>(GetParam());
|
||||
use_jemalloc_ = std::get<1>(GetParam());
|
||||
}
|
||||
const std::string& Type() const override { return std::get<2>(GetParam()); }
|
||||
const std::string& Type() override { return std::get<2>(GetParam()); }
|
||||
bool sec_cache_is_compressed_;
|
||||
bool use_jemalloc_;
|
||||
};
|
||||
@@ -774,7 +773,7 @@ class CompressedSecondaryCacheTestWithCompressionParam
|
||||
CompressedSecondaryCacheTestWithCompressionParam() {
|
||||
sec_cache_is_compressed_ = std::get<0>(GetParam());
|
||||
}
|
||||
const std::string& Type() const override { return std::get<1>(GetParam()); }
|
||||
const std::string& Type() override { return std::get<1>(GetParam()); }
|
||||
bool sec_cache_is_compressed_;
|
||||
};
|
||||
|
||||
@@ -905,16 +904,16 @@ TEST_P(CompressedSecondaryCacheTestWithCompressionParam, EntryRoles) {
|
||||
Slice ith_key = Slice(junk.data(), 16);
|
||||
|
||||
get_perf_context()->Reset();
|
||||
ASSERT_OK(sec_cache->Insert(ith_key, &item, GetHelper(role), false));
|
||||
ASSERT_OK(sec_cache->Insert(ith_key, &item, GetHelper(role)));
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 1U);
|
||||
|
||||
ASSERT_OK(sec_cache->Insert(ith_key, &item, GetHelper(role), false));
|
||||
ASSERT_OK(sec_cache->Insert(ith_key, &item, GetHelper(role)));
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 1U);
|
||||
|
||||
bool kept_in_sec_cache{true};
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle = sec_cache->Lookup(
|
||||
ith_key, GetHelper(role), this, true,
|
||||
/*advise_erase=*/true, /*stats=*/nullptr, kept_in_sec_cache);
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle =
|
||||
sec_cache->Lookup(ith_key, GetHelper(role), this, true,
|
||||
/*advise_erase=*/true, kept_in_sec_cache);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
|
||||
// Lookup returns the right data
|
||||
@@ -951,7 +950,7 @@ class CompressedSecCacheTestWithCompressAndSplitParam
|
||||
sec_cache_is_compressed_ = std::get<0>(GetParam());
|
||||
enable_custom_split_merge_ = std::get<1>(GetParam());
|
||||
}
|
||||
const std::string& Type() const override { return std::get<2>(GetParam()); }
|
||||
const std::string& Type() override { return std::get<2>(GetParam()); }
|
||||
bool sec_cache_is_compressed_;
|
||||
bool enable_custom_split_merge_;
|
||||
};
|
||||
@@ -977,58 +976,24 @@ TEST_P(CompressedSecondaryCacheTest, SplictValueAndMergeChunksTest) {
|
||||
SplictValueAndMergeChunksTest();
|
||||
}
|
||||
|
||||
using secondary_cache_test_util::WithCacheType;
|
||||
|
||||
class CompressedSecCacheTestWithTiered
|
||||
: public testing::Test,
|
||||
public WithCacheType,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<PrimaryCacheType, TieredAdmissionPolicy>> {
|
||||
class CompressedSecCacheTestWithTiered : public ::testing::Test {
|
||||
public:
|
||||
using secondary_cache_test_util::WithCacheType::TestItem;
|
||||
CompressedSecCacheTestWithTiered() {
|
||||
LRUCacheOptions lru_opts;
|
||||
HyperClockCacheOptions hcc_opts(
|
||||
/*_capacity=*/0,
|
||||
/*_estimated_entry_charge=*/256 << 10,
|
||||
/*_num_shard_bits=*/0);
|
||||
// eviction_effort_cap setting simply to avoid churn in existing test
|
||||
hcc_opts.eviction_effort_cap = 100;
|
||||
TieredCacheOptions opts;
|
||||
lru_opts.capacity = 0;
|
||||
lru_opts.num_shard_bits = 0;
|
||||
lru_opts.high_pri_pool_ratio = 0;
|
||||
opts.cache_type = std::get<0>(GetParam());
|
||||
if (opts.cache_type == PrimaryCacheType::kCacheTypeLRU) {
|
||||
opts.cache_opts = &lru_opts;
|
||||
} else {
|
||||
opts.cache_opts = &hcc_opts;
|
||||
}
|
||||
opts.adm_policy = std::get<1>(GetParam());
|
||||
;
|
||||
opts.comp_cache_opts.capacity = 0;
|
||||
opts.comp_cache_opts.num_shard_bits = 0;
|
||||
opts.total_capacity = 100 << 20;
|
||||
opts.compressed_secondary_ratio = 0.3;
|
||||
cache_ = NewTieredCache(opts);
|
||||
TieredVolatileCacheOptions opts;
|
||||
lru_opts.capacity = 70 << 20;
|
||||
opts.cache_opts = &lru_opts;
|
||||
opts.cache_type = PrimaryCacheType::kCacheTypeLRU;
|
||||
opts.comp_cache_opts.capacity = 30 << 20;
|
||||
cache_ = NewTieredVolatileCache(opts);
|
||||
cache_res_mgr_ =
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache_);
|
||||
}
|
||||
|
||||
const std::string& Type() const override {
|
||||
if (std::get<0>(GetParam()) == PrimaryCacheType::kCacheTypeLRU) {
|
||||
return lru_str;
|
||||
} else {
|
||||
return hcc_str;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
CacheReservationManager* cache_res_mgr() { return cache_res_mgr_.get(); }
|
||||
|
||||
std::shared_ptr<Cache> GetTieredCache() { return cache_; }
|
||||
|
||||
Cache* GetCache() {
|
||||
return static_cast_with_check<CacheWithSecondaryAdapter, Cache>(
|
||||
cache_.get())
|
||||
@@ -1048,21 +1013,15 @@ class CompressedSecCacheTestWithTiered
|
||||
private:
|
||||
std::shared_ptr<Cache> cache_;
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr_;
|
||||
static std::string lru_str;
|
||||
static std::string hcc_str;
|
||||
};
|
||||
|
||||
std::string CompressedSecCacheTestWithTiered::lru_str(WithCacheType::kLRU);
|
||||
std::string CompressedSecCacheTestWithTiered::hcc_str(
|
||||
WithCacheType::kFixedHyperClock);
|
||||
|
||||
bool CacheUsageWithinBounds(size_t val1, size_t val2, size_t error) {
|
||||
return ((val1 < (val2 + error)) && (val1 > (val2 - error)));
|
||||
}
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered, CacheReservationManager) {
|
||||
TEST_F(CompressedSecCacheTestWithTiered, CacheReservationManager) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
|
||||
// Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
|
||||
// double explicit casts
|
||||
@@ -1082,10 +1041,10 @@ TEST_P(CompressedSecCacheTestWithTiered, CacheReservationManager) {
|
||||
EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
|
||||
}
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered,
|
||||
TEST_F(CompressedSecCacheTestWithTiered,
|
||||
CacheReservationManagerMultipleUpdate) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
@@ -1108,268 +1067,6 @@ TEST_P(CompressedSecCacheTestWithTiered,
|
||||
EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
|
||||
}
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered, AdmissionPolicy) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_BYPASS("This test requires LZ4 support\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Cache* tiered_cache = GetTieredCache().get();
|
||||
Cache* cache = GetCache();
|
||||
std::vector<CacheKey> keys;
|
||||
std::vector<std::string> vals;
|
||||
// Make the item size slightly less than 10MB to ensure we can fit the
|
||||
// expected number of items in the cache
|
||||
int item_size = (10 << 20) - (1 << 18);
|
||||
int i;
|
||||
Random rnd(301);
|
||||
for (i = 0; i < 14; ++i) {
|
||||
keys.emplace_back(CacheKey::CreateUniqueForCacheLifetime(cache));
|
||||
vals.emplace_back(rnd.RandomString(item_size));
|
||||
}
|
||||
|
||||
for (i = 0; i < 7; ++i) {
|
||||
TestItem* item = new TestItem(vals[i].data(), vals[i].length());
|
||||
ASSERT_OK(tiered_cache->Insert(keys[i].AsSlice(), item, GetHelper(),
|
||||
vals[i].length()));
|
||||
}
|
||||
|
||||
Cache::Handle* handle1;
|
||||
handle1 = tiered_cache->Lookup(keys[0].AsSlice(), GetHelper(),
|
||||
/*context*/ this, Cache::Priority::LOW);
|
||||
ASSERT_NE(handle1, nullptr);
|
||||
Cache::Handle* handle2;
|
||||
handle2 = tiered_cache->Lookup(keys[1].AsSlice(), GetHelper(),
|
||||
/*context*/ this, Cache::Priority::LOW);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
tiered_cache->Release(handle1);
|
||||
tiered_cache->Release(handle2);
|
||||
|
||||
// Flush all previous entries out of the primary cache
|
||||
for (i = 7; i < 14; ++i) {
|
||||
TestItem* item = new TestItem(vals[i].data(), vals[i].length());
|
||||
ASSERT_OK(tiered_cache->Insert(keys[i].AsSlice(), item, GetHelper(),
|
||||
vals[i].length()));
|
||||
}
|
||||
// keys 0 and 1 should be found as they had the hit bit set
|
||||
handle1 = tiered_cache->Lookup(keys[0].AsSlice(), GetHelper(),
|
||||
/*context*/ this, Cache::Priority::LOW);
|
||||
ASSERT_NE(handle1, nullptr);
|
||||
handle2 = tiered_cache->Lookup(keys[1].AsSlice(), GetHelper(),
|
||||
/*context*/ this, Cache::Priority::LOW);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
tiered_cache->Release(handle1);
|
||||
tiered_cache->Release(handle2);
|
||||
|
||||
handle1 = tiered_cache->Lookup(keys[2].AsSlice(), GetHelper(),
|
||||
/*context*/ this, Cache::Priority::LOW);
|
||||
ASSERT_EQ(handle1, nullptr);
|
||||
handle1 = tiered_cache->Lookup(keys[3].AsSlice(), GetHelper(),
|
||||
/*context*/ this, Cache::Priority::LOW);
|
||||
ASSERT_EQ(handle1, nullptr);
|
||||
}
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered, DynamicUpdate) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
std::shared_ptr<Cache> tiered_cache = GetTieredCache();
|
||||
|
||||
// Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
|
||||
// double explicit casts
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
size_t sec_capacity;
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (30 << 20));
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, 130 << 20));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (39 << 20),
|
||||
GetPercent(39 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (39 << 20));
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, 70 << 20));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (21 << 20),
|
||||
GetPercent(21 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (21 << 20));
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, 100 << 20));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (30 << 20));
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.4));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (40 << 20),
|
||||
GetPercent(40 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (40 << 20));
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.2));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (20 << 20),
|
||||
GetPercent(20 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (20 << 20));
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 1.0));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (100 << 20),
|
||||
GetPercent(100 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, 100 << 20);
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.0));
|
||||
// Only check usage for LRU cache. HCC shows a 64KB usage for some reason
|
||||
if (std::get<0>(GetParam()) == PrimaryCacheType::kCacheTypeLRU) {
|
||||
ASSERT_EQ(GetCache()->GetUsage(), 0);
|
||||
}
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, 0);
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.3));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (30 << 20));
|
||||
}
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered, DynamicUpdateWithReservation) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
std::shared_ptr<Cache> tiered_cache = GetTieredCache();
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(10 << 20));
|
||||
// Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
|
||||
// double explicit casts
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (37 << 20),
|
||||
GetPercent(37 << 20, 1));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
|
||||
GetPercent(3 << 20, 1));
|
||||
size_t sec_capacity;
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (30 << 20));
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, 70 << 20));
|
||||
// Only check usage for LRU cache. HCC is slightly off for some reason
|
||||
if (std::get<0>(GetParam()) == PrimaryCacheType::kCacheTypeLRU) {
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (28 << 20),
|
||||
GetPercent(28 << 20, 1));
|
||||
}
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
|
||||
GetPercent(3 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (21 << 20));
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, 130 << 20));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (46 << 20),
|
||||
GetPercent(46 << 20, 1));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
|
||||
GetPercent(3 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (39 << 20));
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, 100 << 20));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (37 << 20),
|
||||
GetPercent(37 << 20, 1));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
|
||||
GetPercent(3 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (30 << 20));
|
||||
|
||||
ASSERT_OK(tiered_cache->GetSecondaryCacheCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, 30 << 20);
|
||||
size_t sec_usage;
|
||||
ASSERT_OK(tiered_cache->GetSecondaryCachePinnedUsage(sec_usage));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_usage, 3 << 20,
|
||||
GetPercent(3 << 20, 1));
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.39));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (45 << 20),
|
||||
GetPercent(45 << 20, 1));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (4 << 20),
|
||||
GetPercent(4 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (39 << 20));
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.2));
|
||||
// Only check usage for LRU cache. HCC is slightly off for some reason
|
||||
if (std::get<0>(GetParam()) == PrimaryCacheType::kCacheTypeLRU) {
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (28 << 20),
|
||||
GetPercent(28 << 20, 1));
|
||||
}
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (2 << 20),
|
||||
GetPercent(2 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (20 << 20));
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 1.0));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (100 << 20),
|
||||
GetPercent(100 << 20, 1));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (10 << 20),
|
||||
GetPercent(10 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, 100 << 20);
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.0));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (10 << 20),
|
||||
GetPercent(10 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, 0);
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.3));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (37 << 20),
|
||||
GetPercent(37 << 20, 1));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
|
||||
GetPercent(3 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, 30 << 20);
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(0));
|
||||
}
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered, ReservationOverCapacity) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
std::shared_ptr<Cache> tiered_cache = GetTieredCache();
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(110 << 20));
|
||||
// Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
|
||||
// double explicit casts
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (110 << 20),
|
||||
GetPercent(110 << 20, 1));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
size_t sec_capacity;
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (30 << 20));
|
||||
|
||||
ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.39));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (110 << 20),
|
||||
GetPercent(110 << 20, 1));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (39 << 20),
|
||||
GetPercent(39 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (39 << 20));
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(90 << 20));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (94 << 20),
|
||||
GetPercent(94 << 20, 1));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (35 << 20),
|
||||
GetPercent(35 << 20, 1));
|
||||
ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
|
||||
ASSERT_EQ(sec_capacity, (39 << 20));
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(0));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
CompressedSecCacheTests, CompressedSecCacheTestWithTiered,
|
||||
::testing::Values(
|
||||
std::make_tuple(PrimaryCacheType::kCacheTypeLRU,
|
||||
TieredAdmissionPolicy::kAdmPolicyAllowCacheHits),
|
||||
std::make_tuple(PrimaryCacheType::kCacheTypeHCC,
|
||||
TieredAdmissionPolicy::kAdmPolicyAllowCacheHits)));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
Vendored
+9
-10
@@ -339,8 +339,8 @@ void LRUCacheShard::NotifyEvicted(
|
||||
MemoryAllocator* alloc = table_.GetAllocator();
|
||||
for (LRUHandle* entry : evicted_handles) {
|
||||
if (eviction_callback_ &&
|
||||
eviction_callback_(entry->key(), static_cast<Cache::Handle*>(entry),
|
||||
entry->HasHit())) {
|
||||
eviction_callback_(entry->key(),
|
||||
reinterpret_cast<Cache::Handle*>(entry))) {
|
||||
// Callback took ownership of obj; just free handle
|
||||
free(entry);
|
||||
} else {
|
||||
@@ -505,8 +505,7 @@ bool LRUCacheShard::Release(LRUHandle* e, bool /*useful*/,
|
||||
// Only call eviction callback if we're sure no one requested erasure
|
||||
// FIXME: disabled because of test churn
|
||||
if (false && was_in_cache && !erase_if_last_ref && eviction_callback_ &&
|
||||
eviction_callback_(e->key(), static_cast<Cache::Handle*>(e),
|
||||
e->HasHit())) {
|
||||
eviction_callback_(e->key(), reinterpret_cast<Cache::Handle*>(e))) {
|
||||
// Callback took ownership of obj; just free handle
|
||||
free(e);
|
||||
} else {
|
||||
@@ -660,18 +659,18 @@ LRUCache::LRUCache(const LRUCacheOptions& opts) : ShardedCache(opts) {
|
||||
}
|
||||
|
||||
Cache::ObjectPtr LRUCache::Value(Handle* handle) {
|
||||
auto h = static_cast<const LRUHandle*>(handle);
|
||||
auto h = reinterpret_cast<const LRUHandle*>(handle);
|
||||
return h->value;
|
||||
}
|
||||
|
||||
size_t LRUCache::GetCharge(Handle* handle) const {
|
||||
return static_cast<const LRUHandle*>(handle)->GetCharge(
|
||||
return reinterpret_cast<const LRUHandle*>(handle)->GetCharge(
|
||||
GetShard(0).metadata_charge_policy_);
|
||||
}
|
||||
|
||||
const Cache::CacheItemHelper* LRUCache::GetCacheItemHelper(
|
||||
Handle* handle) const {
|
||||
auto h = static_cast<const LRUHandle*>(handle);
|
||||
auto h = reinterpret_cast<const LRUHandle*>(handle);
|
||||
return h->helper;
|
||||
}
|
||||
|
||||
@@ -713,12 +712,12 @@ std::shared_ptr<Cache> LRUCacheOptions::MakeSharedCache() const {
|
||||
return cache;
|
||||
}
|
||||
|
||||
std::shared_ptr<RowCache> LRUCacheOptions::MakeSharedRowCache() const {
|
||||
std::shared_ptr<GeneralCache> LRUCacheOptions::MakeSharedGeneralCache() const {
|
||||
if (secondary_cache) {
|
||||
// Not allowed for a RowCache
|
||||
// Not allowed for a GeneralCache
|
||||
return nullptr;
|
||||
}
|
||||
// Works while RowCache is an alias for Cache
|
||||
// Works while GeneralCache is an alias for Cache
|
||||
return MakeSharedCache();
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+1
-1
@@ -47,7 +47,7 @@ namespace lru_cache {
|
||||
// LRUCacheShard::Lookup.
|
||||
// While refs > 0, public properties like value and deleter must not change.
|
||||
|
||||
struct LRUHandle : public Cache::Handle {
|
||||
struct LRUHandle {
|
||||
Cache::ObjectPtr value;
|
||||
const Cache::CacheItemHelper* helper;
|
||||
LRUHandle* next_hash;
|
||||
|
||||
Vendored
+212
-365
@@ -47,7 +47,7 @@ class LRUCacheTest : public testing::Test {
|
||||
double low_pri_pool_ratio = 1.0,
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex) {
|
||||
DeleteCache();
|
||||
cache_ = static_cast<LRUCacheShard*>(
|
||||
cache_ = reinterpret_cast<LRUCacheShard*>(
|
||||
port::cacheline_aligned_alloc(sizeof(LRUCacheShard)));
|
||||
new (cache_) LRUCacheShard(capacity, /*strict_capacity_limit=*/false,
|
||||
high_pri_pool_ratio, low_pri_pool_ratio,
|
||||
@@ -371,12 +371,11 @@ TEST_F(LRUCacheTest, EntriesWithPriority) {
|
||||
|
||||
namespace clock_cache {
|
||||
|
||||
template <class ClockCache>
|
||||
class ClockCacheTest : public testing::Test {
|
||||
public:
|
||||
using Shard = typename ClockCache::Shard;
|
||||
using Table = typename Shard::Table;
|
||||
using TableOpts = typename Table::Opts;
|
||||
using Shard = HyperClockCache::Shard;
|
||||
using Table = HyperClockTable;
|
||||
using HandleImpl = Shard::HandleImpl;
|
||||
|
||||
ClockCacheTest() {}
|
||||
~ClockCacheTest() override { DeleteShard(); }
|
||||
@@ -389,12 +388,13 @@ class ClockCacheTest : public testing::Test {
|
||||
}
|
||||
}
|
||||
|
||||
void NewShard(size_t capacity, bool strict_capacity_limit = true,
|
||||
int eviction_effort_cap = 30) {
|
||||
void NewShard(size_t capacity, bool strict_capacity_limit = true) {
|
||||
DeleteShard();
|
||||
shard_ = static_cast<Shard*>(port::cacheline_aligned_alloc(sizeof(Shard)));
|
||||
shard_ =
|
||||
reinterpret_cast<Shard*>(port::cacheline_aligned_alloc(sizeof(Shard)));
|
||||
|
||||
TableOpts opts{1 /*value_size*/, eviction_effort_cap};
|
||||
Table::Opts opts;
|
||||
opts.estimated_value_size = 1;
|
||||
new (shard_)
|
||||
Shard(capacity, strict_capacity_limit, kDontChargeCacheMetadata,
|
||||
/*allocator*/ nullptr, &eviction_callback_, &hash_seed_, opts);
|
||||
@@ -445,20 +445,12 @@ class ClockCacheTest : public testing::Test {
|
||||
return Slice(reinterpret_cast<const char*>(&hashed_key), 16U);
|
||||
}
|
||||
|
||||
// A bad hash function for testing / stressing collision handling
|
||||
static inline UniqueId64x2 TestHashedKey(char key) {
|
||||
// For testing hash near-collision behavior, put the variance in
|
||||
// hashed_key in bits that are unlikely to be used as hash bits.
|
||||
return {(static_cast<uint64_t>(key) << 56) + 1234U, 5678U};
|
||||
}
|
||||
|
||||
// A reasonable hash function, for testing "typical behavior" etc.
|
||||
template <typename T>
|
||||
static inline UniqueId64x2 CheapHash(T i) {
|
||||
return {static_cast<uint64_t>(i) * uint64_t{0x85EBCA77C2B2AE63},
|
||||
static_cast<uint64_t>(i) * uint64_t{0xC2B2AE3D27D4EB4F}};
|
||||
}
|
||||
|
||||
Shard* shard_ = nullptr;
|
||||
|
||||
private:
|
||||
@@ -466,53 +458,43 @@ class ClockCacheTest : public testing::Test {
|
||||
uint32_t hash_seed_ = 0;
|
||||
};
|
||||
|
||||
using ClockCacheTypes =
|
||||
::testing::Types<AutoHyperClockCache, FixedHyperClockCache>;
|
||||
TYPED_TEST_CASE(ClockCacheTest, ClockCacheTypes);
|
||||
|
||||
TYPED_TEST(ClockCacheTest, Misc) {
|
||||
this->NewShard(3);
|
||||
// NOTE: templated base class prevents simple naming of inherited members,
|
||||
// so lots of `this->`
|
||||
auto& shard = *this->shard_;
|
||||
TEST_F(ClockCacheTest, Misc) {
|
||||
NewShard(3);
|
||||
|
||||
// Key size stuff
|
||||
EXPECT_OK(this->InsertWithLen('a', 16));
|
||||
EXPECT_NOK(this->InsertWithLen('b', 15));
|
||||
EXPECT_OK(this->InsertWithLen('b', 16));
|
||||
EXPECT_NOK(this->InsertWithLen('c', 17));
|
||||
EXPECT_NOK(this->InsertWithLen('d', 1000));
|
||||
EXPECT_NOK(this->InsertWithLen('e', 11));
|
||||
EXPECT_NOK(this->InsertWithLen('f', 0));
|
||||
EXPECT_OK(InsertWithLen('a', 16));
|
||||
EXPECT_NOK(InsertWithLen('b', 15));
|
||||
EXPECT_OK(InsertWithLen('b', 16));
|
||||
EXPECT_NOK(InsertWithLen('c', 17));
|
||||
EXPECT_NOK(InsertWithLen('d', 1000));
|
||||
EXPECT_NOK(InsertWithLen('e', 11));
|
||||
EXPECT_NOK(InsertWithLen('f', 0));
|
||||
|
||||
// Some of this is motivated by code coverage
|
||||
std::string wrong_size_key(15, 'x');
|
||||
EXPECT_FALSE(this->Lookup(wrong_size_key, this->TestHashedKey('x')));
|
||||
EXPECT_FALSE(shard.Ref(nullptr));
|
||||
EXPECT_FALSE(shard.Release(nullptr));
|
||||
shard.Erase(wrong_size_key, this->TestHashedKey('x')); // no-op
|
||||
EXPECT_FALSE(Lookup(wrong_size_key, TestHashedKey('x')));
|
||||
EXPECT_FALSE(shard_->Ref(nullptr));
|
||||
EXPECT_FALSE(shard_->Release(nullptr));
|
||||
shard_->Erase(wrong_size_key, TestHashedKey('x')); // no-op
|
||||
}
|
||||
|
||||
TYPED_TEST(ClockCacheTest, Limits) {
|
||||
constexpr size_t kCapacity = 64;
|
||||
this->NewShard(kCapacity, false /*strict_capacity_limit*/);
|
||||
auto& shard = *this->shard_;
|
||||
using HandleImpl = typename ClockCacheTest<TypeParam>::Shard::HandleImpl;
|
||||
|
||||
TEST_F(ClockCacheTest, Limits) {
|
||||
constexpr size_t kCapacity = 3;
|
||||
NewShard(kCapacity, false /*strict_capacity_limit*/);
|
||||
for (bool strict_capacity_limit : {false, true, false}) {
|
||||
SCOPED_TRACE("strict_capacity_limit = " +
|
||||
std::to_string(strict_capacity_limit));
|
||||
|
||||
// Also tests switching between strict limit and not
|
||||
shard.SetStrictCapacityLimit(strict_capacity_limit);
|
||||
shard_->SetStrictCapacityLimit(strict_capacity_limit);
|
||||
|
||||
UniqueId64x2 hkey = this->TestHashedKey('x');
|
||||
UniqueId64x2 hkey = TestHashedKey('x');
|
||||
|
||||
// Single entry charge beyond capacity
|
||||
{
|
||||
Status s = shard.Insert(this->TestKey(hkey), hkey, nullptr /*value*/,
|
||||
&kNoopCacheItemHelper, kCapacity + 2 /*charge*/,
|
||||
nullptr /*handle*/, Cache::Priority::LOW);
|
||||
Status s = shard_->Insert(TestKey(hkey), hkey, nullptr /*value*/,
|
||||
&kNoopCacheItemHelper, 5 /*charge*/,
|
||||
nullptr /*handle*/, Cache::Priority::LOW);
|
||||
if (strict_capacity_limit) {
|
||||
EXPECT_TRUE(s.IsMemoryLimit());
|
||||
} else {
|
||||
@@ -523,11 +505,11 @@ TYPED_TEST(ClockCacheTest, Limits) {
|
||||
// Single entry fills capacity
|
||||
{
|
||||
HandleImpl* h;
|
||||
ASSERT_OK(shard.Insert(this->TestKey(hkey), hkey, nullptr /*value*/,
|
||||
&kNoopCacheItemHelper, kCapacity /*charge*/, &h,
|
||||
Cache::Priority::LOW));
|
||||
ASSERT_OK(shard_->Insert(TestKey(hkey), hkey, nullptr /*value*/,
|
||||
&kNoopCacheItemHelper, 3 /*charge*/, &h,
|
||||
Cache::Priority::LOW));
|
||||
// Try to insert more
|
||||
Status s = this->Insert('a');
|
||||
Status s = Insert('a');
|
||||
if (strict_capacity_limit) {
|
||||
EXPECT_TRUE(s.IsMemoryLimit());
|
||||
} else {
|
||||
@@ -535,22 +517,22 @@ TYPED_TEST(ClockCacheTest, Limits) {
|
||||
}
|
||||
// Release entry filling capacity.
|
||||
// Cover useful = false case.
|
||||
shard.Release(h, false /*useful*/, false /*erase_if_last_ref*/);
|
||||
shard_->Release(h, false /*useful*/, false /*erase_if_last_ref*/);
|
||||
}
|
||||
|
||||
// Insert more than table size can handle to exceed occupancy limit.
|
||||
// (Cleverly using mostly zero-charge entries, but some non-zero to
|
||||
// verify usage tracking on detached entries.)
|
||||
{
|
||||
size_t n = kCapacity * 5 + 1;
|
||||
size_t n = shard_->GetTableAddressCount() + 1;
|
||||
std::unique_ptr<HandleImpl* []> ha { new HandleImpl* [n] {} };
|
||||
Status s;
|
||||
for (size_t i = 0; i < n && s.ok(); ++i) {
|
||||
hkey[1] = i;
|
||||
s = shard.Insert(this->TestKey(hkey), hkey, nullptr /*value*/,
|
||||
&kNoopCacheItemHelper,
|
||||
(i + kCapacity < n) ? 0 : 1 /*charge*/, &ha[i],
|
||||
Cache::Priority::LOW);
|
||||
s = shard_->Insert(TestKey(hkey), hkey, nullptr /*value*/,
|
||||
&kNoopCacheItemHelper,
|
||||
(i + kCapacity < n) ? 0 : 1 /*charge*/, &ha[i],
|
||||
Cache::Priority::LOW);
|
||||
if (i == 0) {
|
||||
EXPECT_OK(s);
|
||||
}
|
||||
@@ -561,180 +543,130 @@ TYPED_TEST(ClockCacheTest, Limits) {
|
||||
EXPECT_OK(s);
|
||||
}
|
||||
// Same result if not keeping a reference
|
||||
s = this->Insert('a');
|
||||
s = Insert('a');
|
||||
if (strict_capacity_limit) {
|
||||
EXPECT_TRUE(s.IsMemoryLimit());
|
||||
} else {
|
||||
EXPECT_OK(s);
|
||||
}
|
||||
|
||||
EXPECT_EQ(shard.GetOccupancyCount(), shard.GetOccupancyLimit());
|
||||
|
||||
// Regardless, we didn't allow table to actually get full
|
||||
EXPECT_LT(shard.GetOccupancyCount(), shard.GetTableAddressCount());
|
||||
EXPECT_LT(shard_->GetOccupancyCount(), shard_->GetTableAddressCount());
|
||||
|
||||
// Release handles
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (ha[i]) {
|
||||
shard.Release(ha[i]);
|
||||
shard_->Release(ha[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(ClockCacheTest, ClockEvictionTest) {
|
||||
TEST_F(ClockCacheTest, ClockEvictionTest) {
|
||||
for (bool strict_capacity_limit : {false, true}) {
|
||||
SCOPED_TRACE("strict_capacity_limit = " +
|
||||
std::to_string(strict_capacity_limit));
|
||||
|
||||
this->NewShard(6, strict_capacity_limit);
|
||||
auto& shard = *this->shard_;
|
||||
EXPECT_OK(this->Insert('a', Cache::Priority::BOTTOM));
|
||||
EXPECT_OK(this->Insert('b', Cache::Priority::LOW));
|
||||
EXPECT_OK(this->Insert('c', Cache::Priority::HIGH));
|
||||
EXPECT_OK(this->Insert('d', Cache::Priority::BOTTOM));
|
||||
EXPECT_OK(this->Insert('e', Cache::Priority::LOW));
|
||||
EXPECT_OK(this->Insert('f', Cache::Priority::HIGH));
|
||||
NewShard(6, strict_capacity_limit);
|
||||
EXPECT_OK(Insert('a', Cache::Priority::BOTTOM));
|
||||
EXPECT_OK(Insert('b', Cache::Priority::LOW));
|
||||
EXPECT_OK(Insert('c', Cache::Priority::HIGH));
|
||||
EXPECT_OK(Insert('d', Cache::Priority::BOTTOM));
|
||||
EXPECT_OK(Insert('e', Cache::Priority::LOW));
|
||||
EXPECT_OK(Insert('f', Cache::Priority::HIGH));
|
||||
|
||||
EXPECT_TRUE(this->Lookup('a', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('b', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('c', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('d', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('e', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('f', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('a', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('b', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('c', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('d', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('e', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('f', /*use*/ false));
|
||||
|
||||
// Ensure bottom are evicted first, even if new entries are low
|
||||
EXPECT_OK(this->Insert('g', Cache::Priority::LOW));
|
||||
EXPECT_OK(this->Insert('h', Cache::Priority::LOW));
|
||||
EXPECT_OK(Insert('g', Cache::Priority::LOW));
|
||||
EXPECT_OK(Insert('h', Cache::Priority::LOW));
|
||||
|
||||
EXPECT_FALSE(this->Lookup('a', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('b', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('c', /*use*/ false));
|
||||
EXPECT_FALSE(this->Lookup('d', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('e', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('f', /*use*/ false));
|
||||
EXPECT_FALSE(Lookup('a', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('b', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('c', /*use*/ false));
|
||||
EXPECT_FALSE(Lookup('d', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('e', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('f', /*use*/ false));
|
||||
// Mark g & h useful
|
||||
EXPECT_TRUE(this->Lookup('g', /*use*/ true));
|
||||
EXPECT_TRUE(this->Lookup('h', /*use*/ true));
|
||||
EXPECT_TRUE(Lookup('g', /*use*/ true));
|
||||
EXPECT_TRUE(Lookup('h', /*use*/ true));
|
||||
|
||||
// Then old LOW entries
|
||||
EXPECT_OK(this->Insert('i', Cache::Priority::LOW));
|
||||
EXPECT_OK(this->Insert('j', Cache::Priority::LOW));
|
||||
EXPECT_OK(Insert('i', Cache::Priority::LOW));
|
||||
EXPECT_OK(Insert('j', Cache::Priority::LOW));
|
||||
|
||||
EXPECT_FALSE(this->Lookup('b', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('c', /*use*/ false));
|
||||
EXPECT_FALSE(this->Lookup('e', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('f', /*use*/ false));
|
||||
EXPECT_FALSE(Lookup('b', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('c', /*use*/ false));
|
||||
EXPECT_FALSE(Lookup('e', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('f', /*use*/ false));
|
||||
// Mark g & h useful once again
|
||||
EXPECT_TRUE(this->Lookup('g', /*use*/ true));
|
||||
EXPECT_TRUE(this->Lookup('h', /*use*/ true));
|
||||
EXPECT_TRUE(this->Lookup('i', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('j', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('g', /*use*/ true));
|
||||
EXPECT_TRUE(Lookup('h', /*use*/ true));
|
||||
EXPECT_TRUE(Lookup('i', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('j', /*use*/ false));
|
||||
|
||||
// Then old HIGH entries
|
||||
EXPECT_OK(this->Insert('k', Cache::Priority::LOW));
|
||||
EXPECT_OK(this->Insert('l', Cache::Priority::LOW));
|
||||
EXPECT_OK(Insert('k', Cache::Priority::LOW));
|
||||
EXPECT_OK(Insert('l', Cache::Priority::LOW));
|
||||
|
||||
EXPECT_FALSE(this->Lookup('c', /*use*/ false));
|
||||
EXPECT_FALSE(this->Lookup('f', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('g', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('h', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('i', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('j', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('k', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('l', /*use*/ false));
|
||||
EXPECT_FALSE(Lookup('c', /*use*/ false));
|
||||
EXPECT_FALSE(Lookup('f', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('g', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('h', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('i', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('j', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('k', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('l', /*use*/ false));
|
||||
|
||||
// Then the (roughly) least recently useful
|
||||
EXPECT_OK(this->Insert('m', Cache::Priority::HIGH));
|
||||
EXPECT_OK(this->Insert('n', Cache::Priority::HIGH));
|
||||
EXPECT_OK(Insert('m', Cache::Priority::HIGH));
|
||||
EXPECT_OK(Insert('n', Cache::Priority::HIGH));
|
||||
|
||||
EXPECT_TRUE(this->Lookup('g', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('h', /*use*/ false));
|
||||
EXPECT_FALSE(this->Lookup('i', /*use*/ false));
|
||||
EXPECT_FALSE(this->Lookup('j', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('k', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('l', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('g', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('h', /*use*/ false));
|
||||
EXPECT_FALSE(Lookup('i', /*use*/ false));
|
||||
EXPECT_FALSE(Lookup('j', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('k', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('l', /*use*/ false));
|
||||
|
||||
// Now try changing capacity down
|
||||
shard.SetCapacity(4);
|
||||
shard_->SetCapacity(4);
|
||||
// Insert to ensure evictions happen
|
||||
EXPECT_OK(this->Insert('o', Cache::Priority::LOW));
|
||||
EXPECT_OK(this->Insert('p', Cache::Priority::LOW));
|
||||
EXPECT_OK(Insert('o', Cache::Priority::LOW));
|
||||
EXPECT_OK(Insert('p', Cache::Priority::LOW));
|
||||
|
||||
EXPECT_FALSE(this->Lookup('g', /*use*/ false));
|
||||
EXPECT_FALSE(this->Lookup('h', /*use*/ false));
|
||||
EXPECT_FALSE(this->Lookup('k', /*use*/ false));
|
||||
EXPECT_FALSE(this->Lookup('l', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('m', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('n', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('o', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('p', /*use*/ false));
|
||||
EXPECT_FALSE(Lookup('g', /*use*/ false));
|
||||
EXPECT_FALSE(Lookup('h', /*use*/ false));
|
||||
EXPECT_FALSE(Lookup('k', /*use*/ false));
|
||||
EXPECT_FALSE(Lookup('l', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('m', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('n', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('o', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('p', /*use*/ false));
|
||||
|
||||
// Now try changing capacity up
|
||||
EXPECT_TRUE(this->Lookup('m', /*use*/ true));
|
||||
EXPECT_TRUE(this->Lookup('n', /*use*/ true));
|
||||
shard.SetCapacity(6);
|
||||
EXPECT_OK(this->Insert('q', Cache::Priority::HIGH));
|
||||
EXPECT_OK(this->Insert('r', Cache::Priority::HIGH));
|
||||
EXPECT_OK(this->Insert('s', Cache::Priority::HIGH));
|
||||
EXPECT_OK(this->Insert('t', Cache::Priority::HIGH));
|
||||
EXPECT_TRUE(Lookup('m', /*use*/ true));
|
||||
EXPECT_TRUE(Lookup('n', /*use*/ true));
|
||||
shard_->SetCapacity(6);
|
||||
EXPECT_OK(Insert('q', Cache::Priority::HIGH));
|
||||
EXPECT_OK(Insert('r', Cache::Priority::HIGH));
|
||||
EXPECT_OK(Insert('s', Cache::Priority::HIGH));
|
||||
EXPECT_OK(Insert('t', Cache::Priority::HIGH));
|
||||
|
||||
EXPECT_FALSE(this->Lookup('o', /*use*/ false));
|
||||
EXPECT_FALSE(this->Lookup('p', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('m', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('n', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('q', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('r', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('s', /*use*/ false));
|
||||
EXPECT_TRUE(this->Lookup('t', /*use*/ false));
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(ClockCacheTest, ClockEvictionEffortCapTest) {
|
||||
using HandleImpl = typename ClockCacheTest<TypeParam>::Shard::HandleImpl;
|
||||
for (bool strict_capacity_limit : {true, false}) {
|
||||
SCOPED_TRACE("strict_capacity_limit = " +
|
||||
std::to_string(strict_capacity_limit));
|
||||
for (int eec : {-42, 0, 1, 10, 100, 1000}) {
|
||||
SCOPED_TRACE("eviction_effort_cap = " + std::to_string(eec));
|
||||
constexpr size_t kCapacity = 1000;
|
||||
// Start with much larger capacity to ensure that we can go way over
|
||||
// capacity without reaching table occupancy limit.
|
||||
this->NewShard(3 * kCapacity, strict_capacity_limit, eec);
|
||||
auto& shard = *this->shard_;
|
||||
shard.SetCapacity(kCapacity);
|
||||
|
||||
// Nearly fill the cache with pinned entries, then add a bunch of
|
||||
// non-pinned entries. eviction_effort_cap should affect how many
|
||||
// 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] {} };
|
||||
for (size_t i = 0; i < 2 * kCount; ++i) {
|
||||
UniqueId64x2 hkey = this->CheapHash(i);
|
||||
ASSERT_OK(shard.Insert(
|
||||
this->TestKey(hkey), hkey, nullptr /*value*/, &kNoopCacheItemHelper,
|
||||
1 /*charge*/, i < kCount ? &ha[i] : nullptr, Cache::Priority::LOW));
|
||||
}
|
||||
|
||||
if (strict_capacity_limit) {
|
||||
// If strict_capacity_limit is enabled, the cache will never exceed its
|
||||
// capacity
|
||||
EXPECT_EQ(shard.GetOccupancyCount(), kCapacity);
|
||||
} else {
|
||||
// Rough inverse relationship between cap and possible memory
|
||||
// explosion, which shows up as increased table occupancy count.
|
||||
int effective_eec = std::max(int{1}, eec) + 1;
|
||||
EXPECT_NEAR(shard.GetOccupancyCount() * 1.0,
|
||||
kCount * (1 + 1.4 / effective_eec),
|
||||
kCount * (0.6 / effective_eec) + 1.0);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < kCount; ++i) {
|
||||
shard.Release(ha[i]);
|
||||
}
|
||||
}
|
||||
EXPECT_FALSE(Lookup('o', /*use*/ false));
|
||||
EXPECT_FALSE(Lookup('p', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('m', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('n', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('q', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('r', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('s', /*use*/ false));
|
||||
EXPECT_TRUE(Lookup('t', /*use*/ false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,178 +682,113 @@ const Cache::CacheItemHelper kDeleteCounterHelper{
|
||||
} // namespace
|
||||
|
||||
// Testing calls to CorrectNearOverflow in Release
|
||||
TYPED_TEST(ClockCacheTest, ClockCounterOverflowTest) {
|
||||
this->NewShard(6, /*strict_capacity_limit*/ false);
|
||||
auto& shard = *this->shard_;
|
||||
using HandleImpl = typename ClockCacheTest<TypeParam>::Shard::HandleImpl;
|
||||
|
||||
TEST_F(ClockCacheTest, ClockCounterOverflowTest) {
|
||||
NewShard(6, /*strict_capacity_limit*/ false);
|
||||
HandleImpl* h;
|
||||
DeleteCounter val;
|
||||
UniqueId64x2 hkey = this->TestHashedKey('x');
|
||||
ASSERT_OK(shard.Insert(this->TestKey(hkey), hkey, &val, &kDeleteCounterHelper,
|
||||
1, &h, Cache::Priority::HIGH));
|
||||
UniqueId64x2 hkey = TestHashedKey('x');
|
||||
ASSERT_OK(shard_->Insert(TestKey(hkey), hkey, &val, &kDeleteCounterHelper, 1,
|
||||
&h, Cache::Priority::HIGH));
|
||||
|
||||
// Some large number outstanding
|
||||
shard.TEST_RefN(h, 123456789);
|
||||
shard_->TEST_RefN(h, 123456789);
|
||||
// Simulate many lookup/ref + release, plenty to overflow counters
|
||||
for (int i = 0; i < 10000; ++i) {
|
||||
shard.TEST_RefN(h, 1234567);
|
||||
shard.TEST_ReleaseN(h, 1234567);
|
||||
shard_->TEST_RefN(h, 1234567);
|
||||
shard_->TEST_ReleaseN(h, 1234567);
|
||||
}
|
||||
// Mark it invisible (to reach a different CorrectNearOverflow() in Release)
|
||||
shard.Erase(this->TestKey(hkey), hkey);
|
||||
shard_->Erase(TestKey(hkey), hkey);
|
||||
// Simulate many more lookup/ref + release (one-by-one would be too
|
||||
// expensive for unit test)
|
||||
for (int i = 0; i < 10000; ++i) {
|
||||
shard.TEST_RefN(h, 1234567);
|
||||
shard.TEST_ReleaseN(h, 1234567);
|
||||
shard_->TEST_RefN(h, 1234567);
|
||||
shard_->TEST_ReleaseN(h, 1234567);
|
||||
}
|
||||
// Free all but last 1
|
||||
shard.TEST_ReleaseN(h, 123456789);
|
||||
shard_->TEST_ReleaseN(h, 123456789);
|
||||
// Still alive
|
||||
ASSERT_EQ(val.deleted, 0);
|
||||
// Free last ref, which will finalize erasure
|
||||
shard.Release(h);
|
||||
shard_->Release(h);
|
||||
// Deleted
|
||||
ASSERT_EQ(val.deleted, 1);
|
||||
}
|
||||
|
||||
TYPED_TEST(ClockCacheTest, ClockTableFull) {
|
||||
// Force clock cache table to fill up (not usually allowed) in order
|
||||
// to test full probe sequence that is theoretically possible due to
|
||||
// parallel operations
|
||||
this->NewShard(6, /*strict_capacity_limit*/ false);
|
||||
auto& shard = *this->shard_;
|
||||
using HandleImpl = typename ClockCacheTest<TypeParam>::Shard::HandleImpl;
|
||||
|
||||
size_t size = shard.GetTableAddressCount();
|
||||
ASSERT_LE(size + 3, 256); // for using char keys
|
||||
// Modify occupancy and capacity limits to attempt insert on full
|
||||
shard.TEST_MutableOccupancyLimit() = size + 100;
|
||||
shard.SetCapacity(size + 100);
|
||||
|
||||
DeleteCounter val;
|
||||
std::vector<HandleImpl*> handles;
|
||||
// NOTE: the three extra insertions should create standalone entries
|
||||
for (size_t i = 0; i < size + 3; ++i) {
|
||||
UniqueId64x2 hkey = this->TestHashedKey(static_cast<char>(i));
|
||||
ASSERT_OK(shard.Insert(this->TestKey(hkey), hkey, &val,
|
||||
&kDeleteCounterHelper, 1, &handles.emplace_back(),
|
||||
Cache::Priority::HIGH));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size + 3; ++i) {
|
||||
UniqueId64x2 hkey = this->TestHashedKey(static_cast<char>(i));
|
||||
HandleImpl* h = shard.Lookup(this->TestKey(hkey), hkey);
|
||||
if (i < size) {
|
||||
ASSERT_NE(h, nullptr);
|
||||
shard.Release(h);
|
||||
} else {
|
||||
// Standalone entries not visible by lookup
|
||||
ASSERT_EQ(h, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size + 3; ++i) {
|
||||
ASSERT_NE(handles[i], nullptr);
|
||||
shard.Release(handles[i]);
|
||||
if (i < size) {
|
||||
// Everything still in cache
|
||||
ASSERT_EQ(val.deleted, 0);
|
||||
} else {
|
||||
// Standalone entries freed on release
|
||||
ASSERT_EQ(val.deleted, i + 1 - size);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = size + 3; i > 0; --i) {
|
||||
UniqueId64x2 hkey = this->TestHashedKey(static_cast<char>(i - 1));
|
||||
shard.Erase(this->TestKey(hkey), hkey);
|
||||
if (i - 1 > size) {
|
||||
ASSERT_EQ(val.deleted, 3);
|
||||
} else {
|
||||
ASSERT_EQ(val.deleted, 3 + size - (i - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This test is mostly to exercise some corner case logic, by forcing two
|
||||
// keys to have the same hash, and more
|
||||
TYPED_TEST(ClockCacheTest, CollidingInsertEraseTest) {
|
||||
this->NewShard(6, /*strict_capacity_limit*/ false);
|
||||
auto& shard = *this->shard_;
|
||||
using HandleImpl = typename ClockCacheTest<TypeParam>::Shard::HandleImpl;
|
||||
|
||||
TEST_F(ClockCacheTest, CollidingInsertEraseTest) {
|
||||
NewShard(6, /*strict_capacity_limit*/ false);
|
||||
DeleteCounter val;
|
||||
UniqueId64x2 hkey1 = this->TestHashedKey('x');
|
||||
Slice key1 = this->TestKey(hkey1);
|
||||
UniqueId64x2 hkey2 = this->TestHashedKey('y');
|
||||
Slice key2 = this->TestKey(hkey2);
|
||||
UniqueId64x2 hkey3 = this->TestHashedKey('z');
|
||||
Slice key3 = this->TestKey(hkey3);
|
||||
UniqueId64x2 hkey1 = TestHashedKey('x');
|
||||
Slice key1 = TestKey(hkey1);
|
||||
UniqueId64x2 hkey2 = TestHashedKey('y');
|
||||
Slice key2 = TestKey(hkey2);
|
||||
UniqueId64x2 hkey3 = TestHashedKey('z');
|
||||
Slice key3 = TestKey(hkey3);
|
||||
HandleImpl* h1;
|
||||
ASSERT_OK(shard.Insert(key1, hkey1, &val, &kDeleteCounterHelper, 1, &h1,
|
||||
Cache::Priority::HIGH));
|
||||
ASSERT_OK(shard_->Insert(key1, hkey1, &val, &kDeleteCounterHelper, 1, &h1,
|
||||
Cache::Priority::HIGH));
|
||||
HandleImpl* h2;
|
||||
ASSERT_OK(shard.Insert(key2, hkey2, &val, &kDeleteCounterHelper, 1, &h2,
|
||||
Cache::Priority::HIGH));
|
||||
ASSERT_OK(shard_->Insert(key2, hkey2, &val, &kDeleteCounterHelper, 1, &h2,
|
||||
Cache::Priority::HIGH));
|
||||
HandleImpl* h3;
|
||||
ASSERT_OK(shard.Insert(key3, hkey3, &val, &kDeleteCounterHelper, 1, &h3,
|
||||
Cache::Priority::HIGH));
|
||||
ASSERT_OK(shard_->Insert(key3, hkey3, &val, &kDeleteCounterHelper, 1, &h3,
|
||||
Cache::Priority::HIGH));
|
||||
|
||||
// Can repeatedly lookup+release despite the hash collision
|
||||
HandleImpl* tmp_h;
|
||||
for (bool erase_if_last_ref : {true, false}) { // but not last ref
|
||||
tmp_h = shard.Lookup(key1, hkey1);
|
||||
tmp_h = shard_->Lookup(key1, hkey1);
|
||||
ASSERT_EQ(h1, tmp_h);
|
||||
ASSERT_FALSE(shard.Release(tmp_h, erase_if_last_ref));
|
||||
ASSERT_FALSE(shard_->Release(tmp_h, erase_if_last_ref));
|
||||
|
||||
tmp_h = shard.Lookup(key2, hkey2);
|
||||
tmp_h = shard_->Lookup(key2, hkey2);
|
||||
ASSERT_EQ(h2, tmp_h);
|
||||
ASSERT_FALSE(shard.Release(tmp_h, erase_if_last_ref));
|
||||
ASSERT_FALSE(shard_->Release(tmp_h, erase_if_last_ref));
|
||||
|
||||
tmp_h = shard.Lookup(key3, hkey3);
|
||||
tmp_h = shard_->Lookup(key3, hkey3);
|
||||
ASSERT_EQ(h3, tmp_h);
|
||||
ASSERT_FALSE(shard.Release(tmp_h, erase_if_last_ref));
|
||||
ASSERT_FALSE(shard_->Release(tmp_h, erase_if_last_ref));
|
||||
}
|
||||
|
||||
// Make h1 invisible
|
||||
shard.Erase(key1, hkey1);
|
||||
shard_->Erase(key1, hkey1);
|
||||
// Redundant erase
|
||||
shard.Erase(key1, hkey1);
|
||||
shard_->Erase(key1, hkey1);
|
||||
|
||||
// All still alive
|
||||
ASSERT_EQ(val.deleted, 0);
|
||||
|
||||
// Invisible to Lookup
|
||||
tmp_h = shard.Lookup(key1, hkey1);
|
||||
tmp_h = shard_->Lookup(key1, hkey1);
|
||||
ASSERT_EQ(nullptr, tmp_h);
|
||||
|
||||
// Can still find h2, h3
|
||||
for (bool erase_if_last_ref : {true, false}) { // but not last ref
|
||||
tmp_h = shard.Lookup(key2, hkey2);
|
||||
tmp_h = shard_->Lookup(key2, hkey2);
|
||||
ASSERT_EQ(h2, tmp_h);
|
||||
ASSERT_FALSE(shard.Release(tmp_h, erase_if_last_ref));
|
||||
ASSERT_FALSE(shard_->Release(tmp_h, erase_if_last_ref));
|
||||
|
||||
tmp_h = shard.Lookup(key3, hkey3);
|
||||
tmp_h = shard_->Lookup(key3, hkey3);
|
||||
ASSERT_EQ(h3, tmp_h);
|
||||
ASSERT_FALSE(shard.Release(tmp_h, erase_if_last_ref));
|
||||
ASSERT_FALSE(shard_->Release(tmp_h, erase_if_last_ref));
|
||||
}
|
||||
|
||||
// Also Insert with invisible entry there
|
||||
ASSERT_OK(shard.Insert(key1, hkey1, &val, &kDeleteCounterHelper, 1, nullptr,
|
||||
Cache::Priority::HIGH));
|
||||
tmp_h = shard.Lookup(key1, hkey1);
|
||||
ASSERT_OK(shard_->Insert(key1, hkey1, &val, &kDeleteCounterHelper, 1, nullptr,
|
||||
Cache::Priority::HIGH));
|
||||
tmp_h = shard_->Lookup(key1, hkey1);
|
||||
// Found but distinct handle
|
||||
ASSERT_NE(nullptr, tmp_h);
|
||||
ASSERT_NE(h1, tmp_h);
|
||||
ASSERT_TRUE(shard.Release(tmp_h, /*erase_if_last_ref*/ true));
|
||||
ASSERT_TRUE(shard_->Release(tmp_h, /*erase_if_last_ref*/ true));
|
||||
|
||||
// tmp_h deleted
|
||||
ASSERT_EQ(val.deleted--, 1);
|
||||
|
||||
// Release last ref on h1 (already invisible)
|
||||
ASSERT_TRUE(shard.Release(h1, /*erase_if_last_ref*/ false));
|
||||
ASSERT_TRUE(shard_->Release(h1, /*erase_if_last_ref*/ false));
|
||||
|
||||
// h1 deleted
|
||||
ASSERT_EQ(val.deleted--, 1);
|
||||
@@ -929,57 +796,57 @@ TYPED_TEST(ClockCacheTest, CollidingInsertEraseTest) {
|
||||
|
||||
// Can still find h2, h3
|
||||
for (bool erase_if_last_ref : {true, false}) { // but not last ref
|
||||
tmp_h = shard.Lookup(key2, hkey2);
|
||||
tmp_h = shard_->Lookup(key2, hkey2);
|
||||
ASSERT_EQ(h2, tmp_h);
|
||||
ASSERT_FALSE(shard.Release(tmp_h, erase_if_last_ref));
|
||||
ASSERT_FALSE(shard_->Release(tmp_h, erase_if_last_ref));
|
||||
|
||||
tmp_h = shard.Lookup(key3, hkey3);
|
||||
tmp_h = shard_->Lookup(key3, hkey3);
|
||||
ASSERT_EQ(h3, tmp_h);
|
||||
ASSERT_FALSE(shard.Release(tmp_h, erase_if_last_ref));
|
||||
ASSERT_FALSE(shard_->Release(tmp_h, erase_if_last_ref));
|
||||
}
|
||||
|
||||
// Release last ref on h2
|
||||
ASSERT_FALSE(shard.Release(h2, /*erase_if_last_ref*/ false));
|
||||
ASSERT_FALSE(shard_->Release(h2, /*erase_if_last_ref*/ false));
|
||||
|
||||
// h2 still not deleted (unreferenced in cache)
|
||||
ASSERT_EQ(val.deleted, 0);
|
||||
|
||||
// Can still find it
|
||||
tmp_h = shard.Lookup(key2, hkey2);
|
||||
tmp_h = shard_->Lookup(key2, hkey2);
|
||||
ASSERT_EQ(h2, tmp_h);
|
||||
|
||||
// Release last ref on h2, with erase
|
||||
ASSERT_TRUE(shard.Release(h2, /*erase_if_last_ref*/ true));
|
||||
ASSERT_TRUE(shard_->Release(h2, /*erase_if_last_ref*/ true));
|
||||
|
||||
// h2 deleted
|
||||
ASSERT_EQ(val.deleted--, 1);
|
||||
tmp_h = shard.Lookup(key2, hkey2);
|
||||
tmp_h = shard_->Lookup(key2, hkey2);
|
||||
ASSERT_EQ(nullptr, tmp_h);
|
||||
|
||||
// Can still find h3
|
||||
for (bool erase_if_last_ref : {true, false}) { // but not last ref
|
||||
tmp_h = shard.Lookup(key3, hkey3);
|
||||
tmp_h = shard_->Lookup(key3, hkey3);
|
||||
ASSERT_EQ(h3, tmp_h);
|
||||
ASSERT_FALSE(shard.Release(tmp_h, erase_if_last_ref));
|
||||
ASSERT_FALSE(shard_->Release(tmp_h, erase_if_last_ref));
|
||||
}
|
||||
|
||||
// Release last ref on h3, without erase
|
||||
ASSERT_FALSE(shard.Release(h3, /*erase_if_last_ref*/ false));
|
||||
ASSERT_FALSE(shard_->Release(h3, /*erase_if_last_ref*/ false));
|
||||
|
||||
// h3 still not deleted (unreferenced in cache)
|
||||
ASSERT_EQ(val.deleted, 0);
|
||||
|
||||
// Explicit erase
|
||||
shard.Erase(key3, hkey3);
|
||||
shard_->Erase(key3, hkey3);
|
||||
|
||||
// h3 deleted
|
||||
ASSERT_EQ(val.deleted--, 1);
|
||||
tmp_h = shard.Lookup(key3, hkey3);
|
||||
tmp_h = shard_->Lookup(key3, hkey3);
|
||||
ASSERT_EQ(nullptr, tmp_h);
|
||||
}
|
||||
|
||||
// This uses the public API to effectively test CalcHashBits etc.
|
||||
TYPED_TEST(ClockCacheTest, TableSizesTest) {
|
||||
TEST_F(ClockCacheTest, TableSizesTest) {
|
||||
for (size_t est_val_size : {1U, 5U, 123U, 2345U, 345678U}) {
|
||||
SCOPED_TRACE("est_val_size = " + std::to_string(est_val_size));
|
||||
for (double est_count : {1.1, 2.2, 511.9, 512.1, 2345.0}) {
|
||||
@@ -992,10 +859,8 @@ TYPED_TEST(ClockCacheTest, TableSizesTest) {
|
||||
/*memory_allocator*/ nullptr, kDontChargeCacheMetadata)
|
||||
.MakeSharedCache();
|
||||
// Table sizes are currently only powers of two
|
||||
EXPECT_GE(cache->GetTableAddressCount(),
|
||||
est_count / FixedHyperClockTable::kLoadFactor);
|
||||
EXPECT_LE(cache->GetTableAddressCount(),
|
||||
est_count / FixedHyperClockTable::kLoadFactor * 2.0);
|
||||
EXPECT_GE(cache->GetTableAddressCount(), est_count / kLoadFactor);
|
||||
EXPECT_LE(cache->GetTableAddressCount(), est_count / kLoadFactor * 2.0);
|
||||
EXPECT_EQ(cache->GetUsage(), 0);
|
||||
|
||||
// kFullChargeMetaData
|
||||
@@ -1012,10 +877,9 @@ TYPED_TEST(ClockCacheTest, TableSizesTest) {
|
||||
double est_count_after_meta =
|
||||
(capacity - cache->GetUsage()) * 1.0 / est_val_size;
|
||||
EXPECT_GE(cache->GetTableAddressCount(),
|
||||
est_count_after_meta / FixedHyperClockTable::kLoadFactor);
|
||||
EXPECT_LE(
|
||||
cache->GetTableAddressCount(),
|
||||
est_count_after_meta / FixedHyperClockTable::kLoadFactor * 2.0);
|
||||
est_count_after_meta / kLoadFactor);
|
||||
EXPECT_LE(cache->GetTableAddressCount(),
|
||||
est_count_after_meta / kLoadFactor * 2.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1038,14 +902,13 @@ class TestSecondaryCache : public SecondaryCache {
|
||||
|
||||
using ResultMap = std::unordered_map<std::string, ResultType>;
|
||||
|
||||
explicit TestSecondaryCache(size_t capacity, bool insert_saved = false)
|
||||
explicit TestSecondaryCache(size_t capacity)
|
||||
: cache_(NewLRUCache(capacity, 0, false, 0.5 /* high_pri_pool_ratio */,
|
||||
nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata)),
|
||||
num_inserts_(0),
|
||||
num_lookups_(0),
|
||||
inject_failure_(false),
|
||||
insert_saved_(insert_saved) {}
|
||||
inject_failure_(false) {}
|
||||
|
||||
const char* Name() const override { return "TestSecondaryCache"; }
|
||||
|
||||
@@ -1054,8 +917,7 @@ class TestSecondaryCache : public SecondaryCache {
|
||||
void ResetInjectFailure() { inject_failure_ = false; }
|
||||
|
||||
Status Insert(const Slice& key, Cache::ObjectPtr value,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
bool /*force_insert*/) override {
|
||||
const Cache::CacheItemHelper* helper) override {
|
||||
if (inject_failure_) {
|
||||
return Status::Corruption("Insertion Data Corrupted");
|
||||
}
|
||||
@@ -1076,22 +938,10 @@ class TestSecondaryCache : public SecondaryCache {
|
||||
return cache_.Insert(key, buf, size);
|
||||
}
|
||||
|
||||
Status InsertSaved(const Slice& key, const Slice& saved,
|
||||
CompressionType /*type*/ = kNoCompression,
|
||||
CacheTier /*source*/ = CacheTier::kVolatileTier) override {
|
||||
if (insert_saved_) {
|
||||
return Insert(key, const_cast<Slice*>(&saved), &kSliceCacheItemHelper,
|
||||
/*force_insert=*/true);
|
||||
} else {
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool /*wait*/,
|
||||
bool /*advise_erase*/, Statistics* /*stats*/,
|
||||
bool& kept_in_sec_cache) override {
|
||||
bool /*advise_erase*/, bool& kept_in_sec_cache) override {
|
||||
std::string key_str = key.ToString();
|
||||
TEST_SYNC_POINT_CALLBACK("TestSecondaryCache::Lookup", &key_str);
|
||||
|
||||
@@ -1116,8 +966,7 @@ class TestSecondaryCache : public SecondaryCache {
|
||||
char* ptr = cache_.Value(handle);
|
||||
size_t size = DecodeFixed64(ptr);
|
||||
ptr += sizeof(uint64_t);
|
||||
s = helper->create_cb(Slice(ptr, size), kNoCompression,
|
||||
CacheTier::kVolatileTier, create_context,
|
||||
s = helper->create_cb(Slice(ptr, size), create_context,
|
||||
/*alloc*/ nullptr, &value, &charge);
|
||||
}
|
||||
if (s.ok()) {
|
||||
@@ -1206,7 +1055,6 @@ class TestSecondaryCache : public SecondaryCache {
|
||||
uint32_t num_inserts_;
|
||||
uint32_t num_lookups_;
|
||||
bool inject_failure_;
|
||||
bool insert_saved_;
|
||||
std::string ckey_prefix_;
|
||||
ResultMap result_map_;
|
||||
};
|
||||
@@ -1237,7 +1085,7 @@ INSTANTIATE_TEST_CASE_P(DBSecondaryCacheTest, DBSecondaryCacheTest,
|
||||
|
||||
TEST_P(BasicSecondaryCacheTest, BasicTest) {
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
std::make_shared<TestSecondaryCache>(4096, true);
|
||||
std::make_shared<TestSecondaryCache>(4096);
|
||||
std::shared_ptr<Cache> cache =
|
||||
NewCache(1024 /* capacity */, 0 /* num_shard_bits */,
|
||||
false /* strict_capacity_limit */, secondary_cache);
|
||||
@@ -1294,7 +1142,7 @@ TEST_P(BasicSecondaryCacheTest, BasicTest) {
|
||||
|
||||
TEST_P(BasicSecondaryCacheTest, StatsTest) {
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
std::make_shared<TestSecondaryCache>(4096, true);
|
||||
std::make_shared<TestSecondaryCache>(4096);
|
||||
std::shared_ptr<Cache> cache =
|
||||
NewCache(1024 /* capacity */, 0 /* num_shard_bits */,
|
||||
false /* strict_capacity_limit */, secondary_cache);
|
||||
@@ -1348,7 +1196,7 @@ TEST_P(BasicSecondaryCacheTest, StatsTest) {
|
||||
|
||||
TEST_P(BasicSecondaryCacheTest, BasicFailTest) {
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
std::make_shared<TestSecondaryCache>(2048, true);
|
||||
std::make_shared<TestSecondaryCache>(2048);
|
||||
std::shared_ptr<Cache> cache =
|
||||
NewCache(1024 /* capacity */, 0 /* num_shard_bits */,
|
||||
false /* strict_capacity_limit */, secondary_cache);
|
||||
@@ -1390,7 +1238,7 @@ TEST_P(BasicSecondaryCacheTest, BasicFailTest) {
|
||||
|
||||
TEST_P(BasicSecondaryCacheTest, SaveFailTest) {
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
std::make_shared<TestSecondaryCache>(2048, true);
|
||||
std::make_shared<TestSecondaryCache>(2048);
|
||||
std::shared_ptr<Cache> cache =
|
||||
NewCache(1024 /* capacity */, 0 /* num_shard_bits */,
|
||||
false /* strict_capacity_limit */, secondary_cache);
|
||||
@@ -1431,7 +1279,7 @@ TEST_P(BasicSecondaryCacheTest, SaveFailTest) {
|
||||
|
||||
TEST_P(BasicSecondaryCacheTest, CreateFailTest) {
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
std::make_shared<TestSecondaryCache>(2048, true);
|
||||
std::make_shared<TestSecondaryCache>(2048);
|
||||
std::shared_ptr<Cache> cache =
|
||||
NewCache(1024 /* capacity */, 0 /* num_shard_bits */,
|
||||
false /* strict_capacity_limit */, secondary_cache);
|
||||
@@ -1472,7 +1320,7 @@ TEST_P(BasicSecondaryCacheTest, CreateFailTest) {
|
||||
TEST_P(BasicSecondaryCacheTest, FullCapacityTest) {
|
||||
for (bool strict_capacity_limit : {false, true}) {
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
std::make_shared<TestSecondaryCache>(2048, true);
|
||||
std::make_shared<TestSecondaryCache>(2048);
|
||||
std::shared_ptr<Cache> cache =
|
||||
NewCache(1024 /* capacity */, 0 /* num_shard_bits */,
|
||||
strict_capacity_limit, secondary_cache);
|
||||
@@ -1519,7 +1367,7 @@ TEST_P(BasicSecondaryCacheTest, FullCapacityTest) {
|
||||
k2.AsSlice(),
|
||||
GetHelper(CacheEntryRole::kDataBlock, /*secondary_compatible=*/false),
|
||||
/*context*/ this, Cache::Priority::LOW);
|
||||
if (strict_capacity_limit || IsHyperClock()) {
|
||||
if (strict_capacity_limit || GetParam() == kHyperClock) {
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
cache->Release(handle2);
|
||||
ASSERT_EQ(secondary_cache->num_inserts(), 1u);
|
||||
@@ -1544,12 +1392,12 @@ TEST_P(BasicSecondaryCacheTest, FullCapacityTest) {
|
||||
// CORRECTION: this is not quite right. block_1 can be inserted into the block
|
||||
// cache because strict_capacity_limit=false, but it is removed from the cache
|
||||
// in Release() because of being over-capacity, without demoting to secondary
|
||||
// cache. FixedHyperClockCache doesn't check capacity on release (for
|
||||
// efficiency) so can demote the over-capacity item to secondary cache. Also, we
|
||||
// intend to add support for demotion in Release, but that currently causes too
|
||||
// much unit test churn.
|
||||
// cache. HyperClockCache doesn't check capacity on release (for efficiency)
|
||||
// so can demote the over-capacity item to secondary cache. Also, we intend to
|
||||
// add support for demotion in Release, but that currently causes too much
|
||||
// unit test churn.
|
||||
TEST_P(DBSecondaryCacheTest, TestSecondaryCacheCorrectness1) {
|
||||
if (IsHyperClock()) {
|
||||
if (GetParam() == kHyperClock) {
|
||||
// See CORRECTION above
|
||||
ROCKSDB_GTEST_BYPASS("Test depends on LRUCache-specific behaviors");
|
||||
return;
|
||||
@@ -1647,7 +1495,7 @@ TEST_P(DBSecondaryCacheTest, TestSecondaryCacheCorrectness1) {
|
||||
// insert and cache block_1 in the block cache (this is the different place
|
||||
// from TestSecondaryCacheCorrectness1)
|
||||
TEST_P(DBSecondaryCacheTest, TestSecondaryCacheCorrectness2) {
|
||||
if (IsHyperClock()) {
|
||||
if (GetParam() == kHyperClock) {
|
||||
ROCKSDB_GTEST_BYPASS("Test depends on LRUCache-specific behaviors");
|
||||
return;
|
||||
}
|
||||
@@ -1835,7 +1683,7 @@ TEST_P(DBSecondaryCacheTest, SecondaryCacheIntensiveTesting) {
|
||||
// if we try to insert block_1 to the block cache, it will always fails. Only
|
||||
// block_2 will be successfully inserted into the block cache.
|
||||
TEST_P(DBSecondaryCacheTest, SecondaryCacheFailureTest) {
|
||||
if (IsHyperClock()) {
|
||||
if (GetParam() == kHyperClock) {
|
||||
ROCKSDB_GTEST_BYPASS("Test depends on LRUCache-specific behaviors");
|
||||
return;
|
||||
}
|
||||
@@ -1945,7 +1793,7 @@ TEST_P(BasicSecondaryCacheTest, BasicWaitAllTest) {
|
||||
str.length()));
|
||||
}
|
||||
// Force all entries to be evicted to the secondary cache
|
||||
if (IsHyperClock()) {
|
||||
if (GetParam() == kHyperClock) {
|
||||
// HCC doesn't respond immediately to SetCapacity
|
||||
for (int i = 9000; i < 9030; ++i) {
|
||||
ASSERT_OK(cache->Insert(ock.WithOffset(i).AsSlice(), nullptr,
|
||||
@@ -2000,7 +1848,7 @@ TEST_P(BasicSecondaryCacheTest, BasicWaitAllTest) {
|
||||
// a sync point callback in TestSecondaryCache::Lookup. We then control the
|
||||
// lookup result by setting the ResultMap.
|
||||
TEST_P(DBSecondaryCacheTest, TestSecondaryCacheMultiGet) {
|
||||
if (IsHyperClock()) {
|
||||
if (GetParam() == kHyperClock) {
|
||||
ROCKSDB_GTEST_BYPASS("Test depends on LRUCache-specific behaviors");
|
||||
return;
|
||||
}
|
||||
@@ -2091,9 +1939,8 @@ class CacheWithStats : public CacheWrapper {
|
||||
|
||||
Status Insert(const Slice& key, Cache::ObjectPtr value,
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
Handle** handle = nullptr, Priority priority = Priority::LOW,
|
||||
const Slice& /*compressed*/ = Slice(),
|
||||
CompressionType /*type*/ = kNoCompression) override {
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override {
|
||||
insert_count_++;
|
||||
return target_->Insert(key, value, helper, charge, handle, priority);
|
||||
}
|
||||
@@ -2186,7 +2033,7 @@ TEST_P(DBSecondaryCacheTest, LRUCacheDumpLoadBasic) {
|
||||
// we have a new cache it is empty, then, before we do the Get, we do the
|
||||
// dumpload
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
std::make_shared<TestSecondaryCache>(2048 * 1024, true);
|
||||
std::make_shared<TestSecondaryCache>(2048 * 1024);
|
||||
// This time with secondary cache
|
||||
base_cache = NewCache(1024 * 1024 /* capacity */, 0 /* num_shard_bits */,
|
||||
false /* strict_capacity_limit */, secondary_cache);
|
||||
@@ -2342,7 +2189,7 @@ TEST_P(DBSecondaryCacheTest, LRUCacheDumpLoadWithFilter) {
|
||||
// we have a new cache it is empty, then, before we do the Get, we do the
|
||||
// dumpload
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
std::make_shared<TestSecondaryCache>(2048 * 1024, true);
|
||||
std::make_shared<TestSecondaryCache>(2048 * 1024);
|
||||
// This time with secondary_cache
|
||||
base_cache = NewCache(1024 * 1024 /* capacity */, 0 /* num_shard_bits */,
|
||||
false /* strict_capacity_limit */, secondary_cache);
|
||||
@@ -2502,7 +2349,7 @@ TEST_P(DBSecondaryCacheTest, TestSecondaryCacheOptionBasic) {
|
||||
// with new options, which set the lowest_used_cache_tier to
|
||||
// kNonVolatileBlockTier. So secondary cache will be used.
|
||||
TEST_P(DBSecondaryCacheTest, TestSecondaryCacheOptionChange) {
|
||||
if (IsHyperClock()) {
|
||||
if (GetParam() == kHyperClock) {
|
||||
ROCKSDB_GTEST_BYPASS("Test depends on LRUCache-specific behaviors");
|
||||
return;
|
||||
}
|
||||
@@ -2597,7 +2444,7 @@ TEST_P(DBSecondaryCacheTest, TestSecondaryCacheOptionChange) {
|
||||
// Two DB test. We create 2 DBs sharing the same block cache and secondary
|
||||
// cache. We diable the secondary cache option for DB2.
|
||||
TEST_P(DBSecondaryCacheTest, TestSecondaryCacheOptionTwoDB) {
|
||||
if (IsHyperClock()) {
|
||||
if (GetParam() == kHyperClock) {
|
||||
ROCKSDB_GTEST_BYPASS("Test depends on LRUCache-specific behaviors");
|
||||
return;
|
||||
}
|
||||
|
||||
Vendored
+32
@@ -9,4 +9,36 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
|
||||
void NoopDelete(Cache::ObjectPtr, MemoryAllocator*) {}
|
||||
|
||||
size_t SliceSize(Cache::ObjectPtr obj) {
|
||||
return static_cast<Slice*>(obj)->size();
|
||||
}
|
||||
|
||||
Status SliceSaveTo(Cache::ObjectPtr from_obj, size_t from_offset, size_t length,
|
||||
char* out) {
|
||||
const Slice& slice = *static_cast<Slice*>(from_obj);
|
||||
std::memcpy(out, slice.data() + from_offset, length);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status FailCreate(const Slice&, Cache::CreateContext*, MemoryAllocator*,
|
||||
Cache::ObjectPtr*, size_t*) {
|
||||
return Status::NotSupported("Only for dumping data into SecondaryCache");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Status SecondaryCache::InsertSaved(const Slice& key, const Slice& saved) {
|
||||
static Cache::CacheItemHelper helper_no_secondary{CacheEntryRole::kMisc,
|
||||
&NoopDelete};
|
||||
static Cache::CacheItemHelper helper{
|
||||
CacheEntryRole::kMisc, &NoopDelete, &SliceSize,
|
||||
&SliceSaveTo, &FailCreate, &helper_no_secondary};
|
||||
// NOTE: depends on Insert() being synchronous, not keeping pointer `&saved`
|
||||
return Insert(key, const_cast<Slice*>(&saved), &helper);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+35
-342
@@ -5,11 +5,7 @@
|
||||
|
||||
#include "cache/secondary_cache_adapter.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "cache/tiered_secondary_cache.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/cast_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -21,7 +17,6 @@ struct Dummy {
|
||||
};
|
||||
const Dummy kDummy{};
|
||||
Cache::ObjectPtr const kDummyObj = const_cast<Dummy*>(&kDummy);
|
||||
const char* kTieredCacheName = "TieredCache";
|
||||
} // namespace
|
||||
|
||||
// When CacheWithSecondaryAdapter is constructed with the distribute_cache_res
|
||||
@@ -78,19 +73,13 @@ const char* kTieredCacheName = "TieredCache";
|
||||
//
|
||||
CacheWithSecondaryAdapter::CacheWithSecondaryAdapter(
|
||||
std::shared_ptr<Cache> target,
|
||||
std::shared_ptr<SecondaryCache> secondary_cache,
|
||||
TieredAdmissionPolicy adm_policy, bool distribute_cache_res)
|
||||
std::shared_ptr<SecondaryCache> secondary_cache, bool distribute_cache_res)
|
||||
: CacheWrapper(std::move(target)),
|
||||
secondary_cache_(std::move(secondary_cache)),
|
||||
adm_policy_(adm_policy),
|
||||
distribute_cache_res_(distribute_cache_res),
|
||||
placeholder_usage_(0),
|
||||
reserved_usage_(0),
|
||||
sec_reserved_(0) {
|
||||
target_->SetEvictionCallback(
|
||||
[this](const Slice& key, Handle* handle, bool was_hit) {
|
||||
return EvictionHandler(key, handle, was_hit);
|
||||
});
|
||||
distribute_cache_res_(distribute_cache_res) {
|
||||
target_->SetEvictionCallback([this](const Slice& key, Handle* handle) {
|
||||
return EvictionHandler(key, handle);
|
||||
});
|
||||
if (distribute_cache_res_) {
|
||||
size_t sec_capacity = 0;
|
||||
pri_cache_res_ = std::make_shared<ConcurrentCacheReservationManager>(
|
||||
@@ -119,27 +108,20 @@ CacheWithSecondaryAdapter::~CacheWithSecondaryAdapter() {
|
||||
size_t sec_capacity = 0;
|
||||
Status s = secondary_cache_->GetCapacity(sec_capacity);
|
||||
assert(s.ok());
|
||||
assert(placeholder_usage_ == 0);
|
||||
assert(reserved_usage_ == 0);
|
||||
assert(pri_cache_res_->GetTotalMemoryUsed() == sec_capacity);
|
||||
assert(pri_cache_res_->GetTotalReservedCacheSize() == sec_capacity);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
|
||||
bool CacheWithSecondaryAdapter::EvictionHandler(const Slice& key,
|
||||
Handle* handle, bool was_hit) {
|
||||
Handle* handle) {
|
||||
auto helper = GetCacheItemHelper(handle);
|
||||
if (helper->IsSecondaryCacheCompatible() &&
|
||||
adm_policy_ != TieredAdmissionPolicy::kAdmPolicyThreeQueue) {
|
||||
if (helper->IsSecondaryCacheCompatible()) {
|
||||
auto obj = target_->Value(handle);
|
||||
// Ignore dummy entry
|
||||
if (obj != kDummyObj) {
|
||||
bool hit = false;
|
||||
if (adm_policy_ == TieredAdmissionPolicy::kAdmPolicyAllowCacheHits) {
|
||||
hit = was_hit;
|
||||
}
|
||||
// Spill into secondary cache.
|
||||
secondary_cache_->Insert(key, obj, helper, hit).PermitUncheckedError();
|
||||
secondary_cache_->Insert(key, obj, helper).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
// Never takes ownership of obj
|
||||
@@ -236,42 +218,14 @@ Cache::Handle* CacheWithSecondaryAdapter::Promote(
|
||||
Status CacheWithSecondaryAdapter::Insert(const Slice& key, ObjectPtr value,
|
||||
const CacheItemHelper* helper,
|
||||
size_t charge, Handle** handle,
|
||||
Priority priority,
|
||||
const Slice& compressed_value,
|
||||
CompressionType type) {
|
||||
Priority priority) {
|
||||
Status s = target_->Insert(key, value, helper, charge, handle, priority);
|
||||
if (s.ok() && value == nullptr && distribute_cache_res_ && handle) {
|
||||
charge = target_->GetCharge(*handle);
|
||||
|
||||
MutexLock l(&cache_res_mutex_);
|
||||
placeholder_usage_ += charge;
|
||||
// Check if total placeholder reservation is more than the overall
|
||||
// cache capacity. If it is, then we don't try to charge the
|
||||
// secondary cache because we don't want to overcharge it (beyond
|
||||
// its capacity).
|
||||
// In order to make this a bit more lightweight, we also check if
|
||||
// the difference between placeholder_usage_ and reserved_usage_ is
|
||||
// atleast kReservationChunkSize and avoid any adjustments if not.
|
||||
if ((placeholder_usage_ <= target_->GetCapacity()) &&
|
||||
((placeholder_usage_ - reserved_usage_) >= kReservationChunkSize)) {
|
||||
reserved_usage_ = placeholder_usage_ & ~(kReservationChunkSize - 1);
|
||||
size_t new_sec_reserved =
|
||||
static_cast<size_t>(reserved_usage_ * sec_cache_res_ratio_);
|
||||
size_t sec_charge = new_sec_reserved - sec_reserved_;
|
||||
s = secondary_cache_->Deflate(sec_charge);
|
||||
assert(s.ok());
|
||||
s = pri_cache_res_->UpdateCacheReservation(sec_charge,
|
||||
/*increase=*/false);
|
||||
assert(s.ok());
|
||||
sec_reserved_ += sec_charge;
|
||||
}
|
||||
}
|
||||
// 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) {
|
||||
Status status = secondary_cache_->InsertSaved(key, compressed_value, type);
|
||||
assert(status.ok() || status.IsNotSupported());
|
||||
if (s.ok() && value == nullptr && distribute_cache_res_) {
|
||||
size_t sec_charge = static_cast<size_t>(charge * (sec_cache_res_ratio_));
|
||||
s = secondary_cache_->Deflate(sec_charge);
|
||||
assert(s.ok());
|
||||
s = pri_cache_res_->UpdateCacheReservation(sec_charge, /*increase=*/false);
|
||||
assert(s.ok());
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -294,8 +248,7 @@ Cache::Handle* CacheWithSecondaryAdapter::Lookup(const Slice& key,
|
||||
bool kept_in_sec_cache = false;
|
||||
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle =
|
||||
secondary_cache_->Lookup(key, helper, create_context, /*wait*/ true,
|
||||
found_dummy_entry, stats,
|
||||
/*out*/ kept_in_sec_cache);
|
||||
found_dummy_entry, /*out*/ kept_in_sec_cache);
|
||||
if (secondary_handle) {
|
||||
result = Promote(std::move(secondary_handle), key, helper, priority,
|
||||
stats, found_dummy_entry, kept_in_sec_cache);
|
||||
@@ -310,27 +263,11 @@ bool CacheWithSecondaryAdapter::Release(Handle* handle,
|
||||
ObjectPtr v = target_->Value(handle);
|
||||
if (v == nullptr && distribute_cache_res_) {
|
||||
size_t charge = target_->GetCharge(handle);
|
||||
|
||||
MutexLock l(&cache_res_mutex_);
|
||||
placeholder_usage_ -= charge;
|
||||
// Check if total placeholder reservation is more than the overall
|
||||
// cache capacity. If it is, then we do nothing as reserved_usage_ must
|
||||
// be already maxed out
|
||||
if ((placeholder_usage_ <= target_->GetCapacity()) &&
|
||||
(placeholder_usage_ < reserved_usage_)) {
|
||||
// Adjust reserved_usage_ in chunks of kReservationChunkSize, so
|
||||
// we don't hit this slow path too often.
|
||||
reserved_usage_ = placeholder_usage_ & ~(kReservationChunkSize - 1);
|
||||
size_t new_sec_reserved =
|
||||
static_cast<size_t>(reserved_usage_ * sec_cache_res_ratio_);
|
||||
size_t sec_charge = sec_reserved_ - new_sec_reserved;
|
||||
Status s = secondary_cache_->Inflate(sec_charge);
|
||||
assert(s.ok());
|
||||
s = pri_cache_res_->UpdateCacheReservation(sec_charge,
|
||||
/*increase=*/true);
|
||||
assert(s.ok());
|
||||
sec_reserved_ -= sec_charge;
|
||||
}
|
||||
size_t sec_charge = static_cast<size_t>(charge * (sec_cache_res_ratio_));
|
||||
Status s = secondary_cache_->Inflate(sec_charge);
|
||||
assert(s.ok());
|
||||
s = pri_cache_res_->UpdateCacheReservation(sec_charge, /*increase=*/true);
|
||||
assert(s.ok());
|
||||
}
|
||||
}
|
||||
return target_->Release(handle, erase_if_last_ref);
|
||||
@@ -349,10 +286,10 @@ void CacheWithSecondaryAdapter::StartAsyncLookupOnMySecondary(
|
||||
assert(async_handle.result_handle == nullptr);
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle =
|
||||
secondary_cache_->Lookup(
|
||||
async_handle.key, async_handle.helper, async_handle.create_context,
|
||||
/*wait*/ false, async_handle.found_dummy_entry, async_handle.stats,
|
||||
/*out*/ async_handle.kept_in_sec_cache);
|
||||
secondary_cache_->Lookup(async_handle.key, async_handle.helper,
|
||||
async_handle.create_context, /*wait*/ false,
|
||||
async_handle.found_dummy_entry,
|
||||
/*out*/ async_handle.kept_in_sec_cache);
|
||||
if (secondary_handle) {
|
||||
// TODO with stacked secondaries: Check & process if already ready?
|
||||
async_handle.pending_handle = secondary_handle.release();
|
||||
@@ -439,7 +376,7 @@ void CacheWithSecondaryAdapter::WaitAll(AsyncLookupHandle* async_handles,
|
||||
for (AsyncLookupHandle* cur : my_pending) {
|
||||
my_secondary_handles.push_back(cur->pending_handle);
|
||||
}
|
||||
secondary_cache_->WaitAll(std::move(my_secondary_handles));
|
||||
secondary_cache_->WaitAll(my_secondary_handles);
|
||||
}
|
||||
|
||||
// Process results
|
||||
@@ -462,279 +399,35 @@ std::string CacheWithSecondaryAdapter::GetPrintableOptions() const {
|
||||
}
|
||||
|
||||
const char* CacheWithSecondaryAdapter::Name() const {
|
||||
if (distribute_cache_res_) {
|
||||
return kTieredCacheName;
|
||||
} else {
|
||||
// To the user, at least for now, configure the underlying cache with
|
||||
// a secondary cache. So we pretend to be that cache
|
||||
return target_->Name();
|
||||
}
|
||||
// To the user, at least for now, configure the underlying cache with
|
||||
// a secondary cache. So we pretend to be that cache
|
||||
return target_->Name();
|
||||
}
|
||||
|
||||
// Update the total cache capacity. If we're distributing cache reservations
|
||||
// to both primary and secondary, then update the pri_cache_res_reservation
|
||||
// as well. At the moment, we don't have a good way of handling the case
|
||||
// where the new capacity < total cache reservations.
|
||||
void CacheWithSecondaryAdapter::SetCapacity(size_t capacity) {
|
||||
size_t sec_capacity = static_cast<size_t>(
|
||||
capacity * (distribute_cache_res_ ? sec_cache_res_ratio_ : 0.0));
|
||||
size_t old_sec_capacity = 0;
|
||||
|
||||
if (distribute_cache_res_) {
|
||||
MutexLock m(&cache_res_mutex_);
|
||||
|
||||
Status s = secondary_cache_->GetCapacity(old_sec_capacity);
|
||||
if (!s.ok()) {
|
||||
return;
|
||||
}
|
||||
if (old_sec_capacity > sec_capacity) {
|
||||
// We're shrinking the cache. We do things in the following order to
|
||||
// avoid a temporary spike in usage over the configured capacity -
|
||||
// 1. Lower the secondary cache capacity
|
||||
// 2. Credit an equal amount (by decreasing pri_cache_res_) to the
|
||||
// primary cache
|
||||
// 3. Decrease the primary cache capacity to the total budget
|
||||
s = secondary_cache_->SetCapacity(sec_capacity);
|
||||
if (s.ok()) {
|
||||
if (placeholder_usage_ > capacity) {
|
||||
// Adjust reserved_usage_ down
|
||||
reserved_usage_ = capacity & ~(kReservationChunkSize - 1);
|
||||
}
|
||||
size_t new_sec_reserved =
|
||||
static_cast<size_t>(reserved_usage_ * sec_cache_res_ratio_);
|
||||
s = pri_cache_res_->UpdateCacheReservation(
|
||||
(old_sec_capacity - sec_capacity) -
|
||||
(sec_reserved_ - new_sec_reserved),
|
||||
/*increase=*/false);
|
||||
sec_reserved_ = new_sec_reserved;
|
||||
assert(s.ok());
|
||||
target_->SetCapacity(capacity);
|
||||
}
|
||||
} else {
|
||||
// We're expanding the cache. Do it in the following order to avoid
|
||||
// unnecessary evictions -
|
||||
// 1. Increase the primary cache capacity to total budget
|
||||
// 2. Reserve additional memory in primary on behalf of secondary (by
|
||||
// increasing pri_cache_res_ reservation)
|
||||
// 3. Increase secondary cache capacity
|
||||
target_->SetCapacity(capacity);
|
||||
s = pri_cache_res_->UpdateCacheReservation(
|
||||
sec_capacity - old_sec_capacity,
|
||||
/*increase=*/true);
|
||||
assert(s.ok());
|
||||
s = secondary_cache_->SetCapacity(sec_capacity);
|
||||
assert(s.ok());
|
||||
}
|
||||
} else {
|
||||
// No cache reservation distribution. Just set the primary cache capacity.
|
||||
target_->SetCapacity(capacity);
|
||||
}
|
||||
}
|
||||
|
||||
Status CacheWithSecondaryAdapter::GetSecondaryCacheCapacity(
|
||||
size_t& size) const {
|
||||
return secondary_cache_->GetCapacity(size);
|
||||
}
|
||||
|
||||
Status CacheWithSecondaryAdapter::GetSecondaryCachePinnedUsage(
|
||||
size_t& size) const {
|
||||
Status s;
|
||||
if (distribute_cache_res_) {
|
||||
MutexLock m(&cache_res_mutex_);
|
||||
size_t capacity = 0;
|
||||
s = secondary_cache_->GetCapacity(capacity);
|
||||
if (s.ok()) {
|
||||
size = capacity - pri_cache_res_->GetTotalMemoryUsed();
|
||||
} else {
|
||||
size = 0;
|
||||
}
|
||||
} else {
|
||||
size = 0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// Update the secondary/primary allocation ratio (remember, the primary
|
||||
// capacity is the total memory budget when distribute_cache_res_ is true).
|
||||
// When the ratio changes, we may accumulate some error in the calculations
|
||||
// for secondary cache inflate/deflate and pri_cache_res_ reservations.
|
||||
// This is due to the rounding of the reservation amount.
|
||||
//
|
||||
// We rely on the current pri_cache_res_ total memory used to estimate the
|
||||
// new secondary cache reservation after the ratio change. For this reason,
|
||||
// once the ratio is lowered to 0.0 (effectively disabling the secondary
|
||||
// cache and pri_cache_res_ total mem used going down to 0), we cannot
|
||||
// increase the ratio and re-enable it, We might remove this limitation
|
||||
// in the future.
|
||||
Status CacheWithSecondaryAdapter::UpdateCacheReservationRatio(
|
||||
double compressed_secondary_ratio) {
|
||||
if (!distribute_cache_res_) {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
MutexLock m(&cache_res_mutex_);
|
||||
size_t pri_capacity = target_->GetCapacity();
|
||||
size_t sec_capacity =
|
||||
static_cast<size_t>(pri_capacity * compressed_secondary_ratio);
|
||||
size_t old_sec_capacity;
|
||||
Status s = secondary_cache_->GetCapacity(old_sec_capacity);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// Calculate the new secondary cache reservation
|
||||
// reserved_usage_ will never be > the cache capacity, so we don't
|
||||
// have to worry about adjusting it here.
|
||||
sec_cache_res_ratio_ = compressed_secondary_ratio;
|
||||
size_t new_sec_reserved =
|
||||
static_cast<size_t>(reserved_usage_ * sec_cache_res_ratio_);
|
||||
if (sec_capacity > old_sec_capacity) {
|
||||
// We're increasing the ratio, thus ending up with a larger secondary
|
||||
// cache and a smaller usable primary cache capacity. Similar to
|
||||
// SetCapacity(), we try to avoid a temporary increase in total usage
|
||||
// beyond the configured capacity -
|
||||
// 1. A higher secondary cache ratio means it gets a higher share of
|
||||
// cache reservations. So first account for that by deflating the
|
||||
// secondary cache
|
||||
// 2. Increase pri_cache_res_ reservation to reflect the new secondary
|
||||
// cache utilization (increase in capacity - increase in share of cache
|
||||
// reservation)
|
||||
// 3. Increase secondary cache capacity
|
||||
s = secondary_cache_->Deflate(new_sec_reserved - sec_reserved_);
|
||||
assert(s.ok());
|
||||
s = pri_cache_res_->UpdateCacheReservation(
|
||||
(sec_capacity - old_sec_capacity) - (new_sec_reserved - sec_reserved_),
|
||||
/*increase=*/true);
|
||||
assert(s.ok());
|
||||
sec_reserved_ = new_sec_reserved;
|
||||
s = secondary_cache_->SetCapacity(sec_capacity);
|
||||
assert(s.ok());
|
||||
} else {
|
||||
// We're shrinking the ratio. Try to avoid unnecessary evictions -
|
||||
// 1. Lower the secondary cache capacity
|
||||
// 2. Decrease pri_cache_res_ reservation to relect lower secondary
|
||||
// cache utilization (decrease in capacity - decrease in share of cache
|
||||
// reservations)
|
||||
// 3. Inflate the secondary cache to give it back the reduction in its
|
||||
// share of cache reservations
|
||||
s = secondary_cache_->SetCapacity(sec_capacity);
|
||||
if (s.ok()) {
|
||||
s = pri_cache_res_->UpdateCacheReservation(
|
||||
(old_sec_capacity - sec_capacity) -
|
||||
(sec_reserved_ - new_sec_reserved),
|
||||
/*increase=*/false);
|
||||
assert(s.ok());
|
||||
s = secondary_cache_->Inflate(sec_reserved_ - new_sec_reserved);
|
||||
assert(s.ok());
|
||||
sec_reserved_ = new_sec_reserved;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Status CacheWithSecondaryAdapter::UpdateAdmissionPolicy(
|
||||
TieredAdmissionPolicy adm_policy) {
|
||||
adm_policy_ = adm_policy;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewTieredCache(const TieredCacheOptions& _opts) {
|
||||
if (!_opts.cache_opts) {
|
||||
std::shared_ptr<Cache> NewTieredVolatileCache(
|
||||
TieredVolatileCacheOptions& opts) {
|
||||
if (!opts.cache_opts) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TieredCacheOptions opts = _opts;
|
||||
{
|
||||
bool valid_adm_policy = true;
|
||||
|
||||
switch (_opts.adm_policy) {
|
||||
case TieredAdmissionPolicy::kAdmPolicyAuto:
|
||||
// Select an appropriate default policy
|
||||
if (opts.adm_policy == TieredAdmissionPolicy::kAdmPolicyAuto) {
|
||||
if (opts.nvm_sec_cache) {
|
||||
opts.adm_policy = TieredAdmissionPolicy::kAdmPolicyThreeQueue;
|
||||
} else {
|
||||
opts.adm_policy = TieredAdmissionPolicy::kAdmPolicyPlaceholder;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TieredAdmissionPolicy::kAdmPolicyPlaceholder:
|
||||
case TieredAdmissionPolicy::kAdmPolicyAllowCacheHits:
|
||||
if (opts.nvm_sec_cache) {
|
||||
valid_adm_policy = false;
|
||||
}
|
||||
break;
|
||||
case TieredAdmissionPolicy::kAdmPolicyThreeQueue:
|
||||
if (!opts.nvm_sec_cache) {
|
||||
valid_adm_policy = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
valid_adm_policy = false;
|
||||
}
|
||||
if (!valid_adm_policy) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> cache;
|
||||
if (opts.cache_type == PrimaryCacheType::kCacheTypeLRU) {
|
||||
LRUCacheOptions cache_opts =
|
||||
*(static_cast_with_check<LRUCacheOptions, ShardedCacheOptions>(
|
||||
opts.cache_opts));
|
||||
cache_opts.capacity = opts.total_capacity;
|
||||
cache_opts.secondary_cache = nullptr;
|
||||
cache_opts.capacity += opts.comp_cache_opts.capacity;
|
||||
cache = cache_opts.MakeSharedCache();
|
||||
} else if (opts.cache_type == PrimaryCacheType::kCacheTypeHCC) {
|
||||
HyperClockCacheOptions cache_opts =
|
||||
*(static_cast_with_check<HyperClockCacheOptions, ShardedCacheOptions>(
|
||||
opts.cache_opts));
|
||||
cache_opts.capacity = opts.total_capacity;
|
||||
cache_opts.secondary_cache = nullptr;
|
||||
cache = cache_opts.MakeSharedCache();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
std::shared_ptr<SecondaryCache> sec_cache;
|
||||
opts.comp_cache_opts.capacity = static_cast<size_t>(
|
||||
opts.total_capacity * opts.compressed_secondary_ratio);
|
||||
sec_cache = NewCompressedSecondaryCache(opts.comp_cache_opts);
|
||||
|
||||
if (opts.nvm_sec_cache) {
|
||||
if (opts.adm_policy == TieredAdmissionPolicy::kAdmPolicyThreeQueue) {
|
||||
sec_cache = std::make_shared<TieredSecondaryCache>(
|
||||
sec_cache, opts.nvm_sec_cache,
|
||||
TieredAdmissionPolicy::kAdmPolicyThreeQueue);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_shared<CacheWithSecondaryAdapter>(
|
||||
cache, sec_cache, opts.adm_policy, /*distribute_cache_res=*/true);
|
||||
}
|
||||
|
||||
Status UpdateTieredCache(const std::shared_ptr<Cache>& cache,
|
||||
int64_t total_capacity,
|
||||
double compressed_secondary_ratio,
|
||||
TieredAdmissionPolicy adm_policy) {
|
||||
if (!cache || strcmp(cache->Name(), kTieredCacheName)) {
|
||||
return Status::InvalidArgument();
|
||||
}
|
||||
CacheWithSecondaryAdapter* tiered_cache =
|
||||
static_cast<CacheWithSecondaryAdapter*>(cache.get());
|
||||
|
||||
Status s;
|
||||
if (total_capacity > 0) {
|
||||
tiered_cache->SetCapacity(total_capacity);
|
||||
}
|
||||
if (compressed_secondary_ratio >= 0.0 && compressed_secondary_ratio <= 1.0) {
|
||||
s = tiered_cache->UpdateCacheReservationRatio(compressed_secondary_ratio);
|
||||
}
|
||||
if (adm_policy < TieredAdmissionPolicy::kAdmPolicyMax) {
|
||||
s = tiered_cache->UpdateAdmissionPolicy(adm_policy);
|
||||
}
|
||||
return s;
|
||||
return std::make_shared<CacheWithSecondaryAdapter>(cache, sec_cache, true);
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+5
-32
@@ -15,17 +15,14 @@ class CacheWithSecondaryAdapter : public CacheWrapper {
|
||||
explicit CacheWithSecondaryAdapter(
|
||||
std::shared_ptr<Cache> target,
|
||||
std::shared_ptr<SecondaryCache> secondary_cache,
|
||||
TieredAdmissionPolicy adm_policy = TieredAdmissionPolicy::kAdmPolicyAuto,
|
||||
bool distribute_cache_res = false);
|
||||
|
||||
~CacheWithSecondaryAdapter() override;
|
||||
|
||||
Status Insert(
|
||||
const Slice& key, ObjectPtr value, const CacheItemHelper* helper,
|
||||
size_t charge, Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW,
|
||||
const Slice& compressed_value = Slice(),
|
||||
CompressionType type = CompressionType::kNoCompression) override;
|
||||
Status Insert(const Slice& key, ObjectPtr value,
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override;
|
||||
|
||||
Handle* Lookup(const Slice& key, const CacheItemHelper* helper,
|
||||
CreateContext* create_context,
|
||||
@@ -45,24 +42,12 @@ class CacheWithSecondaryAdapter : public CacheWrapper {
|
||||
|
||||
const char* Name() const override;
|
||||
|
||||
void SetCapacity(size_t capacity) override;
|
||||
|
||||
Status GetSecondaryCacheCapacity(size_t& size) const override;
|
||||
|
||||
Status GetSecondaryCachePinnedUsage(size_t& size) const override;
|
||||
|
||||
Status UpdateCacheReservationRatio(double ratio);
|
||||
|
||||
Status UpdateAdmissionPolicy(TieredAdmissionPolicy adm_policy);
|
||||
|
||||
Cache* TEST_GetCache() { return target_.get(); }
|
||||
|
||||
SecondaryCache* TEST_GetSecondaryCache() { return secondary_cache_.get(); }
|
||||
|
||||
private:
|
||||
static constexpr size_t kReservationChunkSize = 1 << 20;
|
||||
|
||||
bool EvictionHandler(const Slice& key, Handle* handle, bool was_hit);
|
||||
bool EvictionHandler(const Slice& key, Handle* handle);
|
||||
|
||||
void StartAsyncLookupOnMySecondary(AsyncLookupHandle& async_handle);
|
||||
|
||||
@@ -76,7 +61,6 @@ class CacheWithSecondaryAdapter : public CacheWrapper {
|
||||
void CleanupCacheObject(ObjectPtr obj, const CacheItemHelper* helper);
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache_;
|
||||
TieredAdmissionPolicy adm_policy_;
|
||||
// Whether to proportionally distribute cache memory reservations, i.e
|
||||
// placeholder entries with null value and a non-zero charge, across
|
||||
// the primary and secondary caches.
|
||||
@@ -87,17 +71,6 @@ class CacheWithSecondaryAdapter : public CacheWrapper {
|
||||
// Fraction of a cache memory reservation to be assigned to the secondary
|
||||
// cache
|
||||
double sec_cache_res_ratio_;
|
||||
// Mutex for use when managing cache memory reservations. Should not be used
|
||||
// for other purposes, as it may risk causing deadlocks.
|
||||
mutable port::Mutex cache_res_mutex_;
|
||||
// Total memory reserved by placeholder entriesin the cache
|
||||
size_t placeholder_usage_;
|
||||
// Total placeholoder memory charged to both the primary and secondary
|
||||
// caches. Will be <= placeholder_usage_.
|
||||
size_t reserved_usage_;
|
||||
// Amount of memory reserved in the secondary cache. This should be
|
||||
// reserved_usage_ * sec_cache_res_ratio_ in steady state.
|
||||
size_t sec_reserved_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+1
-11
@@ -38,7 +38,7 @@ uint32_t DetermineSeed(int32_t hash_seed_option) {
|
||||
return GetSliceHash(hostname) & kSeedMask;
|
||||
} else {
|
||||
// Fall back on something stable within the process.
|
||||
return BitwiseAnd(gen.GetBaseUpper(), kSeedMask);
|
||||
return static_cast<uint32_t>(gen.GetBaseUpper()) & kSeedMask;
|
||||
}
|
||||
} else {
|
||||
// for kQuasiRandomHashSeed and fallback
|
||||
@@ -83,16 +83,6 @@ size_t ShardedCacheBase::GetCapacity() const {
|
||||
return capacity_;
|
||||
}
|
||||
|
||||
Status ShardedCacheBase::GetSecondaryCacheCapacity(size_t& size) const {
|
||||
size = 0;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status ShardedCacheBase::GetSecondaryCachePinnedUsage(size_t& size) const {
|
||||
size = 0;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
bool ShardedCacheBase::HasStrictCapacityLimit() const {
|
||||
MutexLock l(&config_mutex_);
|
||||
return strict_capacity_limit_;
|
||||
|
||||
Vendored
+9
-22
@@ -99,8 +99,6 @@ class ShardedCacheBase : public Cache {
|
||||
|
||||
bool HasStrictCapacityLimit() const override;
|
||||
size_t GetCapacity() const override;
|
||||
Status GetSecondaryCacheCapacity(size_t& size) const override;
|
||||
Status GetSecondaryCachePinnedUsage(size_t& size) const override;
|
||||
|
||||
using Cache::GetUsage;
|
||||
size_t GetUsage(Handle* handle) const override;
|
||||
@@ -139,7 +137,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
|
||||
explicit ShardedCache(const ShardedCacheOptions& opts)
|
||||
: ShardedCacheBase(opts),
|
||||
shards_(static_cast<CacheShard*>(port::cacheline_aligned_alloc(
|
||||
shards_(reinterpret_cast<CacheShard*>(port::cacheline_aligned_alloc(
|
||||
sizeof(CacheShard) * GetNumShards()))),
|
||||
destroy_shards_in_dtor_(false) {}
|
||||
|
||||
@@ -172,12 +170,9 @@ class ShardedCache : public ShardedCacheBase {
|
||||
[s_c_l](CacheShard* cs) { cs->SetStrictCapacityLimit(s_c_l); });
|
||||
}
|
||||
|
||||
Status Insert(
|
||||
const Slice& key, ObjectPtr obj, const CacheItemHelper* helper,
|
||||
size_t charge, Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW,
|
||||
const Slice& /*compressed_value*/ = Slice(),
|
||||
CompressionType /*type*/ = CompressionType::kNoCompression) override {
|
||||
Status Insert(const Slice& key, ObjectPtr obj, const CacheItemHelper* helper,
|
||||
size_t charge, Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override {
|
||||
assert(helper);
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
auto h_out = reinterpret_cast<HandleImpl**>(handle);
|
||||
@@ -192,7 +187,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
HandleImpl* result = GetShard(hash).CreateStandalone(
|
||||
key, hash, obj, helper, charge, allow_uncharged);
|
||||
return static_cast<Handle*>(result);
|
||||
return reinterpret_cast<Handle*>(result);
|
||||
}
|
||||
|
||||
Handle* Lookup(const Slice& key, const CacheItemHelper* helper = nullptr,
|
||||
@@ -202,7 +197,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
HandleImpl* result = GetShard(hash).Lookup(key, hash, helper,
|
||||
create_context, priority, stats);
|
||||
return static_cast<Handle*>(result);
|
||||
return reinterpret_cast<Handle*>(result);
|
||||
}
|
||||
|
||||
void Erase(const Slice& key) override {
|
||||
@@ -212,11 +207,11 @@ class ShardedCache : public ShardedCacheBase {
|
||||
|
||||
bool Release(Handle* handle, bool useful,
|
||||
bool erase_if_last_ref = false) override {
|
||||
auto h = static_cast<HandleImpl*>(handle);
|
||||
auto h = reinterpret_cast<HandleImpl*>(handle);
|
||||
return GetShard(h->GetHash()).Release(h, useful, erase_if_last_ref);
|
||||
}
|
||||
bool Ref(Handle* handle) override {
|
||||
auto h = static_cast<HandleImpl*>(handle);
|
||||
auto h = reinterpret_cast<HandleImpl*>(handle);
|
||||
return GetShard(h->GetHash()).Ref(h);
|
||||
}
|
||||
bool Release(Handle* handle, bool erase_if_last_ref = false) override {
|
||||
@@ -259,7 +254,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
} while (remaining_work);
|
||||
}
|
||||
|
||||
void EraseUnRefEntries() override {
|
||||
virtual void EraseUnRefEntries() override {
|
||||
ForEachShard([](CacheShard* cs) { cs->EraseUnRefEntries(); });
|
||||
}
|
||||
|
||||
@@ -278,14 +273,6 @@ class ShardedCache : public ShardedCacheBase {
|
||||
}
|
||||
}
|
||||
|
||||
inline void ForEachShard(
|
||||
const std::function<void(const CacheShard*)>& fn) const {
|
||||
uint32_t num_shards = GetNumShards();
|
||||
for (uint32_t i = 0; i < num_shards; i++) {
|
||||
fn(shards_ + i);
|
||||
}
|
||||
}
|
||||
|
||||
inline size_t SumOverShards(
|
||||
const std::function<size_t(CacheShard&)>& fn) const {
|
||||
uint32_t num_shards = GetNumShards();
|
||||
|
||||
Vendored
-125
@@ -1,125 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "cache/tiered_secondary_cache.h"
|
||||
|
||||
#include "monitoring/statistics_impl.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Creation callback for use in the lookup path. It calls the upper layer
|
||||
// create_cb to create the object, and optionally calls the compressed
|
||||
// secondary cache InsertSaved to save the compressed block. If
|
||||
// advise_erase is set, it means the primary cache wants the block to be
|
||||
// erased in the secondary cache, so we skip calling InsertSaved.
|
||||
//
|
||||
// For the time being, we assume that all blocks in the nvm tier belong to
|
||||
// the primary block cache (i.e CacheTier::kVolatileTier). That can be changed
|
||||
// if we implement demotion from the compressed secondary cache to the nvm
|
||||
// cache in the future.
|
||||
Status TieredSecondaryCache::MaybeInsertAndCreate(
|
||||
const Slice& data, CompressionType type, CacheTier source,
|
||||
Cache::CreateContext* ctx, MemoryAllocator* allocator,
|
||||
Cache::ObjectPtr* out_obj, size_t* out_charge) {
|
||||
TieredSecondaryCache::CreateContext* context =
|
||||
static_cast<TieredSecondaryCache::CreateContext*>(ctx);
|
||||
assert(source == CacheTier::kVolatileTier);
|
||||
if (!context->advise_erase && type != kNoCompression) {
|
||||
// Attempt to insert into compressed secondary cache
|
||||
// TODO: Don't hardcode the source
|
||||
context->comp_sec_cache->InsertSaved(*context->key, data, type, source)
|
||||
.PermitUncheckedError();
|
||||
RecordTick(context->stats, COMPRESSED_SECONDARY_CACHE_PROMOTIONS);
|
||||
} else {
|
||||
RecordTick(context->stats, COMPRESSED_SECONDARY_CACHE_PROMOTION_SKIPS);
|
||||
}
|
||||
// Primary cache will accept the object, so call its helper to create
|
||||
// the object
|
||||
return context->helper->create_cb(data, type, source, context->inner_ctx,
|
||||
allocator, out_obj, out_charge);
|
||||
}
|
||||
|
||||
// The lookup first looks up in the compressed secondary cache. If its a miss,
|
||||
// then the nvm cache lookup is called. The cache item helper and create
|
||||
// context are wrapped in order to intercept the creation callback to make
|
||||
// the decision on promoting to the compressed secondary cache.
|
||||
std::unique_ptr<SecondaryCacheResultHandle> TieredSecondaryCache::Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool wait, bool advise_erase,
|
||||
Statistics* stats, bool& kept_in_sec_cache) {
|
||||
bool dummy = false;
|
||||
std::unique_ptr<SecondaryCacheResultHandle> result =
|
||||
target()->Lookup(key, helper, create_context, wait, advise_erase, stats,
|
||||
/*kept_in_sec_cache=*/dummy);
|
||||
// We never want the item to spill back into the secondary cache
|
||||
kept_in_sec_cache = true;
|
||||
if (result) {
|
||||
assert(result->IsReady());
|
||||
return result;
|
||||
}
|
||||
|
||||
// If wait is true, then we can be a bit more efficient and avoid a memory
|
||||
// allocation for the CReateContext.
|
||||
const Cache::CacheItemHelper* outer_helper =
|
||||
TieredSecondaryCache::GetHelper();
|
||||
if (wait) {
|
||||
TieredSecondaryCache::CreateContext ctx;
|
||||
ctx.key = &key;
|
||||
ctx.advise_erase = advise_erase;
|
||||
ctx.helper = helper;
|
||||
ctx.inner_ctx = create_context;
|
||||
ctx.comp_sec_cache = target();
|
||||
ctx.stats = stats;
|
||||
|
||||
return nvm_sec_cache_->Lookup(key, outer_helper, &ctx, wait, advise_erase,
|
||||
stats, kept_in_sec_cache);
|
||||
}
|
||||
|
||||
// If wait is false, i.e its an async lookup, we have to allocate a result
|
||||
// handle for tracking purposes. Embed the CreateContext inside the handle
|
||||
// so we need only allocate memory once instead of twice.
|
||||
std::unique_ptr<ResultHandle> handle(new ResultHandle());
|
||||
handle->ctx()->key = &key;
|
||||
handle->ctx()->advise_erase = advise_erase;
|
||||
handle->ctx()->helper = helper;
|
||||
handle->ctx()->inner_ctx = create_context;
|
||||
handle->ctx()->comp_sec_cache = target();
|
||||
handle->ctx()->stats = stats;
|
||||
handle->SetInnerHandle(
|
||||
nvm_sec_cache_->Lookup(key, outer_helper, handle->ctx(), wait,
|
||||
advise_erase, stats, kept_in_sec_cache));
|
||||
if (!handle->inner_handle()) {
|
||||
handle.reset();
|
||||
} else {
|
||||
result.reset(handle.release());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Call the nvm cache WaitAll to complete the lookups
|
||||
void TieredSecondaryCache::WaitAll(
|
||||
std::vector<SecondaryCacheResultHandle*> handles) {
|
||||
std::vector<SecondaryCacheResultHandle*> nvm_handles;
|
||||
std::vector<ResultHandle*> my_handles;
|
||||
nvm_handles.reserve(handles.size());
|
||||
for (auto handle : handles) {
|
||||
// The handle could belong to the compressed secondary cache. Skip if
|
||||
// that's the case.
|
||||
if (handle->IsReady()) {
|
||||
continue;
|
||||
}
|
||||
ResultHandle* hdl = static_cast<ResultHandle*>(handle);
|
||||
nvm_handles.push_back(hdl->inner_handle());
|
||||
my_handles.push_back(hdl);
|
||||
}
|
||||
nvm_sec_cache_->WaitAll(nvm_handles);
|
||||
for (auto handle : my_handles) {
|
||||
assert(handle->inner_handle()->IsReady());
|
||||
handle->Complete();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
-158
@@ -1,158 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// A SecondaryCache that implements stacking of a compressed secondary cache
|
||||
// and a non-volatile (local flash) cache. It implements an admission
|
||||
// policy of warming the bottommost tier (local flash) with compressed
|
||||
// blocks from the SST on misses, and on hits in the bottommost tier,
|
||||
// promoting to the compressed and/or primary block cache. The admission
|
||||
// policies of the primary block cache and compressed secondary cache remain
|
||||
// unchanged - promote on second access. There is no demotion ofablocks
|
||||
// evicted from a tier. They are just discarded.
|
||||
//
|
||||
// In order to properly handle compressed blocks directly read from SSTs, and
|
||||
// to allow writeback of blocks compressed by the compressed secondary
|
||||
// cache in the future, we make use of the compression type and source
|
||||
// cache tier arguments in InsertSaved.
|
||||
class TieredSecondaryCache : public SecondaryCacheWrapper {
|
||||
public:
|
||||
TieredSecondaryCache(std::shared_ptr<SecondaryCache> comp_sec_cache,
|
||||
std::shared_ptr<SecondaryCache> nvm_sec_cache,
|
||||
TieredAdmissionPolicy adm_policy)
|
||||
: SecondaryCacheWrapper(comp_sec_cache), nvm_sec_cache_(nvm_sec_cache) {
|
||||
#ifndef NDEBUG
|
||||
assert(adm_policy == TieredAdmissionPolicy::kAdmPolicyThreeQueue);
|
||||
#else
|
||||
(void)adm_policy;
|
||||
#endif
|
||||
}
|
||||
|
||||
~TieredSecondaryCache() override {}
|
||||
|
||||
const char* Name() const override { return "TieredSecondaryCache"; }
|
||||
|
||||
// This is a no-op as we currently don't allow demotion (i.e
|
||||
// insertion by the upper layer) of evicted blocks.
|
||||
Status Insert(const Slice& /*key*/, Cache::ObjectPtr /*obj*/,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
bool /*force_insert*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Warm up the nvm tier directly
|
||||
Status InsertSaved(const Slice& key, const Slice& saved,
|
||||
CompressionType type = CompressionType::kNoCompression,
|
||||
CacheTier source = CacheTier::kVolatileTier) override {
|
||||
return nvm_sec_cache_->InsertSaved(key, saved, type, source);
|
||||
}
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool wait, bool advise_erase,
|
||||
Statistics* stats, bool& kept_in_sec_cache) override;
|
||||
|
||||
void WaitAll(std::vector<SecondaryCacheResultHandle*> handles) override;
|
||||
|
||||
private:
|
||||
struct CreateContext : public Cache::CreateContext {
|
||||
const Slice* key;
|
||||
bool advise_erase;
|
||||
const Cache::CacheItemHelper* helper;
|
||||
Cache::CreateContext* inner_ctx;
|
||||
std::shared_ptr<SecondaryCacheResultHandle> inner_handle;
|
||||
SecondaryCache* comp_sec_cache;
|
||||
Statistics* stats;
|
||||
};
|
||||
|
||||
class ResultHandle : public SecondaryCacheResultHandle {
|
||||
public:
|
||||
~ResultHandle() override {}
|
||||
|
||||
bool IsReady() override {
|
||||
if (inner_handle_ && inner_handle_->IsReady()) {
|
||||
Complete();
|
||||
}
|
||||
return ready_;
|
||||
}
|
||||
|
||||
void Wait() override {
|
||||
inner_handle_->Wait();
|
||||
Complete();
|
||||
}
|
||||
|
||||
size_t Size() override { return size_; }
|
||||
|
||||
Cache::ObjectPtr Value() override { return value_; }
|
||||
|
||||
void Complete() {
|
||||
size_ = inner_handle_->Size();
|
||||
value_ = inner_handle_->Value();
|
||||
inner_handle_.reset();
|
||||
ready_ = true;
|
||||
}
|
||||
|
||||
void SetInnerHandle(std::unique_ptr<SecondaryCacheResultHandle>&& handle) {
|
||||
inner_handle_ = std::move(handle);
|
||||
}
|
||||
|
||||
void SetSize(size_t size) { size_ = size; }
|
||||
|
||||
void SetValue(Cache::ObjectPtr val) { value_ = val; }
|
||||
|
||||
CreateContext* ctx() { return &ctx_; }
|
||||
|
||||
SecondaryCacheResultHandle* inner_handle() { return inner_handle_.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<SecondaryCacheResultHandle> inner_handle_;
|
||||
CreateContext ctx_;
|
||||
size_t size_;
|
||||
Cache::ObjectPtr value_;
|
||||
bool ready_ = false;
|
||||
};
|
||||
|
||||
static void NoopDelete(Cache::ObjectPtr /*obj*/,
|
||||
MemoryAllocator* /*allocator*/) {
|
||||
assert(false);
|
||||
}
|
||||
static size_t ZeroSize(Cache::ObjectPtr /*obj*/) {
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
static Status NoopSaveTo(Cache::ObjectPtr /*from_obj*/,
|
||||
size_t /*from_offset*/, size_t /*length*/,
|
||||
char* /*out_buf*/) {
|
||||
assert(false);
|
||||
return Status::OK();
|
||||
}
|
||||
static Status MaybeInsertAndCreate(const Slice& data, CompressionType type,
|
||||
CacheTier source,
|
||||
Cache::CreateContext* ctx,
|
||||
MemoryAllocator* allocator,
|
||||
Cache::ObjectPtr* out_obj,
|
||||
size_t* out_charge);
|
||||
|
||||
static const Cache::CacheItemHelper* GetHelper() {
|
||||
const static Cache::CacheItemHelper basic_helper(CacheEntryRole::kMisc,
|
||||
&NoopDelete);
|
||||
const static Cache::CacheItemHelper maybe_insert_and_create_helper{
|
||||
CacheEntryRole::kMisc, &NoopDelete, &ZeroSize,
|
||||
&NoopSaveTo, &MaybeInsertAndCreate, &basic_helper,
|
||||
};
|
||||
return &maybe_insert_and_create_helper;
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache> comp_sec_cache_;
|
||||
std::shared_ptr<SecondaryCache> nvm_sec_cache_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
-831
@@ -1,831 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
#include "cache/compressed_secondary_cache.h"
|
||||
#include "cache/secondary_cache_adapter.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "typed_cache.h"
|
||||
#include "util/random.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class TestSecondaryCache : public SecondaryCache {
|
||||
public:
|
||||
explicit TestSecondaryCache(size_t capacity, bool ready_before_wait)
|
||||
: cache_(NewLRUCache(capacity, 0, false, 0.5 /* high_pri_pool_ratio */,
|
||||
nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata)),
|
||||
ready_before_wait_(ready_before_wait),
|
||||
num_insert_saved_(0),
|
||||
num_hits_(0),
|
||||
num_misses_(0) {}
|
||||
|
||||
const char* Name() const override { return "TestSecondaryCache"; }
|
||||
|
||||
Status Insert(const Slice& /*key*/, Cache::ObjectPtr /*value*/,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
bool /*force_insert*/) override {
|
||||
assert(false);
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
Status InsertSaved(const Slice& key, const Slice& saved,
|
||||
CompressionType type = kNoCompression,
|
||||
CacheTier source = CacheTier::kVolatileTier) override {
|
||||
CheckCacheKeyCommonPrefix(key);
|
||||
size_t size;
|
||||
char* buf;
|
||||
Status s;
|
||||
|
||||
num_insert_saved_++;
|
||||
size = saved.size();
|
||||
buf = new char[size + sizeof(uint64_t) + 2 * sizeof(uint16_t)];
|
||||
EncodeFixed64(buf, size);
|
||||
buf += sizeof(uint64_t);
|
||||
EncodeFixed16(buf, type);
|
||||
buf += sizeof(uint16_t);
|
||||
EncodeFixed16(buf, (uint16_t)source);
|
||||
buf += sizeof(uint16_t);
|
||||
memcpy(buf, saved.data(), size);
|
||||
buf -= sizeof(uint64_t) + 2 * sizeof(uint16_t);
|
||||
if (!s.ok()) {
|
||||
delete[] buf;
|
||||
return s;
|
||||
}
|
||||
return cache_.Insert(key, buf, size);
|
||||
}
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool wait, bool /*advise_erase*/,
|
||||
Statistics* /*stats*/, bool& kept_in_sec_cache) override {
|
||||
std::string key_str = key.ToString();
|
||||
TEST_SYNC_POINT_CALLBACK("TestSecondaryCache::Lookup", &key_str);
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle;
|
||||
kept_in_sec_cache = false;
|
||||
|
||||
TypedHandle* handle = cache_.Lookup(key);
|
||||
if (handle) {
|
||||
num_hits_++;
|
||||
Cache::ObjectPtr value = nullptr;
|
||||
size_t charge = 0;
|
||||
Status s;
|
||||
char* ptr = cache_.Value(handle);
|
||||
CompressionType type;
|
||||
CacheTier source;
|
||||
size_t size = DecodeFixed64(ptr);
|
||||
ptr += sizeof(uint64_t);
|
||||
type = static_cast<CompressionType>(DecodeFixed16(ptr));
|
||||
ptr += sizeof(uint16_t);
|
||||
source = static_cast<CacheTier>(DecodeFixed16(ptr));
|
||||
assert(source == CacheTier::kVolatileTier);
|
||||
ptr += sizeof(uint16_t);
|
||||
s = helper->create_cb(Slice(ptr, size), type, source, create_context,
|
||||
/*alloc*/ nullptr, &value, &charge);
|
||||
if (s.ok()) {
|
||||
secondary_handle.reset(new TestSecondaryCacheResultHandle(
|
||||
cache_.get(), handle, value, charge,
|
||||
/*ready=*/wait || ready_before_wait_));
|
||||
kept_in_sec_cache = true;
|
||||
} else {
|
||||
cache_.Release(handle);
|
||||
}
|
||||
} else {
|
||||
num_misses_++;
|
||||
}
|
||||
return secondary_handle;
|
||||
}
|
||||
|
||||
bool SupportForceErase() const override { return false; }
|
||||
|
||||
void Erase(const Slice& /*key*/) override {}
|
||||
|
||||
void WaitAll(std::vector<SecondaryCacheResultHandle*> handles) override {
|
||||
for (SecondaryCacheResultHandle* handle : handles) {
|
||||
TestSecondaryCacheResultHandle* sec_handle =
|
||||
static_cast<TestSecondaryCacheResultHandle*>(handle);
|
||||
EXPECT_FALSE(sec_handle->IsReady());
|
||||
sec_handle->SetReady();
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetPrintableOptions() const override { return ""; }
|
||||
|
||||
uint32_t num_insert_saved() { return num_insert_saved_; }
|
||||
|
||||
uint32_t num_hits() { return num_hits_; }
|
||||
|
||||
uint32_t num_misses() { return num_misses_; }
|
||||
|
||||
void CheckCacheKeyCommonPrefix(const Slice& key) {
|
||||
Slice current_prefix(key.data(), OffsetableCacheKey::kCommonPrefixSize);
|
||||
if (ckey_prefix_.empty()) {
|
||||
ckey_prefix_ = current_prefix.ToString();
|
||||
} else {
|
||||
EXPECT_EQ(ckey_prefix_, current_prefix.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
class TestSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
|
||||
public:
|
||||
TestSecondaryCacheResultHandle(Cache* cache, Cache::Handle* handle,
|
||||
Cache::ObjectPtr value, size_t size,
|
||||
bool ready)
|
||||
: cache_(cache),
|
||||
handle_(handle),
|
||||
value_(value),
|
||||
size_(size),
|
||||
is_ready_(ready) {}
|
||||
|
||||
~TestSecondaryCacheResultHandle() override { cache_->Release(handle_); }
|
||||
|
||||
bool IsReady() override { return is_ready_; }
|
||||
|
||||
void Wait() override {}
|
||||
|
||||
Cache::ObjectPtr Value() override {
|
||||
assert(is_ready_);
|
||||
return value_;
|
||||
}
|
||||
|
||||
size_t Size() override { return Value() ? size_ : 0; }
|
||||
|
||||
void SetReady() { is_ready_ = true; }
|
||||
|
||||
private:
|
||||
Cache* cache_;
|
||||
Cache::Handle* handle_;
|
||||
Cache::ObjectPtr value_;
|
||||
size_t size_;
|
||||
bool is_ready_;
|
||||
};
|
||||
|
||||
using SharedCache =
|
||||
BasicTypedSharedCacheInterface<char[], CacheEntryRole::kMisc>;
|
||||
using TypedHandle = SharedCache::TypedHandle;
|
||||
SharedCache cache_;
|
||||
bool ready_before_wait_;
|
||||
uint32_t num_insert_saved_;
|
||||
uint32_t num_hits_;
|
||||
uint32_t num_misses_;
|
||||
std::string ckey_prefix_;
|
||||
};
|
||||
|
||||
class DBTieredSecondaryCacheTest : public DBTestBase {
|
||||
public:
|
||||
DBTieredSecondaryCacheTest()
|
||||
: DBTestBase("db_tiered_secondary_cache_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
std::shared_ptr<Cache> NewCache(
|
||||
size_t pri_capacity, size_t compressed_capacity, size_t nvm_capacity,
|
||||
TieredAdmissionPolicy adm_policy = TieredAdmissionPolicy::kAdmPolicyAuto,
|
||||
bool ready_before_wait = false) {
|
||||
LRUCacheOptions lru_opts;
|
||||
TieredCacheOptions opts;
|
||||
lru_opts.capacity = 0;
|
||||
lru_opts.num_shard_bits = 0;
|
||||
lru_opts.high_pri_pool_ratio = 0;
|
||||
opts.cache_opts = &lru_opts;
|
||||
opts.cache_type = PrimaryCacheType::kCacheTypeLRU;
|
||||
opts.comp_cache_opts.capacity = 0;
|
||||
opts.comp_cache_opts.num_shard_bits = 0;
|
||||
opts.total_capacity = pri_capacity + compressed_capacity;
|
||||
opts.compressed_secondary_ratio = compressed_secondary_ratio_ =
|
||||
(double)compressed_capacity / opts.total_capacity;
|
||||
if (nvm_capacity > 0) {
|
||||
nvm_sec_cache_.reset(
|
||||
new TestSecondaryCache(nvm_capacity, ready_before_wait));
|
||||
opts.nvm_sec_cache = nvm_sec_cache_;
|
||||
}
|
||||
opts.adm_policy = adm_policy;
|
||||
cache_ = NewTieredCache(opts);
|
||||
assert(cache_ != nullptr);
|
||||
|
||||
return cache_;
|
||||
}
|
||||
|
||||
void ClearPrimaryCache() {
|
||||
ASSERT_EQ(UpdateTieredCache(cache_, -1, 1.0), Status::OK());
|
||||
ASSERT_EQ(UpdateTieredCache(cache_, -1, compressed_secondary_ratio_),
|
||||
Status::OK());
|
||||
}
|
||||
|
||||
TestSecondaryCache* nvm_sec_cache() { return nvm_sec_cache_.get(); }
|
||||
|
||||
CompressedSecondaryCache* compressed_secondary_cache() {
|
||||
return static_cast<CompressedSecondaryCache*>(
|
||||
static_cast<CacheWithSecondaryAdapter*>(cache_.get())
|
||||
->TEST_GetSecondaryCache());
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Cache> cache_;
|
||||
std::shared_ptr<TestSecondaryCache> nvm_sec_cache_;
|
||||
double compressed_secondary_ratio_;
|
||||
};
|
||||
|
||||
// In this test, the block size is set to 4096. Each value is 1007 bytes, so
|
||||
// each data block contains exactly 4 KV pairs. Metadata blocks are not
|
||||
// cached, so we can accurately estimate the cache usage.
|
||||
TEST_F(DBTieredSecondaryCacheTest, BasicTest) {
|
||||
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.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;
|
||||
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());
|
||||
|
||||
// The first 2 Gets, for keys 0 and 5, will load the corresponding data
|
||||
// blocks as they will be cache misses. The nvm secondary cache will be
|
||||
// warmed up with the compressed blocks
|
||||
std::string v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 1u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 1u);
|
||||
|
||||
v = Get(Key(5));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
|
||||
// At this point, the nvm cache is warmed up with the data blocks for 0
|
||||
// and 5. The next Get will lookup the block in nvm and will be a hit.
|
||||
// It will be created as a standalone entry in memory, and a placeholder
|
||||
// will be inserted in the primary and compressed caches.
|
||||
v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 1u);
|
||||
|
||||
// For this Get, the primary and compressed only have placeholders for
|
||||
// the required data block. So we will lookup the nvm cache and find the
|
||||
// block there. This time, the block will be promoted to the primary
|
||||
// block cache. No promotion to the compressed secondary cache happens,
|
||||
// and it will retain the placeholder.
|
||||
v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 2u);
|
||||
|
||||
// This Get will find the data block in the primary cache.
|
||||
v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 2u);
|
||||
|
||||
// We repeat the sequence for key 5. This will end up evicting the block
|
||||
// for 0 from the in-memory cache.
|
||||
v = Get(Key(5));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 3u);
|
||||
|
||||
v = Get(Key(5));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
|
||||
|
||||
v = Get(Key(5));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
|
||||
|
||||
// This Get for key 0 will find the data block in nvm. Since the compressed
|
||||
// cache still has the placeholder, the block (compressed) will be
|
||||
// admitted. It is theh inserted into the primary as a standalone entry.
|
||||
v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 5u);
|
||||
|
||||
// This Get for key 0 will find the data block in the compressed secondary
|
||||
// cache.
|
||||
v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 5u);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
// This test is very similar to BasicTest, except it calls MultiGet rather
|
||||
// than Get, in order to exercise the async lookup and WaitAll path.
|
||||
TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewCache(260 * 1024, 10 * 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.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
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());
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(12));
|
||||
keys.push_back(Key(16));
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 3u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 6u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 6u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(12));
|
||||
keys.push_back(Key(16));
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 9u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(12));
|
||||
keys.push_back(Key(16));
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 12u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(12));
|
||||
keys.push_back(Key(16));
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 12u);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewCache(250 * 1024, 20 * 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.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
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());
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(12));
|
||||
keys.push_back(Key(16));
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
|
||||
// Insert placeholders for 4 in primary and compressed
|
||||
std::string val = Get(Key(4));
|
||||
|
||||
// Force placeholder 4 out of primary
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(24));
|
||||
keys.push_back(Key(28));
|
||||
keys.push_back(Key(32));
|
||||
keys.push_back(Key(36));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 10u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 10u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 1u);
|
||||
|
||||
// Now read 4 again. This will create a placeholder in primary, and insert
|
||||
// in compressed secondary since it already has a placeholder
|
||||
val = Get(Key(4));
|
||||
|
||||
// Now read 0, 4 and 8. While 4 is already in the compressed secondary
|
||||
// cache, 0 and 8 will be read asynchronously from the nvm tier. The
|
||||
// WaitAll will be called for all 3 blocks.
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 10u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 10u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewCache(250 * 1024, 20 * 1024, 256 * 1024,
|
||||
TieredAdmissionPolicy::kAdmPolicyAuto,
|
||||
/*ready_before_wait=*/true);
|
||||
table_options.block_size = 4 * 1024;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
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());
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 3u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(12));
|
||||
keys.push_back(Key(16));
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 6u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 3u);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 6u);
|
||||
|
||||
ClearPrimaryCache();
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(32));
|
||||
keys.push_back(Key(36));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 8u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(32));
|
||||
keys.push_back(Key(36));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 8u);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
// This test is for iteration. It iterates through a set of keys in two
|
||||
// passes. First pass loads the compressed blocks into the nvm tier, and
|
||||
// the second pass should hit all of those blocks.
|
||||
TEST_F(DBTieredSecondaryCacheTest, IterateTest) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewCache(250 * 1024, 10 * 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.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
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());
|
||||
|
||||
ReadOptions ro;
|
||||
ro.readahead_size = 256 * 1024;
|
||||
auto iter = dbfull()->NewIterator(ro);
|
||||
iter->SeekToFirst();
|
||||
for (int i = 0; i < 31; ++i) {
|
||||
ASSERT_EQ(Key(i), iter->key().ToString());
|
||||
ASSERT_EQ(1007, iter->value().size());
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
delete iter;
|
||||
|
||||
iter = dbfull()->NewIterator(ro);
|
||||
iter->SeekToFirst();
|
||||
for (int i = 0; i < 31; ++i) {
|
||||
ASSERT_EQ(Key(i), iter->key().ToString());
|
||||
ASSERT_EQ(1007, iter->value().size());
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 8u);
|
||||
delete iter;
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
class DBTieredAdmPolicyTest
|
||||
: public DBTieredSecondaryCacheTest,
|
||||
public testing::WithParamInterface<TieredAdmissionPolicy> {};
|
||||
|
||||
TEST_P(DBTieredAdmPolicyTest, CompressedOnlyTest) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
// We want a block cache of size 10KB, and a compressed secondary cache of
|
||||
// size 10KB. 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 + 10 = 266KB,
|
||||
// and 256KB will be reserved for the compressed cache, leaving 10KB 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, 10 * 1024, 0, GetParam());
|
||||
table_options.block_size = 4 * 1024;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
size_t comp_cache_usage = compressed_secondary_cache()->TEST_GetUsage();
|
||||
// Disable paranoid_file_checks so that flush will not read back the newly
|
||||
// written file
|
||||
options.paranoid_file_checks = false;
|
||||
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());
|
||||
|
||||
// The first 2 Gets, for keys 0 and 5, will load the corresponding data
|
||||
// blocks as they will be cache misses. Since this is a 2-tier cache (
|
||||
// primary and compressed), no warm-up should happen with the compressed
|
||||
// blocks.
|
||||
std::string v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
|
||||
v = Get(Key(5));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
|
||||
ASSERT_EQ(compressed_secondary_cache()->TEST_GetUsage(), comp_cache_usage);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTieredAdmPolicyTest, DBTieredAdmPolicyTest,
|
||||
::testing::Values(TieredAdmissionPolicy::kAdmPolicyAuto,
|
||||
TieredAdmissionPolicy::kAdmPolicyPlaceholder,
|
||||
TieredAdmissionPolicy::kAdmPolicyAllowCacheHits));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Vendored
+15
-20
@@ -155,7 +155,7 @@ class BasicTypedCacheInterface : public BaseCacheInterface<CachePtr>,
|
||||
using BaseCacheInterface<CachePtr>::BaseCacheInterface;
|
||||
struct TypedAsyncLookupHandle : public Cache::AsyncLookupHandle {
|
||||
TypedHandle* Result() {
|
||||
return static_cast<TypedHandle*>(Cache::AsyncLookupHandle::Result());
|
||||
return reinterpret_cast<TypedHandle*>(Cache::AsyncLookupHandle::Result());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -169,7 +169,8 @@ class BasicTypedCacheInterface : public BaseCacheInterface<CachePtr>,
|
||||
}
|
||||
|
||||
inline TypedHandle* Lookup(const Slice& key, Statistics* stats = nullptr) {
|
||||
return static_cast<TypedHandle*>(this->cache_->BasicLookup(key, stats));
|
||||
return reinterpret_cast<TypedHandle*>(
|
||||
this->cache_->BasicLookup(key, stats));
|
||||
}
|
||||
|
||||
inline void StartAsyncLookup(TypedAsyncLookupHandle& async_handle) {
|
||||
@@ -233,19 +234,15 @@ class FullTypedCacheHelperFns : public BasicTypedCacheHelperFns<TValue> {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static Status Create(const Slice& data, CompressionType type,
|
||||
CacheTier source, CreateContext* context,
|
||||
static Status Create(const Slice& data, CreateContext* context,
|
||||
MemoryAllocator* allocator, ObjectPtr* out_obj,
|
||||
size_t* out_charge) {
|
||||
std::unique_ptr<TValue> value = nullptr;
|
||||
if (source != CacheTier::kVolatileTier) {
|
||||
return Status::InvalidArgument();
|
||||
}
|
||||
if constexpr (sizeof(TCreateContext) > 0) {
|
||||
TCreateContext* tcontext = static_cast<TCreateContext*>(context);
|
||||
tcontext->Create(&value, out_charge, data, type, allocator);
|
||||
tcontext->Create(&value, out_charge, data, allocator);
|
||||
} else {
|
||||
TCreateContext::Create(&value, out_charge, data, type, allocator);
|
||||
TCreateContext::Create(&value, out_charge, data, allocator);
|
||||
}
|
||||
*out_obj = UpCastValue(value.release());
|
||||
return Status::OK();
|
||||
@@ -304,15 +301,13 @@ class FullTypedCacheInterface
|
||||
inline Status InsertFull(
|
||||
const Slice& key, TValuePtr value, size_t charge,
|
||||
TypedHandle** handle = nullptr, Priority priority = Priority::LOW,
|
||||
CacheTier lowest_used_cache_tier = CacheTier::kNonVolatileBlockTier,
|
||||
const Slice& compressed = Slice(),
|
||||
CompressionType type = CompressionType::kNoCompression) {
|
||||
CacheTier lowest_used_cache_tier = CacheTier::kNonVolatileBlockTier) {
|
||||
auto untyped_handle = reinterpret_cast<Handle**>(handle);
|
||||
auto helper = lowest_used_cache_tier > CacheTier::kVolatileTier
|
||||
auto helper = lowest_used_cache_tier == CacheTier::kNonVolatileBlockTier
|
||||
? GetFullHelper()
|
||||
: GetBasicHelper();
|
||||
return this->cache_->Insert(key, UpCastValue(value), helper, charge,
|
||||
untyped_handle, priority, compressed, type);
|
||||
untyped_handle, priority);
|
||||
}
|
||||
|
||||
// Like SecondaryCache::InsertSaved, with SecondaryCache compatibility
|
||||
@@ -324,9 +319,9 @@ class FullTypedCacheInterface
|
||||
size_t* out_charge = nullptr) {
|
||||
ObjectPtr value;
|
||||
size_t charge;
|
||||
Status st = GetFullHelper()->create_cb(
|
||||
data, kNoCompression, CacheTier::kVolatileTier, create_context,
|
||||
this->cache_->memory_allocator(), &value, &charge);
|
||||
Status st = GetFullHelper()->create_cb(data, create_context,
|
||||
this->cache_->memory_allocator(),
|
||||
&value, &charge);
|
||||
if (out_charge) {
|
||||
*out_charge = charge;
|
||||
}
|
||||
@@ -345,8 +340,8 @@ class FullTypedCacheInterface
|
||||
const Slice& key, TCreateContext* create_context = nullptr,
|
||||
Priority priority = Priority::LOW, Statistics* stats = nullptr,
|
||||
CacheTier lowest_used_cache_tier = CacheTier::kNonVolatileBlockTier) {
|
||||
if (lowest_used_cache_tier > CacheTier::kVolatileTier) {
|
||||
return static_cast<TypedHandle*>(this->cache_->Lookup(
|
||||
if (lowest_used_cache_tier == CacheTier::kNonVolatileBlockTier) {
|
||||
return reinterpret_cast<TypedHandle*>(this->cache_->Lookup(
|
||||
key, GetFullHelper(), create_context, priority, stats));
|
||||
} else {
|
||||
return BasicTypedCacheInterface<TValue, kRole, CachePtr>::Lookup(key,
|
||||
@@ -357,7 +352,7 @@ class FullTypedCacheInterface
|
||||
inline void StartAsyncLookupFull(
|
||||
TypedAsyncLookupHandle& async_handle,
|
||||
CacheTier lowest_used_cache_tier = CacheTier::kNonVolatileBlockTier) {
|
||||
if (lowest_used_cache_tier > CacheTier::kVolatileTier) {
|
||||
if (lowest_used_cache_tier == CacheTier::kNonVolatileBlockTier) {
|
||||
async_handle.helper = GetFullHelper();
|
||||
this->cache_->StartAsyncLookup(async_handle);
|
||||
} else {
|
||||
|
||||
@@ -14,7 +14,7 @@ find_library(zstd_LIBRARIES
|
||||
HINTS ${zstd_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(zstd DEFAULT_MSG zstd_LIBRARIES ZSTD_INCLUDE_DIRS)
|
||||
find_package_handle_standard_args(zstd DEFAULT_MSG zstd_LIBRARIES zstd_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
zstd_LIBRARIES
|
||||
|
||||
+11
-25
@@ -19,14 +19,6 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
inline static SequenceNumber GetSeqNum(const DBImpl* db, const Snapshot* s) {
|
||||
if (s) {
|
||||
return s->GetSequenceNumber();
|
||||
} else {
|
||||
return db->GetLatestSequenceNumber();
|
||||
}
|
||||
}
|
||||
|
||||
Status ArenaWrappedDBIter::GetProperty(std::string prop_name,
|
||||
std::string* prop) {
|
||||
if (prop_name == "rocksdb.iterator.super-version-number") {
|
||||
@@ -62,9 +54,7 @@ void ArenaWrappedDBIter::Init(
|
||||
}
|
||||
}
|
||||
|
||||
Status ArenaWrappedDBIter::Refresh() { return Refresh(nullptr); }
|
||||
|
||||
Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
Status ArenaWrappedDBIter::Refresh() {
|
||||
if (cfd_ == nullptr || db_impl_ == nullptr || !allow_refresh_) {
|
||||
return Status::NotSupported("Creating renew iterator is not allowed.");
|
||||
}
|
||||
@@ -73,10 +63,6 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
// correct behavior. Will be corrected automatically when we take a snapshot
|
||||
// here for the case of WritePreparedTxnDB.
|
||||
uint64_t cur_sv_number = cfd_->GetSuperVersionNumber();
|
||||
// If we recreate a new internal iterator below (NewInternalIterator()),
|
||||
// we will pass in read_options_. We need to make sure it
|
||||
// has the right snapshot.
|
||||
read_options_.snapshot = snapshot;
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:1");
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:2");
|
||||
auto reinit_internal_iter = [&]() {
|
||||
@@ -86,19 +72,18 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
new (&arena_) Arena();
|
||||
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
assert(sv->version_number >= cur_sv_number);
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl_, snapshot);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(read_seq);
|
||||
read_callback_->Refresh(latest_seq);
|
||||
}
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
sv->current, read_seq,
|
||||
sv->current, latest_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback_, db_impl_, cfd_, expose_blob_index_,
|
||||
cur_sv_number, read_callback_, db_impl_, cfd_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, read_seq,
|
||||
read_options_, cfd_, sv, &arena_, latest_seq,
|
||||
/* allow_unprepared_value */ true, /* db_iter */ this);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
};
|
||||
@@ -107,13 +92,13 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
reinit_internal_iter();
|
||||
break;
|
||||
} else {
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl_, snapshot);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
// Refresh range-tombstones in MemTable
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
SuperVersion* sv = cfd_->GetThreadLocalSuperVersion(db_impl_);
|
||||
TEST_SYNC_POINT_CALLBACK("ArenaWrappedDBIter::Refresh:SV", nullptr);
|
||||
auto t = sv->mem->NewRangeTombstoneIterator(
|
||||
read_options_, read_seq, false /* immutable_memtable */);
|
||||
read_options_, latest_seq, false /* immutable_memtable */);
|
||||
if (!t || t->empty()) {
|
||||
// If memtable_range_tombstone_iter_ points to a non-empty tombstone
|
||||
// iterator, then it means sv->mem is not the memtable that
|
||||
@@ -143,6 +128,9 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
}
|
||||
db_impl_->ReturnAndCleanupSuperVersion(cfd_, sv);
|
||||
}
|
||||
// Refresh latest sequence number
|
||||
db_iter_->set_sequence(latest_seq);
|
||||
db_iter_->set_valid(false);
|
||||
// Check again if the latest super version number is changed
|
||||
uint64_t latest_sv_number = cfd_->GetSuperVersionNumber();
|
||||
if (latest_sv_number != cur_sv_number) {
|
||||
@@ -151,8 +139,6 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
cur_sv_number = latest_sv_number;
|
||||
continue;
|
||||
}
|
||||
db_iter_->set_sequence(read_seq);
|
||||
db_iter_->set_valid(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,6 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
Status GetProperty(std::string prop_name, std::string* prop) override;
|
||||
|
||||
Status Refresh() override;
|
||||
Status Refresh(const Snapshot*) override;
|
||||
|
||||
void Init(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
@@ -118,7 +117,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
// Generate the arena wrapped iterator class.
|
||||
// `db_impl` and `cfd` are used for reneweal. If left null, renewal will not
|
||||
// be supported.
|
||||
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
|
||||
@@ -46,8 +46,7 @@ class BlobContents {
|
||||
class BlobContentsCreator : public Cache::CreateContext {
|
||||
public:
|
||||
static void Create(std::unique_ptr<BlobContents>* out, size_t* out_charge,
|
||||
const Slice& contents, CompressionType /*type*/,
|
||||
MemoryAllocator* alloc) {
|
||||
const Slice& contents, MemoryAllocator* alloc) {
|
||||
auto raw = new BlobContents(AllocateAndCopyBlock(contents, alloc),
|
||||
contents.size());
|
||||
out->reset(raw);
|
||||
|
||||
@@ -34,9 +34,9 @@ BlobFileBuilder::BlobFileBuilder(
|
||||
VersionSet* versions, FileSystem* fs,
|
||||
const ImmutableOptions* immutable_options,
|
||||
const MutableCFOptions* mutable_cf_options, const FileOptions* file_options,
|
||||
const WriteOptions* write_options, std::string db_id,
|
||||
std::string db_session_id, int job_id, uint32_t column_family_id,
|
||||
const std::string& column_family_name, Env::WriteLifeTimeHint write_hint,
|
||||
std::string db_id, std::string db_session_id, int job_id,
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
Env::IOPriority io_priority, Env::WriteLifeTimeHint write_hint,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCompletionCallback* blob_callback,
|
||||
BlobFileCreationReason creation_reason,
|
||||
@@ -44,18 +44,18 @@ BlobFileBuilder::BlobFileBuilder(
|
||||
std::vector<BlobFileAddition>* blob_file_additions)
|
||||
: BlobFileBuilder([versions]() { return versions->NewFileNumber(); }, fs,
|
||||
immutable_options, mutable_cf_options, file_options,
|
||||
write_options, db_id, db_session_id, job_id,
|
||||
column_family_id, column_family_name, write_hint,
|
||||
io_tracer, blob_callback, creation_reason,
|
||||
blob_file_paths, blob_file_additions) {}
|
||||
db_id, db_session_id, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, io_tracer,
|
||||
blob_callback, creation_reason, blob_file_paths,
|
||||
blob_file_additions) {}
|
||||
|
||||
BlobFileBuilder::BlobFileBuilder(
|
||||
std::function<uint64_t()> file_number_generator, FileSystem* fs,
|
||||
const ImmutableOptions* immutable_options,
|
||||
const MutableCFOptions* mutable_cf_options, const FileOptions* file_options,
|
||||
const WriteOptions* write_options, std::string db_id,
|
||||
std::string db_session_id, int job_id, uint32_t column_family_id,
|
||||
const std::string& column_family_name, Env::WriteLifeTimeHint write_hint,
|
||||
std::string db_id, std::string db_session_id, int job_id,
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
Env::IOPriority io_priority, Env::WriteLifeTimeHint write_hint,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCompletionCallback* blob_callback,
|
||||
BlobFileCreationReason creation_reason,
|
||||
@@ -69,12 +69,12 @@ BlobFileBuilder::BlobFileBuilder(
|
||||
blob_compression_type_(mutable_cf_options->blob_compression_type),
|
||||
prepopulate_blob_cache_(mutable_cf_options->prepopulate_blob_cache),
|
||||
file_options_(file_options),
|
||||
write_options_(write_options),
|
||||
db_id_(std::move(db_id)),
|
||||
db_session_id_(std::move(db_session_id)),
|
||||
job_id_(job_id),
|
||||
column_family_id_(column_family_id),
|
||||
column_family_name_(column_family_name),
|
||||
io_priority_(io_priority),
|
||||
write_hint_(write_hint),
|
||||
io_tracer_(io_tracer),
|
||||
blob_callback_(blob_callback),
|
||||
@@ -87,7 +87,6 @@ BlobFileBuilder::BlobFileBuilder(
|
||||
assert(fs_);
|
||||
assert(immutable_options_);
|
||||
assert(file_options_);
|
||||
assert(write_options_);
|
||||
assert(blob_file_paths_);
|
||||
assert(blob_file_paths_->empty());
|
||||
assert(blob_file_additions_);
|
||||
@@ -208,14 +207,14 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
|
||||
blob_file_paths_->emplace_back(std::move(blob_file_path));
|
||||
|
||||
assert(file);
|
||||
file->SetIOPriority(write_options_->rate_limiter_priority);
|
||||
file->SetIOPriority(io_priority_);
|
||||
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(
|
||||
std::move(file), blob_file_paths_->back(), *file_options_,
|
||||
immutable_options_->clock, io_tracer_, statistics,
|
||||
Histograms::BLOB_DB_BLOB_FILE_WRITE_MICROS, immutable_options_->listeners,
|
||||
immutable_options_->listeners,
|
||||
immutable_options_->file_checksum_gen_factory.get(),
|
||||
tmp_set.Contains(FileType::kBlobFile), false));
|
||||
|
||||
@@ -232,7 +231,7 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
|
||||
expiration_range);
|
||||
|
||||
{
|
||||
Status s = blob_log_writer->WriteHeader(*write_options_, header);
|
||||
Status s = blob_log_writer->WriteHeader(header);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"BlobFileBuilder::OpenBlobFileIfNeeded:WriteHeader", &s);
|
||||
@@ -262,7 +261,7 @@ Status BlobFileBuilder::CompressBlobIfNeeded(
|
||||
|
||||
// TODO: allow user CompressionOptions, including max_compressed_bytes_per_kb
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(blob_compression_type_, opts);
|
||||
CompressionContext context(blob_compression_type_);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
@@ -297,8 +296,7 @@ Status BlobFileBuilder::WriteBlobToFile(const Slice& key, const Slice& blob,
|
||||
|
||||
uint64_t key_offset = 0;
|
||||
|
||||
Status s =
|
||||
writer_->AddRecord(*write_options_, key, blob, &key_offset, blob_offset);
|
||||
Status s = writer_->AddRecord(key, blob, &key_offset, blob_offset);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("BlobFileBuilder::WriteBlobToFile:AddRecord", &s);
|
||||
|
||||
@@ -323,8 +321,7 @@ Status BlobFileBuilder::CloseBlobFile() {
|
||||
std::string checksum_method;
|
||||
std::string checksum_value;
|
||||
|
||||
Status s = writer_->AppendFooter(*write_options_, footer, &checksum_method,
|
||||
&checksum_value);
|
||||
Status s = writer_->AppendFooter(footer, &checksum_method, &checksum_value);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("BlobFileBuilder::WriteBlobToFile:AppendFooter", &s);
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "rocksdb/advanced_options.h"
|
||||
#include "rocksdb/compression_type.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
#include "rocksdb/types.h"
|
||||
|
||||
@@ -37,11 +36,11 @@ class BlobFileBuilder {
|
||||
BlobFileBuilder(VersionSet* versions, FileSystem* fs,
|
||||
const ImmutableOptions* immutable_options,
|
||||
const MutableCFOptions* mutable_cf_options,
|
||||
const FileOptions* file_options,
|
||||
const WriteOptions* write_options, std::string db_id,
|
||||
const FileOptions* file_options, std::string db_id,
|
||||
std::string db_session_id, int job_id,
|
||||
uint32_t column_family_id,
|
||||
const std::string& column_family_name,
|
||||
Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCompletionCallback* blob_callback,
|
||||
@@ -52,11 +51,11 @@ class BlobFileBuilder {
|
||||
BlobFileBuilder(std::function<uint64_t()> file_number_generator,
|
||||
FileSystem* fs, const ImmutableOptions* immutable_options,
|
||||
const MutableCFOptions* mutable_cf_options,
|
||||
const FileOptions* file_options,
|
||||
const WriteOptions* write_options, std::string db_id,
|
||||
const FileOptions* file_options, std::string db_id,
|
||||
std::string db_session_id, int job_id,
|
||||
uint32_t column_family_id,
|
||||
const std::string& column_family_name,
|
||||
Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCompletionCallback* blob_callback,
|
||||
@@ -93,12 +92,12 @@ class BlobFileBuilder {
|
||||
CompressionType blob_compression_type_;
|
||||
PrepopulateBlobCache prepopulate_blob_cache_;
|
||||
const FileOptions* file_options_;
|
||||
const WriteOptions* write_options_;
|
||||
const std::string db_id_;
|
||||
const std::string db_session_id_;
|
||||
int job_id_;
|
||||
uint32_t column_family_id_;
|
||||
std::string column_family_name_;
|
||||
Env::IOPriority io_priority_;
|
||||
Env::WriteLifeTimeHint write_hint_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
BlobFileCompletionCallback* blob_callback_;
|
||||
|
||||
@@ -43,7 +43,6 @@ class BlobFileBuilderTest : public testing::Test {
|
||||
mock_env_.reset(MockEnv::Create(Env::Default()));
|
||||
fs_ = mock_env_->GetFileSystem().get();
|
||||
clock_ = mock_env_->GetSystemClock().get();
|
||||
write_options_.rate_limiter_priority = Env::IO_HIGH;
|
||||
}
|
||||
|
||||
void VerifyBlobFile(uint64_t blob_file_number,
|
||||
@@ -114,7 +113,6 @@ class BlobFileBuilderTest : public testing::Test {
|
||||
FileSystem* fs_;
|
||||
SystemClock* clock_;
|
||||
FileOptions file_options_;
|
||||
WriteOptions write_options_;
|
||||
};
|
||||
|
||||
TEST_F(BlobFileBuilderTest, BuildAndCheckOneFile) {
|
||||
@@ -138,6 +136,7 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckOneFile) {
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<std::string> blob_file_paths;
|
||||
@@ -145,8 +144,8 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckOneFile) {
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
|
||||
job_id, column_family_id, column_family_name, write_hint,
|
||||
&file_options_, "" /*db_id*/, "" /*db_session_id*/, job_id,
|
||||
column_family_id, column_family_name, io_priority, write_hint,
|
||||
nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
|
||||
|
||||
@@ -222,6 +221,7 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckMultipleFiles) {
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<std::string> blob_file_paths;
|
||||
@@ -229,8 +229,8 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckMultipleFiles) {
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
|
||||
job_id, column_family_id, column_family_name, write_hint,
|
||||
&file_options_, "" /*db_id*/, "" /*db_session_id*/, job_id,
|
||||
column_family_id, column_family_name, io_priority, write_hint,
|
||||
nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
|
||||
|
||||
@@ -309,6 +309,7 @@ TEST_F(BlobFileBuilderTest, InlinedValues) {
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<std::string> blob_file_paths;
|
||||
@@ -316,8 +317,8 @@ TEST_F(BlobFileBuilderTest, InlinedValues) {
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
|
||||
job_id, column_family_id, column_family_name, write_hint,
|
||||
&file_options_, "" /*db_id*/, "" /*db_session_id*/, job_id,
|
||||
column_family_id, column_family_name, io_priority, write_hint,
|
||||
nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
|
||||
|
||||
@@ -363,6 +364,7 @@ TEST_F(BlobFileBuilderTest, Compression) {
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<std::string> blob_file_paths;
|
||||
@@ -370,8 +372,8 @@ TEST_F(BlobFileBuilderTest, Compression) {
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
|
||||
job_id, column_family_id, column_family_name, write_hint,
|
||||
&file_options_, "" /*db_id*/, "" /*db_session_id*/, job_id,
|
||||
column_family_id, column_family_name, io_priority, write_hint,
|
||||
nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
|
||||
|
||||
@@ -404,7 +406,7 @@ TEST_F(BlobFileBuilderTest, Compression) {
|
||||
ASSERT_EQ(blob_file_addition.GetTotalBlobCount(), 1);
|
||||
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(kSnappyCompression, opts);
|
||||
CompressionContext context(kSnappyCompression);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
@@ -446,6 +448,7 @@ TEST_F(BlobFileBuilderTest, CompressionError) {
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<std::string> blob_file_paths;
|
||||
@@ -453,8 +456,8 @@ TEST_F(BlobFileBuilderTest, CompressionError) {
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
|
||||
job_id, column_family_id, column_family_name, write_hint,
|
||||
&file_options_, "" /*db_id*/, "" /*db_session_id*/, job_id,
|
||||
column_family_id, column_family_name, io_priority, write_hint,
|
||||
nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
|
||||
|
||||
@@ -525,6 +528,7 @@ TEST_F(BlobFileBuilderTest, Checksum) {
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<std::string> blob_file_paths;
|
||||
@@ -532,8 +536,8 @@ TEST_F(BlobFileBuilderTest, Checksum) {
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
|
||||
job_id, column_family_id, column_family_name, write_hint,
|
||||
&file_options_, "" /*db_id*/, "" /*db_session_id*/, job_id,
|
||||
column_family_id, column_family_name, io_priority, write_hint,
|
||||
nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
|
||||
|
||||
@@ -585,13 +589,11 @@ class BlobFileBuilderIOErrorTest
|
||||
BlobFileBuilderIOErrorTest() : sync_point_(GetParam()) {
|
||||
mock_env_.reset(MockEnv::Create(Env::Default()));
|
||||
fs_ = mock_env_->GetFileSystem().get();
|
||||
write_options_.rate_limiter_priority = Env::IO_HIGH;
|
||||
}
|
||||
|
||||
std::unique_ptr<Env> mock_env_;
|
||||
FileSystem* fs_;
|
||||
FileOptions file_options_;
|
||||
WriteOptions write_options_;
|
||||
std::string sync_point_;
|
||||
};
|
||||
|
||||
@@ -624,6 +626,7 @@ TEST_P(BlobFileBuilderIOErrorTest, IOError) {
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<std::string> blob_file_paths;
|
||||
@@ -631,8 +634,8 @@ TEST_P(BlobFileBuilderIOErrorTest, IOError) {
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
|
||||
job_id, column_family_id, column_family_name, write_hint,
|
||||
&file_options_, "" /*db_id*/, "" /*db_session_id*/, job_id,
|
||||
column_family_id, column_family_name, io_priority, write_hint,
|
||||
nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ void WriteBlobFile(uint32_t column_family_id,
|
||||
BlobLogHeader header(column_family_id, kNoCompression, has_ttl,
|
||||
expiration_range);
|
||||
|
||||
ASSERT_OK(blob_log_writer.WriteHeader(WriteOptions(), header));
|
||||
ASSERT_OK(blob_log_writer.WriteHeader(header));
|
||||
|
||||
constexpr char key[] = "key";
|
||||
constexpr char blob[] = "blob";
|
||||
@@ -67,8 +67,7 @@ void WriteBlobFile(uint32_t column_family_id,
|
||||
uint64_t key_offset = 0;
|
||||
uint64_t blob_offset = 0;
|
||||
|
||||
ASSERT_OK(blob_log_writer.AddRecord(WriteOptions(), key, blob, &key_offset,
|
||||
&blob_offset));
|
||||
ASSERT_OK(blob_log_writer.AddRecord(key, blob, &key_offset, &blob_offset));
|
||||
|
||||
BlobLogFooter footer;
|
||||
footer.blob_count = 1;
|
||||
@@ -77,8 +76,8 @@ void WriteBlobFile(uint32_t column_family_id,
|
||||
std::string checksum_method;
|
||||
std::string checksum_value;
|
||||
|
||||
ASSERT_OK(blob_log_writer.AppendFooter(WriteOptions(), footer,
|
||||
&checksum_method, &checksum_value));
|
||||
ASSERT_OK(
|
||||
blob_log_writer.AppendFooter(footer, &checksum_method, &checksum_value));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
+20
-18
@@ -154,9 +154,11 @@ Status BlobFileReader::ReadHeader(const RandomAccessFileReader* file_reader,
|
||||
constexpr uint64_t read_offset = 0;
|
||||
constexpr size_t read_size = BlobLogHeader::kSize;
|
||||
|
||||
// TODO: rate limit reading headers from blob files.
|
||||
const Status s =
|
||||
ReadFromFile(file_reader, read_options, read_offset, read_size,
|
||||
statistics, &header_slice, &buf, &aligned_buf);
|
||||
statistics, &header_slice, &buf, &aligned_buf,
|
||||
Env::IO_TOTAL /* rate_limiter_priority */);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -205,9 +207,11 @@ Status BlobFileReader::ReadFooter(const RandomAccessFileReader* file_reader,
|
||||
const uint64_t read_offset = file_size - BlobLogFooter::kSize;
|
||||
constexpr size_t read_size = BlobLogFooter::kSize;
|
||||
|
||||
// TODO: rate limit reading footers from blob files.
|
||||
const Status s =
|
||||
ReadFromFile(file_reader, read_options, read_offset, read_size,
|
||||
statistics, &footer_slice, &buf, &aligned_buf);
|
||||
statistics, &footer_slice, &buf, &aligned_buf,
|
||||
Env::IO_TOTAL /* rate_limiter_priority */);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -238,7 +242,8 @@ Status BlobFileReader::ReadFromFile(const RandomAccessFileReader* file_reader,
|
||||
const ReadOptions& read_options,
|
||||
uint64_t read_offset, size_t read_size,
|
||||
Statistics* statistics, Slice* slice,
|
||||
Buffer* buf, AlignedBuf* aligned_buf) {
|
||||
Buffer* buf, AlignedBuf* aligned_buf,
|
||||
Env::IOPriority rate_limiter_priority) {
|
||||
assert(slice);
|
||||
assert(buf);
|
||||
assert(aligned_buf);
|
||||
@@ -259,13 +264,13 @@ Status BlobFileReader::ReadFromFile(const RandomAccessFileReader* file_reader,
|
||||
constexpr char* scratch = nullptr;
|
||||
|
||||
s = file_reader->Read(io_options, read_offset, read_size, slice, scratch,
|
||||
aligned_buf);
|
||||
aligned_buf, rate_limiter_priority);
|
||||
} else {
|
||||
buf->reset(new char[read_size]);
|
||||
constexpr AlignedBuf* aligned_scratch = nullptr;
|
||||
|
||||
s = file_reader->Read(io_options, read_offset, read_size, slice, buf->get(),
|
||||
aligned_scratch);
|
||||
aligned_scratch, rate_limiter_priority);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
@@ -340,7 +345,8 @@ Status BlobFileReader::GetBlob(
|
||||
}
|
||||
prefetched = prefetch_buffer->TryReadFromCache(
|
||||
io_options, file_reader_.get(), record_offset,
|
||||
static_cast<size_t>(record_size), &record_slice, &s, for_compaction);
|
||||
static_cast<size_t>(record_size), &record_slice, &s,
|
||||
read_options.rate_limiter_priority, for_compaction);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -351,10 +357,10 @@ Status BlobFileReader::GetBlob(
|
||||
PERF_COUNTER_ADD(blob_read_count, 1);
|
||||
PERF_COUNTER_ADD(blob_read_byte, record_size);
|
||||
PERF_TIMER_GUARD(blob_read_time);
|
||||
const Status s =
|
||||
ReadFromFile(file_reader_.get(), read_options, record_offset,
|
||||
static_cast<size_t>(record_size), statistics_,
|
||||
&record_slice, &buf, &aligned_buf);
|
||||
const Status s = ReadFromFile(
|
||||
file_reader_.get(), read_options, record_offset,
|
||||
static_cast<size_t>(record_size), statistics_, &record_slice, &buf,
|
||||
&aligned_buf, read_options.rate_limiter_priority);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -462,12 +468,9 @@ void BlobFileReader::MultiGetBlob(
|
||||
TEST_SYNC_POINT("BlobFileReader::MultiGetBlob:ReadFromFile");
|
||||
PERF_COUNTER_ADD(blob_read_count, num_blobs);
|
||||
PERF_COUNTER_ADD(blob_read_byte, total_len);
|
||||
IOOptions opts;
|
||||
s = file_reader_->PrepareIOOptions(read_options, opts);
|
||||
if (s.ok()) {
|
||||
s = file_reader_->MultiRead(opts, read_reqs.data(), read_reqs.size(),
|
||||
direct_io ? &aligned_buf : nullptr);
|
||||
}
|
||||
s = file_reader_->MultiRead(IOOptions(), read_reqs.data(), read_reqs.size(),
|
||||
direct_io ? &aligned_buf : nullptr,
|
||||
read_options.rate_limiter_priority);
|
||||
if (!s.ok()) {
|
||||
for (auto& req : read_reqs) {
|
||||
req.status.PermitUncheckedError();
|
||||
@@ -585,8 +588,7 @@ Status BlobFileReader::UncompressBlobIfNeeded(
|
||||
assert(result);
|
||||
|
||||
if (compression_type == kNoCompression) {
|
||||
BlobContentsCreator::Create(result, nullptr, value_slice, kNoCompression,
|
||||
allocator);
|
||||
BlobContentsCreator::Create(result, nullptr, value_slice, allocator);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,8 @@ class BlobFileReader {
|
||||
const ReadOptions& read_options,
|
||||
uint64_t read_offset, size_t read_size,
|
||||
Statistics* statistics, Slice* slice, Buffer* buf,
|
||||
AlignedBuf* aligned_buf);
|
||||
AlignedBuf* aligned_buf,
|
||||
Env::IOPriority rate_limiter_priority);
|
||||
|
||||
static Status VerifyBlob(const Slice& record_slice, const Slice& user_key,
|
||||
uint64_t value_size);
|
||||
|
||||
@@ -63,7 +63,7 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
BlobLogHeader header(column_family_id, compression, has_ttl,
|
||||
expiration_range_header);
|
||||
|
||||
ASSERT_OK(blob_log_writer.WriteHeader(WriteOptions(), header));
|
||||
ASSERT_OK(blob_log_writer.WriteHeader(header));
|
||||
|
||||
std::vector<std::string> compressed_blobs(num);
|
||||
std::vector<Slice> blobs_to_write(num);
|
||||
@@ -74,7 +74,7 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
}
|
||||
} else {
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(compression, opts);
|
||||
CompressionContext context(compression);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
compression, sample_for_compression);
|
||||
@@ -91,8 +91,7 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
uint64_t key_offset = 0;
|
||||
ASSERT_OK(blob_log_writer.AddRecord(WriteOptions(), keys[i],
|
||||
blobs_to_write[i], &key_offset,
|
||||
ASSERT_OK(blob_log_writer.AddRecord(keys[i], blobs_to_write[i], &key_offset,
|
||||
&blob_offsets[i]));
|
||||
}
|
||||
|
||||
@@ -102,8 +101,8 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
|
||||
std::string checksum_method;
|
||||
std::string checksum_value;
|
||||
ASSERT_OK(blob_log_writer.AppendFooter(WriteOptions(), footer,
|
||||
&checksum_method, &checksum_value));
|
||||
ASSERT_OK(
|
||||
blob_log_writer.AppendFooter(footer, &checksum_method, &checksum_value));
|
||||
}
|
||||
|
||||
// Creates a test blob file with a single blob in it. Note: this method
|
||||
@@ -474,7 +473,7 @@ TEST_F(BlobFileReaderTest, Malformed) {
|
||||
BlobLogHeader header(column_family_id, kNoCompression, has_ttl,
|
||||
expiration_range);
|
||||
|
||||
ASSERT_OK(blob_log_writer.WriteHeader(WriteOptions(), header));
|
||||
ASSERT_OK(blob_log_writer.WriteHeader(header));
|
||||
}
|
||||
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
@@ -29,8 +29,9 @@ Status BlobLogSequentialReader::ReadSlice(uint64_t size, Slice* slice,
|
||||
|
||||
StopWatch read_sw(clock_, statistics_, BLOB_DB_BLOB_FILE_READ_MICROS);
|
||||
// TODO: rate limit `BlobLogSequentialReader` reads (it appears unused?)
|
||||
Status s = file_->Read(IOOptions(), next_byte_, static_cast<size_t>(size),
|
||||
slice, buf, nullptr);
|
||||
Status s =
|
||||
file_->Read(IOOptions(), next_byte_, static_cast<size_t>(size), slice,
|
||||
buf, nullptr, Env::IO_TOTAL /* rate_limiter_priority */);
|
||||
next_byte_ += size;
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
|
||||
+27
-56
@@ -33,49 +33,35 @@ BlobLogWriter::BlobLogWriter(std::unique_ptr<WritableFileWriter>&& dest,
|
||||
|
||||
BlobLogWriter::~BlobLogWriter() = default;
|
||||
|
||||
Status BlobLogWriter::Sync(const WriteOptions& write_options) {
|
||||
Status BlobLogWriter::Sync() {
|
||||
TEST_SYNC_POINT("BlobLogWriter::Sync");
|
||||
|
||||
StopWatch sync_sw(clock_, statistics_, BLOB_DB_BLOB_FILE_SYNC_MICROS);
|
||||
IOOptions opts;
|
||||
Status s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
if (s.ok()) {
|
||||
s = dest_->Sync(opts, use_fsync_);
|
||||
}
|
||||
if (s.ok()) {
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_SYNCED);
|
||||
}
|
||||
Status s = dest_->Sync(use_fsync_);
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_SYNCED);
|
||||
return s;
|
||||
}
|
||||
|
||||
Status BlobLogWriter::WriteHeader(const WriteOptions& write_options,
|
||||
BlobLogHeader& header) {
|
||||
Status BlobLogWriter::WriteHeader(BlobLogHeader& header) {
|
||||
assert(block_offset_ == 0);
|
||||
assert(last_elem_type_ == kEtNone);
|
||||
std::string str;
|
||||
header.EncodeTo(&str);
|
||||
|
||||
IOOptions opts;
|
||||
Status s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(opts, Slice(str));
|
||||
}
|
||||
Status s = dest_->Append(Slice(str));
|
||||
if (s.ok()) {
|
||||
block_offset_ += str.size();
|
||||
if (do_flush_) {
|
||||
s = dest_->Flush(opts);
|
||||
s = dest_->Flush();
|
||||
}
|
||||
}
|
||||
last_elem_type_ = kEtFileHdr;
|
||||
if (s.ok()) {
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_BYTES_WRITTEN,
|
||||
BlobLogHeader::kSize);
|
||||
}
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_BYTES_WRITTEN,
|
||||
BlobLogHeader::kSize);
|
||||
return s;
|
||||
}
|
||||
|
||||
Status BlobLogWriter::AppendFooter(const WriteOptions& write_options,
|
||||
BlobLogFooter& footer,
|
||||
Status BlobLogWriter::AppendFooter(BlobLogFooter& footer,
|
||||
std::string* checksum_method,
|
||||
std::string* checksum_value) {
|
||||
assert(block_offset_ != 0);
|
||||
@@ -89,17 +75,14 @@ Status BlobLogWriter::AppendFooter(const WriteOptions& write_options,
|
||||
s.PermitUncheckedError();
|
||||
return Status::IOError("Seen Error. Skip closing.");
|
||||
} else {
|
||||
IOOptions opts;
|
||||
s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(opts, Slice(str));
|
||||
}
|
||||
s = dest_->Append(Slice(str));
|
||||
if (s.ok()) {
|
||||
block_offset_ += str.size();
|
||||
s = Sync(write_options);
|
||||
|
||||
s = Sync();
|
||||
|
||||
if (s.ok()) {
|
||||
s = dest_->Close(opts);
|
||||
s = dest_->Close();
|
||||
|
||||
if (s.ok()) {
|
||||
assert(!!checksum_method == !!checksum_value);
|
||||
@@ -128,15 +111,12 @@ Status BlobLogWriter::AppendFooter(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
last_elem_type_ = kEtFileFooter;
|
||||
if (s.ok()) {
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_BYTES_WRITTEN,
|
||||
BlobLogFooter::kSize);
|
||||
}
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_BYTES_WRITTEN,
|
||||
BlobLogFooter::kSize);
|
||||
return s;
|
||||
}
|
||||
|
||||
Status BlobLogWriter::AddRecord(const WriteOptions& write_options,
|
||||
const Slice& key, const Slice& val,
|
||||
Status BlobLogWriter::AddRecord(const Slice& key, const Slice& val,
|
||||
uint64_t expiration, uint64_t* key_offset,
|
||||
uint64_t* blob_offset) {
|
||||
assert(block_offset_ != 0);
|
||||
@@ -145,13 +125,11 @@ Status BlobLogWriter::AddRecord(const WriteOptions& write_options,
|
||||
std::string buf;
|
||||
ConstructBlobHeader(&buf, key, val, expiration);
|
||||
|
||||
Status s =
|
||||
EmitPhysicalRecord(write_options, buf, key, val, key_offset, blob_offset);
|
||||
Status s = EmitPhysicalRecord(buf, key, val, key_offset, blob_offset);
|
||||
return s;
|
||||
}
|
||||
|
||||
Status BlobLogWriter::AddRecord(const WriteOptions& write_options,
|
||||
const Slice& key, const Slice& val,
|
||||
Status BlobLogWriter::AddRecord(const Slice& key, const Slice& val,
|
||||
uint64_t* key_offset, uint64_t* blob_offset) {
|
||||
assert(block_offset_ != 0);
|
||||
assert(last_elem_type_ == kEtFileHdr || last_elem_type_ == kEtRecord);
|
||||
@@ -159,8 +137,7 @@ Status BlobLogWriter::AddRecord(const WriteOptions& write_options,
|
||||
std::string buf;
|
||||
ConstructBlobHeader(&buf, key, val, 0);
|
||||
|
||||
Status s =
|
||||
EmitPhysicalRecord(write_options, buf, key, val, key_offset, blob_offset);
|
||||
Status s = EmitPhysicalRecord(buf, key, val, key_offset, blob_offset);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -173,34 +150,28 @@ void BlobLogWriter::ConstructBlobHeader(std::string* buf, const Slice& key,
|
||||
record.EncodeHeaderTo(buf);
|
||||
}
|
||||
|
||||
Status BlobLogWriter::EmitPhysicalRecord(const WriteOptions& write_options,
|
||||
const std::string& headerbuf,
|
||||
Status BlobLogWriter::EmitPhysicalRecord(const std::string& headerbuf,
|
||||
const Slice& key, const Slice& val,
|
||||
uint64_t* key_offset,
|
||||
uint64_t* blob_offset) {
|
||||
IOOptions opts;
|
||||
Status s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
StopWatch write_sw(clock_, statistics_, BLOB_DB_BLOB_FILE_WRITE_MICROS);
|
||||
Status s = dest_->Append(Slice(headerbuf));
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(opts, Slice(headerbuf));
|
||||
s = dest_->Append(key);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(opts, key);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(opts, val);
|
||||
s = dest_->Append(val);
|
||||
}
|
||||
if (do_flush_ && s.ok()) {
|
||||
s = dest_->Flush(opts);
|
||||
s = dest_->Flush();
|
||||
}
|
||||
|
||||
*key_offset = block_offset_ + BlobLogRecord::kHeaderSize;
|
||||
*blob_offset = *key_offset + key.size();
|
||||
block_offset_ = *blob_offset + val.size();
|
||||
last_elem_type_ = kEtRecord;
|
||||
if (s.ok()) {
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_BYTES_WRITTEN,
|
||||
BlobLogRecord::kHeaderSize + key.size() + val.size());
|
||||
}
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_BYTES_WRITTEN,
|
||||
BlobLogRecord::kHeaderSize + key.size() + val.size());
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,24 +43,20 @@ class BlobLogWriter {
|
||||
static void ConstructBlobHeader(std::string* buf, const Slice& key,
|
||||
const Slice& val, uint64_t expiration);
|
||||
|
||||
Status AddRecord(const WriteOptions& write_options, const Slice& key,
|
||||
const Slice& val, uint64_t* key_offset,
|
||||
Status AddRecord(const Slice& key, const Slice& val, uint64_t* key_offset,
|
||||
uint64_t* blob_offset);
|
||||
|
||||
Status AddRecord(const WriteOptions& write_options, const Slice& key,
|
||||
const Slice& val, uint64_t expiration, uint64_t* key_offset,
|
||||
uint64_t* blob_offset);
|
||||
Status AddRecord(const Slice& key, const Slice& val, uint64_t expiration,
|
||||
uint64_t* key_offset, uint64_t* blob_offset);
|
||||
|
||||
Status EmitPhysicalRecord(const WriteOptions& write_options,
|
||||
const std::string& headerbuf, const Slice& key,
|
||||
Status EmitPhysicalRecord(const std::string& headerbuf, const Slice& key,
|
||||
const Slice& val, uint64_t* key_offset,
|
||||
uint64_t* blob_offset);
|
||||
|
||||
Status AppendFooter(const WriteOptions& write_options, BlobLogFooter& footer,
|
||||
std::string* checksum_method,
|
||||
Status AppendFooter(BlobLogFooter& footer, std::string* checksum_method,
|
||||
std::string* checksum_value);
|
||||
|
||||
Status WriteHeader(const WriteOptions& write_options, BlobLogHeader& header);
|
||||
Status WriteHeader(BlobLogHeader& header);
|
||||
|
||||
WritableFileWriter* file() { return dest_.get(); }
|
||||
|
||||
@@ -68,7 +64,7 @@ class BlobLogWriter {
|
||||
|
||||
uint64_t get_log_number() const { return log_number_; }
|
||||
|
||||
Status Sync(const WriteOptions& write_options);
|
||||
Status Sync();
|
||||
|
||||
private:
|
||||
std::unique_ptr<WritableFileWriter> dest_;
|
||||
|
||||
@@ -65,7 +65,7 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
BlobLogHeader header(column_family_id, compression, has_ttl,
|
||||
expiration_range_header);
|
||||
|
||||
ASSERT_OK(blob_log_writer.WriteHeader(WriteOptions(), header));
|
||||
ASSERT_OK(blob_log_writer.WriteHeader(header));
|
||||
|
||||
std::vector<std::string> compressed_blobs(num);
|
||||
std::vector<Slice> blobs_to_write(num);
|
||||
@@ -76,7 +76,7 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
}
|
||||
} else {
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(compression, opts);
|
||||
CompressionContext context(compression);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
compression, sample_for_compression);
|
||||
@@ -93,8 +93,7 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
uint64_t key_offset = 0;
|
||||
ASSERT_OK(blob_log_writer.AddRecord(WriteOptions(), keys[i],
|
||||
blobs_to_write[i], &key_offset,
|
||||
ASSERT_OK(blob_log_writer.AddRecord(keys[i], blobs_to_write[i], &key_offset,
|
||||
&blob_offsets[i]));
|
||||
}
|
||||
|
||||
@@ -104,8 +103,8 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
|
||||
std::string checksum_method;
|
||||
std::string checksum_value;
|
||||
ASSERT_OK(blob_log_writer.AppendFooter(WriteOptions(), footer,
|
||||
&checksum_method, &checksum_value));
|
||||
ASSERT_OK(
|
||||
blob_log_writer.AppendFooter(footer, &checksum_method, &checksum_value));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
@@ -1221,7 +1220,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
auto sec_handle0 = secondary_cache->Lookup(
|
||||
key0, BlobSource::SharedCacheInterface::GetFullHelper(),
|
||||
/*context*/ nullptr, true,
|
||||
/*advise_erase=*/true, /*stats=*/nullptr, kept_in_sec_cache);
|
||||
/*advise_erase=*/true, kept_in_sec_cache);
|
||||
ASSERT_FALSE(kept_in_sec_cache);
|
||||
ASSERT_NE(sec_handle0, nullptr);
|
||||
ASSERT_TRUE(sec_handle0->IsReady());
|
||||
@@ -1249,7 +1248,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
auto sec_handle1 = secondary_cache->Lookup(
|
||||
key1, BlobSource::SharedCacheInterface::GetFullHelper(),
|
||||
/*context*/ nullptr, true,
|
||||
/*advise_erase=*/true, /*stats=*/nullptr, kept_in_sec_cache);
|
||||
/*advise_erase=*/true, kept_in_sec_cache);
|
||||
ASSERT_FALSE(kept_in_sec_cache);
|
||||
ASSERT_EQ(sec_handle1, nullptr);
|
||||
|
||||
|
||||
@@ -168,7 +168,6 @@ TEST_F(DBBlobBasicTest, IterateBlobsFromCache) {
|
||||
ASSERT_EQ(iter->value().ToString(), blobs[i]);
|
||||
++i;
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(i, num_blobs);
|
||||
ASSERT_EQ(options.statistics->getAndResetTickerCount(BLOB_DB_CACHE_ADD), 0);
|
||||
}
|
||||
@@ -204,7 +203,6 @@ TEST_F(DBBlobBasicTest, IterateBlobsFromCache) {
|
||||
ASSERT_EQ(iter->value().ToString(), blobs[i]);
|
||||
++i;
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(i, num_blobs);
|
||||
ASSERT_EQ(options.statistics->getAndResetTickerCount(BLOB_DB_CACHE_ADD),
|
||||
num_blobs);
|
||||
@@ -226,7 +224,6 @@ TEST_F(DBBlobBasicTest, IterateBlobsFromCache) {
|
||||
ASSERT_EQ(iter->value().ToString(), blobs[i]);
|
||||
++i;
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(i, num_blobs);
|
||||
ASSERT_EQ(options.statistics->getAndResetTickerCount(BLOB_DB_CACHE_ADD), 0);
|
||||
}
|
||||
@@ -2126,7 +2123,6 @@ TEST_F(DBBlobWithTimestampTest, IterateBlobs) {
|
||||
/*key_is_internal*/ false);
|
||||
iter->Prev();
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// Backward iteration, then reverse to forward.
|
||||
@@ -2173,7 +2169,6 @@ TEST_F(DBBlobWithTimestampTest, IterateBlobs) {
|
||||
iter->Next();
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// Backward iterating multiple versions of the same key, get in this order:
|
||||
@@ -2192,7 +2187,6 @@ TEST_F(DBBlobWithTimestampTest, IterateBlobs) {
|
||||
iter->Prev();
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
int upper_bound_idx = num_blobs - 2;
|
||||
@@ -2215,7 +2209,6 @@ TEST_F(DBBlobWithTimestampTest, IterateBlobs) {
|
||||
iter->Next();
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// Backward iteration with upper and lower bound.
|
||||
@@ -2231,7 +2224,6 @@ TEST_F(DBBlobWithTimestampTest, IterateBlobs) {
|
||||
iter->Prev();
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,13 +96,9 @@ class DBBlobIndexTest : public DBTestBase {
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter* GetBlobIterator() {
|
||||
ColumnFamilyData* column_family = cfd();
|
||||
DBImpl* db_impl = dbfull();
|
||||
return db_impl->NewIteratorImpl(
|
||||
ReadOptions(), column_family,
|
||||
column_family->GetReferencedSuperVersion(db_impl),
|
||||
db_impl->GetLatestSequenceNumber(), nullptr /*read_callback*/,
|
||||
true /*expose_blob_index*/);
|
||||
return dbfull()->NewIteratorImpl(
|
||||
ReadOptions(), cfd(), dbfull()->GetLatestSequenceNumber(),
|
||||
nullptr /*read_callback*/, true /*expose_blob_index*/);
|
||||
}
|
||||
|
||||
Options GetTestOptions() {
|
||||
@@ -325,7 +321,8 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
|
||||
auto check_is_blob = [&](bool is_blob) {
|
||||
return [is_blob](Iterator* iterator) {
|
||||
ASSERT_EQ(is_blob, static_cast<ArenaWrappedDBIter*>(iterator)->IsBlob());
|
||||
ASSERT_EQ(is_blob,
|
||||
reinterpret_cast<ArenaWrappedDBIter*>(iterator)->IsBlob());
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -11,10 +11,8 @@ FilePrefetchBuffer* PrefetchBufferCollection::GetOrCreatePrefetchBuffer(
|
||||
uint64_t file_number) {
|
||||
auto& prefetch_buffer = prefetch_buffers_[file_number];
|
||||
if (!prefetch_buffer) {
|
||||
ReadaheadParams readahead_params;
|
||||
readahead_params.initial_readahead_size = readahead_size_;
|
||||
readahead_params.max_readahead_size = readahead_size_;
|
||||
prefetch_buffer.reset(new FilePrefetchBuffer(readahead_params));
|
||||
prefetch_buffer.reset(
|
||||
new FilePrefetchBuffer(readahead_size_, readahead_size_));
|
||||
}
|
||||
|
||||
return prefetch_buffer.get();
|
||||
|
||||
+41
-79
@@ -15,7 +15,6 @@
|
||||
|
||||
#include "db/blob/blob_file_builder.h"
|
||||
#include "db/compaction/compaction_iterator.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/merge_helper.h"
|
||||
@@ -32,11 +31,9 @@
|
||||
#include "options/options_helper.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "seqno_to_time_mapping.h"
|
||||
#include "table/block_based/block_based_table_builder.h"
|
||||
#include "table/format.h"
|
||||
#include "table/internal_iterator.h"
|
||||
@@ -59,8 +56,8 @@ TableBuilder* NewTableBuilder(const TableBuilderOptions& tboptions,
|
||||
Status BuildTable(
|
||||
const std::string& dbname, VersionSet* versions,
|
||||
const ImmutableDBOptions& db_options, const TableBuilderOptions& tboptions,
|
||||
const FileOptions& file_options, TableCache* table_cache,
|
||||
InternalIterator* iter,
|
||||
const FileOptions& file_options, const ReadOptions& read_options,
|
||||
TableCache* table_cache, InternalIterator* iter,
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
|
||||
@@ -71,8 +68,9 @@ Status BuildTable(
|
||||
IOStatus* io_status, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCreationReason blob_creation_reason,
|
||||
const SeqnoToTimeMapping& seqno_to_time_mapping, EventLogger* event_logger,
|
||||
int job_id, TableProperties* table_properties,
|
||||
Env::WriteLifeTimeHint write_hint, const std::string* full_history_ts_low,
|
||||
int job_id, const Env::IOPriority io_priority,
|
||||
TableProperties* table_properties, Env::WriteLifeTimeHint write_hint,
|
||||
const std::string* full_history_ts_low,
|
||||
BlobFileCompletionCallback* blob_callback, Version* version,
|
||||
uint64_t* num_input_entries, uint64_t* memtable_payload_bytes,
|
||||
uint64_t* memtable_garbage_bytes) {
|
||||
@@ -83,8 +81,11 @@ Status BuildTable(
|
||||
auto& ioptions = tboptions.ioptions;
|
||||
// Reports the IOStats for flush for every following bytes.
|
||||
const size_t kReportFlushIOStatsEvery = 1048576;
|
||||
OutputValidator output_validator(tboptions.internal_comparator,
|
||||
/*enable_hash=*/paranoid_file_checks);
|
||||
OutputValidator output_validator(
|
||||
tboptions.internal_comparator,
|
||||
/*enable_order_check=*/
|
||||
mutable_cf_options.check_flush_compaction_key_order,
|
||||
/*enable_hash=*/paranoid_file_checks);
|
||||
Status s;
|
||||
meta->fd.file_size = 0;
|
||||
iter->SeekToFirst();
|
||||
@@ -162,11 +163,11 @@ Status BuildTable(
|
||||
|
||||
table_file_created = true;
|
||||
FileTypeSet tmp_set = ioptions.checksum_handoff_file_types;
|
||||
file->SetIOPriority(tboptions.write_options.rate_limiter_priority);
|
||||
file->SetIOPriority(io_priority);
|
||||
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,
|
||||
ioptions.stats, ioptions.listeners,
|
||||
ioptions.file_checksum_gen_factory.get(),
|
||||
tmp_set.Contains(FileType::kTableFile), false));
|
||||
|
||||
@@ -186,11 +187,10 @@ Status BuildTable(
|
||||
blob_file_additions)
|
||||
? new BlobFileBuilder(
|
||||
versions, fs, &ioptions, &mutable_cf_options, &file_options,
|
||||
&(tboptions.write_options), tboptions.db_id,
|
||||
tboptions.db_session_id, job_id, tboptions.column_family_id,
|
||||
tboptions.column_family_name, write_hint, io_tracer,
|
||||
blob_callback, blob_creation_reason, &blob_file_paths,
|
||||
blob_file_additions)
|
||||
tboptions.db_id, tboptions.db_session_id, job_id,
|
||||
tboptions.column_family_id, tboptions.column_family_name,
|
||||
io_priority, write_hint, io_tracer, blob_callback,
|
||||
blob_creation_reason, &blob_file_paths, blob_file_additions)
|
||||
: nullptr);
|
||||
|
||||
const std::atomic<bool> kManualCompactionCanceledFalse{false};
|
||||
@@ -202,52 +202,30 @@ Status BuildTable(
|
||||
blob_file_builder.get(), ioptions.allow_data_in_errors,
|
||||
ioptions.enforce_single_del_contracts,
|
||||
/*manual_compaction_canceled=*/kManualCompactionCanceledFalse,
|
||||
true /* must_count_input_entries */,
|
||||
/*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;
|
||||
|
||||
std::string key_after_flush_buf;
|
||||
c_iter.SeekToFirst();
|
||||
for (; c_iter.Valid(); c_iter.Next()) {
|
||||
const Slice& key = c_iter.key();
|
||||
const Slice& value = c_iter.value();
|
||||
const ParsedInternalKey& ikey = c_iter.ikey();
|
||||
Slice key_after_flush = key;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Generate a rolling 64-bit hash of the key and values
|
||||
// Note :
|
||||
// Here "key" integrates 'sequence_number'+'kType'+'user key'.
|
||||
s = output_validator.Add(key_after_flush, value);
|
||||
// Generate a rolling 64-bit hash of the key and values
|
||||
// Note :
|
||||
// Here "key" integrates 'sequence_number'+'kType'+'user key'.
|
||||
s = output_validator.Add(key, value);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
builder->Add(key_after_flush, value);
|
||||
builder->Add(key, value);
|
||||
|
||||
s = meta->UpdateBoundaries(key_after_flush, value, ikey.sequence,
|
||||
ikey.type);
|
||||
s = meta->UpdateBoundaries(key, value, ikey.sequence, ikey.type);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO(noetzli): Update stats after flush, too.
|
||||
// TODO(hx235): Replace `rate_limiter_priority` with `io_activity` for
|
||||
// flush IO in repair when we have an `Env::IOActivity` enum for it
|
||||
if ((tboptions.write_options.io_activity == Env::IOActivity::kFlush ||
|
||||
tboptions.write_options.io_activity == Env::IOActivity::kDBOpen ||
|
||||
tboptions.write_options.rate_limiter_priority == Env::IO_HIGH) &&
|
||||
if (io_priority == Env::IO_HIGH &&
|
||||
IOSTATS(bytes_written) >= kReportFlushIOStatsEvery) {
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::FLUSH_BYTES_WRITTEN, IOSTATS(bytes_written));
|
||||
@@ -264,12 +242,8 @@ Status BuildTable(
|
||||
Slice last_tombstone_start_user_key{};
|
||||
for (range_del_it->SeekToFirst(); range_del_it->Valid();
|
||||
range_del_it->Next()) {
|
||||
// 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();
|
||||
auto tombstone = range_del_it->Tombstone();
|
||||
auto kv = tombstone.Serialize();
|
||||
builder->Add(kv.first.Encode(), kv.second);
|
||||
InternalKey tombstone_end = tombstone.SerializeEndKey();
|
||||
meta->UpdateBoundariesForRange(kv.first, tombstone_end, tombstone.seq_,
|
||||
@@ -281,7 +255,7 @@ Status BuildTable(
|
||||
SizeApproximationOptions approx_opts;
|
||||
approx_opts.files_size_error_margin = 0.1;
|
||||
meta->compensated_range_deletion_size += versions->ApproximateSize(
|
||||
approx_opts, tboptions.read_options, version, kv.first.Encode(),
|
||||
approx_opts, read_options, version, kv.first.Encode(),
|
||||
tombstone_end.Encode(), 0 /* start_level */, -1 /* end_level */,
|
||||
TableReaderCaller::kFlush);
|
||||
}
|
||||
@@ -293,21 +267,18 @@ Status BuildTable(
|
||||
TEST_SYNC_POINT("BuildTable:BeforeFinishBuildTable");
|
||||
const bool empty = builder->IsEmpty();
|
||||
if (num_input_entries != nullptr) {
|
||||
assert(c_iter.HasNumInputEntryScanned());
|
||||
*num_input_entries =
|
||||
c_iter.NumInputEntryScanned() + num_unfragmented_tombstones;
|
||||
c_iter.num_input_entry_scanned() + num_unfragmented_tombstones;
|
||||
}
|
||||
if (!s.ok() || empty) {
|
||||
builder->Abandon();
|
||||
} else {
|
||||
SeqnoToTimeMapping relevant_mapping;
|
||||
relevant_mapping.CopyFromSeqnoRange(seqno_to_time_mapping,
|
||||
meta->fd.smallest_seqno,
|
||||
meta->fd.largest_seqno);
|
||||
relevant_mapping.SetCapacity(kMaxSeqnoTimePairsPerSST);
|
||||
relevant_mapping.Enforce(tboptions.file_creation_time);
|
||||
std::string seqno_time_mapping_str;
|
||||
seqno_to_time_mapping.Encode(
|
||||
seqno_time_mapping_str, meta->fd.smallest_seqno,
|
||||
meta->fd.largest_seqno, meta->file_creation_time);
|
||||
builder->SetSeqnoTimeTableProperties(
|
||||
relevant_mapping,
|
||||
seqno_time_mapping_str,
|
||||
ioptions.compaction_style == CompactionStyle::kCompactionStyleFIFO
|
||||
? meta->file_creation_time
|
||||
: meta->oldest_ancester_time);
|
||||
@@ -354,16 +325,13 @@ Status BuildTable(
|
||||
|
||||
// Finish and check for file errors
|
||||
TEST_SYNC_POINT("BuildTable:BeforeSyncTable");
|
||||
IOOptions opts;
|
||||
*io_status =
|
||||
WritableFileWriter::PrepareIOOptions(tboptions.write_options, opts);
|
||||
if (s.ok() && io_status->ok() && !empty) {
|
||||
if (s.ok() && !empty) {
|
||||
StopWatch sw(ioptions.clock, ioptions.stats, TABLE_SYNC_MICROS);
|
||||
*io_status = file_writer->Sync(opts, ioptions.use_fsync);
|
||||
*io_status = file_writer->Sync(ioptions.use_fsync);
|
||||
}
|
||||
TEST_SYNC_POINT("BuildTable:BeforeCloseTableFile");
|
||||
if (s.ok() && io_status->ok() && !empty) {
|
||||
*io_status = file_writer->Close(opts);
|
||||
*io_status = file_writer->Close();
|
||||
}
|
||||
if (s.ok() && io_status->ok() && !empty) {
|
||||
// Add the checksum information to file metadata.
|
||||
@@ -386,8 +354,6 @@ Status BuildTable(
|
||||
s = *io_status;
|
||||
}
|
||||
|
||||
// TODO(yuzhangyu): handle the key copy in the blob when ts should be
|
||||
// stripped.
|
||||
if (blob_file_builder) {
|
||||
if (s.ok()) {
|
||||
s = blob_file_builder->Finish();
|
||||
@@ -407,9 +373,9 @@ Status BuildTable(
|
||||
// No matter whether use_direct_io_for_flush_and_compaction is true,
|
||||
// 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.prefix_extractor, nullptr,
|
||||
read_options, file_options, tboptions.internal_comparator, *meta,
|
||||
nullptr /* range_del_agg */, mutable_cf_options.prefix_extractor,
|
||||
nullptr,
|
||||
(internal_stats == nullptr) ? nullptr
|
||||
: internal_stats->GetFileReadHist(0),
|
||||
TableReaderCaller::kFlush, /*arena=*/nullptr,
|
||||
@@ -422,6 +388,7 @@ Status BuildTable(
|
||||
s = it->status();
|
||||
if (s.ok() && paranoid_file_checks) {
|
||||
OutputValidator file_validator(tboptions.internal_comparator,
|
||||
/*enable_order_check=*/true,
|
||||
/*enable_hash=*/true);
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
// Generate a rolling 64-bit hash of the key and values
|
||||
@@ -446,13 +413,8 @@ Status BuildTable(
|
||||
constexpr IODebugContext* dbg = nullptr;
|
||||
|
||||
if (table_file_created) {
|
||||
IOOptions opts;
|
||||
Status prepare =
|
||||
WritableFileWriter::PrepareIOOptions(tboptions.write_options, opts);
|
||||
if (prepare.ok()) {
|
||||
Status ignored = fs->DeleteFile(fname, opts, dbg);
|
||||
ignored.PermitUncheckedError();
|
||||
}
|
||||
Status ignored = fs->DeleteFile(fname, IOOptions(), dbg);
|
||||
ignored.PermitUncheckedError();
|
||||
}
|
||||
|
||||
assert(blob_file_additions || blob_file_paths.empty());
|
||||
|
||||
+4
-3
@@ -50,11 +50,11 @@ TableBuilder* NewTableBuilder(const TableBuilderOptions& tboptions,
|
||||
//
|
||||
// @param column_family_name Name of the column family that is also identified
|
||||
// by column_family_id, or empty string if unknown.
|
||||
Status BuildTable(
|
||||
extern Status BuildTable(
|
||||
const std::string& dbname, VersionSet* versions,
|
||||
const ImmutableDBOptions& db_options, const TableBuilderOptions& tboptions,
|
||||
const FileOptions& file_options, TableCache* table_cache,
|
||||
InternalIterator* iter,
|
||||
const FileOptions& file_options, const ReadOptions& read_options,
|
||||
TableCache* table_cache, InternalIterator* iter,
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
|
||||
@@ -66,6 +66,7 @@ Status BuildTable(
|
||||
BlobFileCreationReason blob_creation_reason,
|
||||
const SeqnoToTimeMapping& seqno_to_time_mapping,
|
||||
EventLogger* event_logger = nullptr, int job_id = 0,
|
||||
const Env::IOPriority io_priority = Env::IO_HIGH,
|
||||
TableProperties* table_properties = nullptr,
|
||||
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET,
|
||||
const std::string* full_history_ts_low = nullptr,
|
||||
|
||||
@@ -45,8 +45,6 @@
|
||||
#include "rocksdb/utilities/transaction_db.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "util/stderr_logger.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
using ROCKSDB_NAMESPACE::BackupEngine;
|
||||
@@ -79,7 +77,6 @@ using ROCKSDB_NAMESPACE::EnvOptions;
|
||||
using ROCKSDB_NAMESPACE::FileLock;
|
||||
using ROCKSDB_NAMESPACE::FilterPolicy;
|
||||
using ROCKSDB_NAMESPACE::FlushOptions;
|
||||
using ROCKSDB_NAMESPACE::HistogramData;
|
||||
using ROCKSDB_NAMESPACE::HyperClockCacheOptions;
|
||||
using ROCKSDB_NAMESPACE::InfoLogLevel;
|
||||
using ROCKSDB_NAMESPACE::IngestExternalFileOptions;
|
||||
@@ -116,19 +113,16 @@ using ROCKSDB_NAMESPACE::Snapshot;
|
||||
using ROCKSDB_NAMESPACE::SstFileMetaData;
|
||||
using ROCKSDB_NAMESPACE::SstFileWriter;
|
||||
using ROCKSDB_NAMESPACE::Status;
|
||||
using ROCKSDB_NAMESPACE::StderrLogger;
|
||||
using ROCKSDB_NAMESPACE::TablePropertiesCollectorFactory;
|
||||
using ROCKSDB_NAMESPACE::Transaction;
|
||||
using ROCKSDB_NAMESPACE::TransactionDB;
|
||||
using ROCKSDB_NAMESPACE::TransactionDBOptions;
|
||||
using ROCKSDB_NAMESPACE::TransactionLogIterator;
|
||||
using ROCKSDB_NAMESPACE::TransactionOptions;
|
||||
using ROCKSDB_NAMESPACE::WaitForCompactOptions;
|
||||
using ROCKSDB_NAMESPACE::WALRecoveryMode;
|
||||
using ROCKSDB_NAMESPACE::WritableFile;
|
||||
using ROCKSDB_NAMESPACE::WriteBatch;
|
||||
using ROCKSDB_NAMESPACE::WriteBatchWithIndex;
|
||||
using ROCKSDB_NAMESPACE::WriteBufferManager;
|
||||
using ROCKSDB_NAMESPACE::WriteOptions;
|
||||
|
||||
using std::unordered_set;
|
||||
@@ -223,9 +217,6 @@ struct rocksdb_memory_allocator_t {
|
||||
struct rocksdb_cache_t {
|
||||
std::shared_ptr<Cache> rep;
|
||||
};
|
||||
struct rocksdb_write_buffer_manager_t {
|
||||
std::shared_ptr<WriteBufferManager> rep;
|
||||
};
|
||||
struct rocksdb_livefiles_t {
|
||||
std::vector<LiveFileMetaData> rep;
|
||||
};
|
||||
@@ -283,19 +274,11 @@ struct rocksdb_optimistictransactiondb_t {
|
||||
struct rocksdb_optimistictransaction_options_t {
|
||||
OptimisticTransactionOptions rep;
|
||||
};
|
||||
struct rocksdb_wait_for_compact_options_t {
|
||||
WaitForCompactOptions rep;
|
||||
};
|
||||
|
||||
struct rocksdb_compactionfiltercontext_t {
|
||||
CompactionFilter::Context rep;
|
||||
};
|
||||
|
||||
struct rocksdb_statistics_histogram_data_t {
|
||||
rocksdb_statistics_histogram_data_t() : rep() {}
|
||||
HistogramData rep;
|
||||
};
|
||||
|
||||
struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
void* state_;
|
||||
void (*destructor_)(void*);
|
||||
@@ -1074,36 +1057,6 @@ rocksdb_column_family_handle_t* rocksdb_create_column_family(
|
||||
return handle;
|
||||
}
|
||||
|
||||
rocksdb_column_family_handle_t** rocksdb_create_column_families(
|
||||
rocksdb_t* db, const rocksdb_options_t* column_family_options,
|
||||
int num_column_families, const char* const* column_family_names,
|
||||
size_t* lencfs, char** errptr) {
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
std::vector<std::string> names;
|
||||
for (int i = 0; i != num_column_families; ++i) {
|
||||
names.push_back(std::string(column_family_names[i]));
|
||||
}
|
||||
SaveError(errptr, db->rep->CreateColumnFamilies(
|
||||
ColumnFamilyOptions(column_family_options->rep), names,
|
||||
&handles));
|
||||
|
||||
*lencfs = handles.size();
|
||||
rocksdb_column_family_handle_t** c_handles =
|
||||
static_cast<rocksdb_column_family_handle_t**>(
|
||||
malloc(sizeof(rocksdb_column_family_handle_t*) * handles.size()));
|
||||
for (size_t i = 0; i != handles.size(); ++i) {
|
||||
c_handles[i] = new rocksdb_column_family_handle_t;
|
||||
c_handles[i]->rep = handles[i];
|
||||
}
|
||||
|
||||
return c_handles;
|
||||
}
|
||||
|
||||
void rocksdb_create_column_families_destroy(
|
||||
rocksdb_column_family_handle_t** list) {
|
||||
free(list);
|
||||
}
|
||||
|
||||
rocksdb_column_family_handle_t* rocksdb_create_column_family_with_ttl(
|
||||
rocksdb_t* db, const rocksdb_options_t* column_family_options,
|
||||
const char* column_family_name, int ttl, char** errptr) {
|
||||
@@ -1687,11 +1640,6 @@ void rocksdb_release_snapshot(rocksdb_t* db,
|
||||
delete snapshot;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_snapshot_get_sequence_number(
|
||||
const rocksdb_snapshot_t* snapshot) {
|
||||
return snapshot->rep->GetSequenceNumber();
|
||||
}
|
||||
|
||||
char* rocksdb_property_value(rocksdb_t* db, const char* propname) {
|
||||
std::string tmp;
|
||||
if (db->rep->GetProperty(Slice(propname), &tmp)) {
|
||||
@@ -1879,8 +1827,9 @@ void rocksdb_disable_file_deletions(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->DisableFileDeletions());
|
||||
}
|
||||
|
||||
void rocksdb_enable_file_deletions(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->EnableFileDeletions());
|
||||
void rocksdb_enable_file_deletions(rocksdb_t* db, unsigned char force,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->EnableFileDeletions(force));
|
||||
}
|
||||
|
||||
void rocksdb_destroy_db(const rocksdb_options_t* options, const char* name,
|
||||
@@ -2915,33 +2864,6 @@ void rocksdb_options_set_db_paths(rocksdb_options_t* opt,
|
||||
opt->rep.db_paths = db_paths;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_cf_paths(rocksdb_options_t* opt,
|
||||
const rocksdb_dbpath_t** dbpath_values,
|
||||
size_t num_paths) {
|
||||
std::vector<DbPath> cf_paths(num_paths);
|
||||
for (size_t i = 0; i < num_paths; ++i) {
|
||||
cf_paths[i] = dbpath_values[i]->rep;
|
||||
}
|
||||
opt->rep.cf_paths = cf_paths;
|
||||
}
|
||||
|
||||
rocksdb_logger_t* rocksdb_logger_create_stderr_logger(int log_level,
|
||||
const char* prefix) {
|
||||
rocksdb_logger_t* logger = new rocksdb_logger_t;
|
||||
|
||||
if (prefix) {
|
||||
logger->rep = std::make_shared<StderrLogger>(
|
||||
static_cast<InfoLogLevel>(log_level), prefix);
|
||||
} else {
|
||||
logger->rep =
|
||||
std::make_shared<StderrLogger>(static_cast<InfoLogLevel>(log_level));
|
||||
}
|
||||
|
||||
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) {
|
||||
opt->rep.env = (env ? env->rep : nullptr);
|
||||
}
|
||||
@@ -2952,12 +2874,6 @@ void rocksdb_options_set_info_log(rocksdb_options_t* opt, rocksdb_logger_t* l) {
|
||||
}
|
||||
}
|
||||
|
||||
rocksdb_logger_t* rocksdb_options_get_info_log(rocksdb_options_t* opt) {
|
||||
rocksdb_logger_t* info_log = new rocksdb_logger_t;
|
||||
info_log->rep = opt->rep.info_log;
|
||||
return info_log;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_info_log_level(rocksdb_options_t* opt, int v) {
|
||||
opt->rep.info_log_level = static_cast<InfoLogLevel>(v);
|
||||
}
|
||||
@@ -2979,11 +2895,6 @@ void rocksdb_options_set_write_buffer_size(rocksdb_options_t* opt, size_t s) {
|
||||
opt->rep.write_buffer_size = s;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_write_buffer_manager(
|
||||
rocksdb_options_t* opt, rocksdb_write_buffer_manager_t* wbm) {
|
||||
opt->rep.write_buffer_manager = wbm->rep;
|
||||
}
|
||||
|
||||
size_t rocksdb_options_get_write_buffer_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.write_buffer_size;
|
||||
}
|
||||
@@ -3078,51 +2989,10 @@ void rocksdb_options_set_max_bytes_for_level_multiplier_additional(
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_options_set_ttl(rocksdb_options_t* opt, uint64_t seconds) {
|
||||
opt->rep.ttl = seconds;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_ttl(rocksdb_options_t* opt) {
|
||||
return opt->rep.ttl;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_periodic_compaction_seconds(rocksdb_options_t* opt,
|
||||
uint64_t seconds) {
|
||||
opt->rep.periodic_compaction_seconds = seconds;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_periodic_compaction_seconds(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.periodic_compaction_seconds;
|
||||
}
|
||||
|
||||
void rocksdb_options_enable_statistics(rocksdb_options_t* opt) {
|
||||
opt->rep.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
}
|
||||
|
||||
void rocksdb_options_set_statistics_level(rocksdb_options_t* opt, int level) {
|
||||
if (!opt->rep.statistics) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (level < rocksdb_statistics_level_disable_all) {
|
||||
level = rocksdb_statistics_level_disable_all;
|
||||
}
|
||||
if (level > rocksdb_statistics_level_all) {
|
||||
level = rocksdb_statistics_level_all;
|
||||
}
|
||||
opt->rep.statistics->set_stats_level(
|
||||
static_cast<ROCKSDB_NAMESPACE::StatsLevel>(level));
|
||||
}
|
||||
|
||||
int rocksdb_options_get_statistics_level(rocksdb_options_t* opt) {
|
||||
if (!opt->rep.statistics) {
|
||||
return ROCKSDB_NAMESPACE::StatsLevel::kDisableAll;
|
||||
}
|
||||
|
||||
return static_cast<int>(opt->rep.statistics->get_stats_level());
|
||||
}
|
||||
|
||||
void rocksdb_options_set_skip_stats_update_on_db_open(rocksdb_options_t* opt,
|
||||
unsigned char val) {
|
||||
opt->rep.skip_stats_update_on_db_open = val;
|
||||
@@ -3148,7 +3018,7 @@ void rocksdb_options_set_enable_blob_files(rocksdb_options_t* opt,
|
||||
unsigned char val) {
|
||||
opt->rep.enable_blob_files = val;
|
||||
}
|
||||
ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_enable_blob_files(
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_enable_blob_files(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.enable_blob_files;
|
||||
}
|
||||
@@ -3520,6 +3390,35 @@ unsigned char rocksdb_options_get_advise_random_on_open(
|
||||
return opt->rep.advise_random_on_open;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_access_hint_on_compaction_start(rocksdb_options_t* opt,
|
||||
int v) {
|
||||
switch (v) {
|
||||
case 0:
|
||||
opt->rep.access_hint_on_compaction_start =
|
||||
ROCKSDB_NAMESPACE::Options::NONE;
|
||||
break;
|
||||
case 1:
|
||||
opt->rep.access_hint_on_compaction_start =
|
||||
ROCKSDB_NAMESPACE::Options::NORMAL;
|
||||
break;
|
||||
case 2:
|
||||
opt->rep.access_hint_on_compaction_start =
|
||||
ROCKSDB_NAMESPACE::Options::SEQUENTIAL;
|
||||
break;
|
||||
case 3:
|
||||
opt->rep.access_hint_on_compaction_start =
|
||||
ROCKSDB_NAMESPACE::Options::WILLNEED;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
int rocksdb_options_get_access_hint_on_compaction_start(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.access_hint_on_compaction_start;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_use_adaptive_mutex(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.use_adaptive_mutex = v;
|
||||
@@ -3841,21 +3740,16 @@ void rocksdb_options_set_hash_link_list_rep(rocksdb_options_t* opt,
|
||||
ROCKSDB_NAMESPACE::NewHashLinkListRepFactory(bucket_count));
|
||||
}
|
||||
|
||||
void rocksdb_options_set_plain_table_factory(
|
||||
rocksdb_options_t* opt, uint32_t user_key_len, int bloom_bits_per_key,
|
||||
double hash_table_ratio, size_t index_sparseness, size_t huge_page_tlb_size,
|
||||
char encoding_type, unsigned char full_scan_mode,
|
||||
unsigned char store_index_in_file) {
|
||||
void rocksdb_options_set_plain_table_factory(rocksdb_options_t* opt,
|
||||
uint32_t user_key_len,
|
||||
int bloom_bits_per_key,
|
||||
double hash_table_ratio,
|
||||
size_t index_sparseness) {
|
||||
ROCKSDB_NAMESPACE::PlainTableOptions options;
|
||||
options.user_key_len = user_key_len;
|
||||
options.bloom_bits_per_key = bloom_bits_per_key;
|
||||
options.hash_table_ratio = hash_table_ratio;
|
||||
options.index_sparseness = index_sparseness;
|
||||
options.huge_page_tlb_size = huge_page_tlb_size;
|
||||
options.encoding_type =
|
||||
static_cast<ROCKSDB_NAMESPACE::EncodingType>(encoding_type);
|
||||
options.full_scan_mode = full_scan_mode;
|
||||
options.store_index_in_file = store_index_in_file;
|
||||
|
||||
ROCKSDB_NAMESPACE::TableFactory* factory =
|
||||
ROCKSDB_NAMESPACE::NewPlainTableFactory(options);
|
||||
@@ -3933,26 +3827,6 @@ char* rocksdb_options_statistics_get_string(rocksdb_options_t* opt) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_statistics_get_ticker_count(rocksdb_options_t* opt,
|
||||
uint32_t ticker_type) {
|
||||
ROCKSDB_NAMESPACE::Statistics* statistics = opt->rep.statistics.get();
|
||||
if (statistics) {
|
||||
return statistics->getTickerCount(ticker_type);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rocksdb_options_statistics_get_histogram_data(
|
||||
rocksdb_options_t* opt, uint32_t type,
|
||||
rocksdb_statistics_histogram_data_t* const data) {
|
||||
ROCKSDB_NAMESPACE::Statistics* statistics = opt->rep.statistics.get();
|
||||
if (statistics) {
|
||||
statistics->histogramData(type, &data->rep);
|
||||
} else {
|
||||
*data = rocksdb_statistics_histogram_data_t{};
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_options_set_ratelimiter(rocksdb_options_t* opt,
|
||||
rocksdb_ratelimiter_t* limiter) {
|
||||
if (limiter) {
|
||||
@@ -3995,26 +3869,6 @@ rocksdb_ratelimiter_t* rocksdb_ratelimiter_create(int64_t rate_bytes_per_sec,
|
||||
return rate_limiter;
|
||||
}
|
||||
|
||||
rocksdb_ratelimiter_t* rocksdb_ratelimiter_create_auto_tuned(
|
||||
int64_t rate_bytes_per_sec, int64_t refill_period_us, int32_t fairness) {
|
||||
rocksdb_ratelimiter_t* rate_limiter = new rocksdb_ratelimiter_t;
|
||||
rate_limiter->rep.reset(NewGenericRateLimiter(rate_bytes_per_sec,
|
||||
refill_period_us, fairness,
|
||||
RateLimiter::Mode::kWritesOnly,
|
||||
true)); // auto_tuned
|
||||
return rate_limiter;
|
||||
}
|
||||
|
||||
rocksdb_ratelimiter_t* rocksdb_ratelimiter_create_with_mode(
|
||||
int64_t rate_bytes_per_sec, int64_t refill_period_us, int32_t fairness,
|
||||
int mode, bool auto_tuned) {
|
||||
rocksdb_ratelimiter_t* rate_limiter = new rocksdb_ratelimiter_t;
|
||||
rate_limiter->rep.reset(
|
||||
NewGenericRateLimiter(rate_bytes_per_sec, refill_period_us, fairness,
|
||||
static_cast<RateLimiter::Mode>(mode), auto_tuned));
|
||||
return rate_limiter;
|
||||
}
|
||||
|
||||
void rocksdb_ratelimiter_destroy(rocksdb_ratelimiter_t* limiter) {
|
||||
delete limiter;
|
||||
}
|
||||
@@ -4611,7 +4465,7 @@ void rocksdb_readoptions_set_io_timeout(rocksdb_readoptions_t* opt,
|
||||
opt->rep.io_timeout = std::chrono::microseconds(microseconds);
|
||||
}
|
||||
|
||||
ROCKSDB_LIBRARY_API uint64_t
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_readoptions_get_io_timeout(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.io_timeout.count();
|
||||
}
|
||||
@@ -4647,11 +4501,6 @@ void rocksdb_readoptions_set_iter_start_ts(rocksdb_readoptions_t* opt,
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_auto_readahead_size(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.auto_readahead_size = v;
|
||||
}
|
||||
|
||||
rocksdb_writeoptions_t* rocksdb_writeoptions_create() {
|
||||
return new rocksdb_writeoptions_t;
|
||||
}
|
||||
@@ -4924,60 +4773,6 @@ size_t rocksdb_cache_get_occupancy_count(const rocksdb_cache_t* cache) {
|
||||
return cache->rep->GetOccupancyCount();
|
||||
}
|
||||
|
||||
rocksdb_write_buffer_manager_t* rocksdb_write_buffer_manager_create(
|
||||
size_t buffer_size, bool allow_stall) {
|
||||
rocksdb_write_buffer_manager_t* wbm = new rocksdb_write_buffer_manager_t;
|
||||
wbm->rep.reset(new WriteBufferManager(buffer_size, {}, allow_stall));
|
||||
return wbm;
|
||||
}
|
||||
|
||||
rocksdb_write_buffer_manager_t* rocksdb_write_buffer_manager_create_with_cache(
|
||||
size_t buffer_size, const rocksdb_cache_t* cache, bool allow_stall) {
|
||||
rocksdb_write_buffer_manager_t* wbm = new rocksdb_write_buffer_manager_t;
|
||||
wbm->rep.reset(new WriteBufferManager(buffer_size, cache->rep, allow_stall));
|
||||
return wbm;
|
||||
}
|
||||
|
||||
void rocksdb_write_buffer_manager_destroy(rocksdb_write_buffer_manager_t* wbm) {
|
||||
delete wbm;
|
||||
}
|
||||
|
||||
bool rocksdb_write_buffer_manager_enabled(rocksdb_write_buffer_manager_t* wbm) {
|
||||
return wbm->rep->enabled();
|
||||
}
|
||||
|
||||
bool rocksdb_write_buffer_manager_cost_to_cache(
|
||||
rocksdb_write_buffer_manager_t* wbm) {
|
||||
return wbm->rep->cost_to_cache();
|
||||
}
|
||||
|
||||
size_t rocksdb_write_buffer_manager_memory_usage(
|
||||
rocksdb_write_buffer_manager_t* wbm) {
|
||||
return wbm->rep->memory_usage();
|
||||
}
|
||||
|
||||
size_t rocksdb_write_buffer_manager_mutable_memtable_memory_usage(
|
||||
rocksdb_write_buffer_manager_t* wbm) {
|
||||
return wbm->rep->mutable_memtable_memory_usage();
|
||||
}
|
||||
|
||||
size_t rocksdb_write_buffer_manager_dummy_entries_in_cache_usage(
|
||||
rocksdb_write_buffer_manager_t* wbm) {
|
||||
return wbm->rep->dummy_entries_in_cache_usage();
|
||||
}
|
||||
size_t rocksdb_write_buffer_manager_buffer_size(
|
||||
rocksdb_write_buffer_manager_t* wbm) {
|
||||
return wbm->rep->buffer_size();
|
||||
}
|
||||
void rocksdb_write_buffer_manager_set_buffer_size(
|
||||
rocksdb_write_buffer_manager_t* wbm, size_t new_size) {
|
||||
wbm->rep->SetBufferSize(new_size);
|
||||
}
|
||||
ROCKSDB_LIBRARY_API void rocksdb_write_buffer_manager_set_allow_stall(
|
||||
rocksdb_write_buffer_manager_t* wbm, bool new_allow_stall) {
|
||||
wbm->rep->SetAllowStall(new_allow_stall);
|
||||
}
|
||||
|
||||
rocksdb_dbpath_t* rocksdb_dbpath_create(const char* path,
|
||||
uint64_t target_size) {
|
||||
rocksdb_dbpath_t* result = new rocksdb_dbpath_t;
|
||||
@@ -5364,8 +5159,7 @@ rocksdb_fifo_compaction_options_t* rocksdb_fifo_compaction_options_create() {
|
||||
}
|
||||
|
||||
void rocksdb_fifo_compaction_options_set_allow_compaction(
|
||||
rocksdb_fifo_compaction_options_t* fifo_opts,
|
||||
unsigned char allow_compaction) {
|
||||
rocksdb_fifo_compaction_options_t* fifo_opts, unsigned char allow_compaction) {
|
||||
fifo_opts->rep.allow_compaction = allow_compaction;
|
||||
}
|
||||
|
||||
@@ -5444,7 +5238,9 @@ uint64_t rocksdb_livefiles_deletions(const rocksdb_livefiles_t* lf, int index) {
|
||||
return lf->rep[index].num_deletions;
|
||||
}
|
||||
|
||||
void rocksdb_livefiles_destroy(const rocksdb_livefiles_t* lf) { delete lf; }
|
||||
extern void rocksdb_livefiles_destroy(const rocksdb_livefiles_t* lf) {
|
||||
delete lf;
|
||||
}
|
||||
|
||||
void rocksdb_get_options_from_string(const rocksdb_options_t* base_options,
|
||||
const char* opts_str,
|
||||
@@ -5574,11 +5370,6 @@ char* rocksdb_sst_file_metadata_get_relative_filename(
|
||||
return strdup(file_meta->rep->relative_filename.c_str());
|
||||
}
|
||||
|
||||
char* rocksdb_sst_file_metadata_get_directory(
|
||||
rocksdb_sst_file_metadata_t* file_meta) {
|
||||
return strdup(file_meta->rep->directory.c_str());
|
||||
}
|
||||
|
||||
uint64_t rocksdb_sst_file_metadata_get_size(
|
||||
rocksdb_sst_file_metadata_t* file_meta) {
|
||||
return file_meta->rep->size;
|
||||
@@ -5797,20 +5588,6 @@ int rocksdb_transactiondb_property_int(rocksdb_transactiondb_t* db,
|
||||
}
|
||||
}
|
||||
|
||||
rocksdb_t* rocksdb_transactiondb_get_base_db(rocksdb_transactiondb_t* txn_db) {
|
||||
DB* base_db = txn_db->rep->GetBaseDB();
|
||||
|
||||
if (base_db != nullptr) {
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
result->rep = base_db;
|
||||
return result;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_close_base_db(rocksdb_t* base_db) { delete base_db; }
|
||||
|
||||
rocksdb_transaction_t* rocksdb_transaction_begin(
|
||||
rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* write_options,
|
||||
@@ -6788,114 +6565,4 @@ void rocksdb_enable_manual_compaction(rocksdb_t* db) {
|
||||
db->rep->EnableManualCompaction();
|
||||
}
|
||||
|
||||
rocksdb_statistics_histogram_data_t*
|
||||
rocksdb_statistics_histogram_data_create() {
|
||||
return new rocksdb_statistics_histogram_data_t{};
|
||||
}
|
||||
|
||||
void rocksdb_statistics_histogram_data_destroy(
|
||||
rocksdb_statistics_histogram_data_t* data) {
|
||||
delete data;
|
||||
}
|
||||
|
||||
double rocksdb_statistics_histogram_data_get_median(
|
||||
rocksdb_statistics_histogram_data_t* data) {
|
||||
return data->rep.median;
|
||||
}
|
||||
|
||||
double rocksdb_statistics_histogram_data_get_p95(
|
||||
rocksdb_statistics_histogram_data_t* data) {
|
||||
return data->rep.percentile95;
|
||||
}
|
||||
|
||||
double rocksdb_statistics_histogram_data_get_p99(
|
||||
rocksdb_statistics_histogram_data_t* data) {
|
||||
return data->rep.percentile99;
|
||||
}
|
||||
|
||||
double rocksdb_statistics_histogram_data_get_average(
|
||||
rocksdb_statistics_histogram_data_t* data) {
|
||||
return data->rep.average;
|
||||
}
|
||||
|
||||
double rocksdb_statistics_histogram_data_get_std_dev(
|
||||
rocksdb_statistics_histogram_data_t* data) {
|
||||
return data->rep.standard_deviation;
|
||||
}
|
||||
|
||||
double rocksdb_statistics_histogram_data_get_max(
|
||||
rocksdb_statistics_histogram_data_t* data) {
|
||||
return data->rep.max;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_statistics_histogram_data_get_count(
|
||||
rocksdb_statistics_histogram_data_t* data) {
|
||||
return data->rep.count;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_statistics_histogram_data_get_sum(
|
||||
rocksdb_statistics_histogram_data_t* data) {
|
||||
return data->rep.sum;
|
||||
}
|
||||
|
||||
double rocksdb_statistics_histogram_data_get_min(
|
||||
rocksdb_statistics_histogram_data_t* data) {
|
||||
return data->rep.min;
|
||||
}
|
||||
|
||||
void rocksdb_wait_for_compact(rocksdb_t* db,
|
||||
rocksdb_wait_for_compact_options_t* options,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->WaitForCompact(options->rep));
|
||||
}
|
||||
|
||||
rocksdb_wait_for_compact_options_t* rocksdb_wait_for_compact_options_create() {
|
||||
return new rocksdb_wait_for_compact_options_t;
|
||||
}
|
||||
|
||||
void rocksdb_wait_for_compact_options_destroy(
|
||||
rocksdb_wait_for_compact_options_t* opt) {
|
||||
delete opt;
|
||||
}
|
||||
|
||||
void rocksdb_wait_for_compact_options_set_abort_on_pause(
|
||||
rocksdb_wait_for_compact_options_t* opt, unsigned char v) {
|
||||
opt->rep.abort_on_pause = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_wait_for_compact_options_get_abort_on_pause(
|
||||
rocksdb_wait_for_compact_options_t* opt) {
|
||||
return opt->rep.abort_on_pause;
|
||||
}
|
||||
|
||||
void rocksdb_wait_for_compact_options_set_flush(
|
||||
rocksdb_wait_for_compact_options_t* opt, unsigned char v) {
|
||||
opt->rep.flush = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_wait_for_compact_options_get_flush(
|
||||
rocksdb_wait_for_compact_options_t* opt) {
|
||||
return opt->rep.flush;
|
||||
}
|
||||
|
||||
void rocksdb_wait_for_compact_options_set_close_db(
|
||||
rocksdb_wait_for_compact_options_t* opt, unsigned char v) {
|
||||
opt->rep.close_db = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_wait_for_compact_options_get_close_db(
|
||||
rocksdb_wait_for_compact_options_t* opt) {
|
||||
return opt->rep.close_db;
|
||||
}
|
||||
|
||||
void rocksdb_wait_for_compact_options_set_timeout(
|
||||
rocksdb_wait_for_compact_options_t* opt, uint64_t microseconds) {
|
||||
opt->rep.timeout = std::chrono::microseconds(microseconds);
|
||||
}
|
||||
|
||||
uint64_t rocksdb_wait_for_compact_options_get_timeout(
|
||||
rocksdb_wait_for_compact_options_t* opt) {
|
||||
return opt->rep.timeout.count();
|
||||
}
|
||||
|
||||
} // end extern "C"
|
||||
+18
-259
@@ -3,14 +3,15 @@
|
||||
found in the LICENSE file. See the AUTHORS file for names of contributors. */
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
#include "rocksdb/c.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "rocksdb/c.h"
|
||||
#ifndef OS_WIN
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@@ -375,11 +376,6 @@ static rocksdb_t* CheckCompaction(rocksdb_t* db, rocksdb_options_t* options,
|
||||
|
||||
// Force compaction
|
||||
rocksdb_compact_range(db, NULL, 0, NULL, 0);
|
||||
rocksdb_wait_for_compact_options_t* wco;
|
||||
wco = rocksdb_wait_for_compact_options_create();
|
||||
rocksdb_wait_for_compact(db, wco, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_wait_for_compact_options_destroy(wco);
|
||||
// should have filtered bar, but not foo
|
||||
CheckGet(db, roptions, "foo", "foovalue");
|
||||
CheckGet(db, roptions, "bar", NULL);
|
||||
@@ -713,16 +709,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_ratelimiter(options, rate_limiter);
|
||||
rocksdb_ratelimiter_destroy(rate_limiter);
|
||||
|
||||
rate_limiter =
|
||||
rocksdb_ratelimiter_create_auto_tuned(1000 * 1024 * 1024, 100 * 1000, 10);
|
||||
rocksdb_options_set_ratelimiter(options, rate_limiter);
|
||||
rocksdb_ratelimiter_destroy(rate_limiter);
|
||||
|
||||
rate_limiter = rocksdb_ratelimiter_create_with_mode(1000 * 1024 * 1024,
|
||||
100 * 1000, 10, 0, true);
|
||||
rocksdb_options_set_ratelimiter(options, rate_limiter);
|
||||
rocksdb_ratelimiter_destroy(rate_limiter);
|
||||
|
||||
roptions = rocksdb_readoptions_create();
|
||||
rocksdb_readoptions_set_verify_checksums(roptions, 1);
|
||||
rocksdb_readoptions_set_fill_cache(roptions, 1);
|
||||
@@ -1484,20 +1470,10 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(cflen == 2);
|
||||
rocksdb_list_column_families_destroy(column_fams, cflen);
|
||||
|
||||
rocksdb_options_t* cf_options_1 = rocksdb_options_create();
|
||||
rocksdb_options_t* cf_options_2 = rocksdb_options_create();
|
||||
|
||||
// use dbpathname2 as the cf_path for "cf1"
|
||||
rocksdb_dbpath_t* dbpath2;
|
||||
char dbpathname2[200];
|
||||
snprintf(dbpathname2, sizeof(dbpathname2), "%s/rocksdb_c_test-%d-dbpath2",
|
||||
GetTempDir(), ((int)geteuid()));
|
||||
dbpath2 = rocksdb_dbpath_create(dbpathname2, 1024 * 1024);
|
||||
const rocksdb_dbpath_t* cf_paths[1] = {dbpath2};
|
||||
rocksdb_options_set_cf_paths(cf_options_2, cf_paths, 1);
|
||||
rocksdb_options_t* cf_options = rocksdb_options_create();
|
||||
|
||||
const char* cf_names[2] = {"default", "cf1"};
|
||||
const rocksdb_options_t* cf_opts[2] = {cf_options_1, cf_options_2};
|
||||
const rocksdb_options_t* cf_opts[2] = {cf_options, cf_options};
|
||||
rocksdb_column_family_handle_t* handles[2];
|
||||
|
||||
LoadAndCheckLatestOptions(dbname, env, false, cache, NULL, 2, cf_names,
|
||||
@@ -1525,37 +1501,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_flushoptions_t* flush_options = rocksdb_flushoptions_create();
|
||||
rocksdb_flushoptions_set_wait(flush_options, 1);
|
||||
rocksdb_flush_cf(db, flush_options, handles[1], &err);
|
||||
|
||||
// make sure all files in "cf1" are under the specified cf path
|
||||
{
|
||||
rocksdb_column_family_metadata_t* cf_meta =
|
||||
rocksdb_get_column_family_metadata_cf(db, handles[1]);
|
||||
size_t cf_file_count = rocksdb_column_family_metadata_get_size(cf_meta);
|
||||
assert(cf_file_count > 0);
|
||||
size_t level_count =
|
||||
rocksdb_column_family_metadata_get_level_count(cf_meta);
|
||||
assert(level_count > 0);
|
||||
for (size_t l = 0; l < level_count; ++l) {
|
||||
rocksdb_level_metadata_t* level_meta =
|
||||
rocksdb_column_family_metadata_get_level_metadata(cf_meta, l);
|
||||
assert(level_meta);
|
||||
|
||||
size_t file_count = rocksdb_level_metadata_get_file_count(level_meta);
|
||||
for (size_t f = 0; f < file_count; ++f) {
|
||||
rocksdb_sst_file_metadata_t* file_meta =
|
||||
rocksdb_level_metadata_get_sst_file_metadata(level_meta, f);
|
||||
assert(file_meta);
|
||||
char* file_path = rocksdb_sst_file_metadata_get_directory(file_meta);
|
||||
assert(strcmp(file_path, dbpathname2) == 0);
|
||||
Free(&file_path);
|
||||
rocksdb_sst_file_metadata_destroy(file_meta);
|
||||
}
|
||||
rocksdb_level_metadata_destroy(level_meta);
|
||||
}
|
||||
|
||||
rocksdb_column_family_metadata_destroy(cf_meta);
|
||||
}
|
||||
|
||||
CheckNoError(err) rocksdb_flushoptions_destroy(flush_options);
|
||||
|
||||
CheckGetCF(db, roptions, handles[1], "foo", "hello");
|
||||
@@ -1719,9 +1664,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
rocksdb_options_destroy(db_options);
|
||||
rocksdb_options_destroy(cf_options_1);
|
||||
rocksdb_options_destroy(cf_options_2);
|
||||
rocksdb_dbpath_destroy(dbpath2);
|
||||
rocksdb_options_destroy(cf_options);
|
||||
}
|
||||
|
||||
StartPhase("prefix");
|
||||
@@ -1731,8 +1674,7 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_prefix_extractor(
|
||||
options, rocksdb_slicetransform_create_fixed_prefix(3));
|
||||
rocksdb_options_set_hash_skip_list_rep(options, 5000, 4, 4);
|
||||
rocksdb_options_set_plain_table_factory(options, 4, 10, 0.75, 16, 0, 0, 0,
|
||||
0);
|
||||
rocksdb_options_set_plain_table_factory(options, 4, 10, 0.75, 16);
|
||||
rocksdb_options_set_allow_concurrent_memtable_write(options, 0);
|
||||
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
@@ -1926,13 +1868,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(2.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(o));
|
||||
|
||||
rocksdb_options_set_periodic_compaction_seconds(o, 100000);
|
||||
CheckCondition(100000 ==
|
||||
rocksdb_options_get_periodic_compaction_seconds(o));
|
||||
|
||||
rocksdb_options_set_ttl(o, 5000);
|
||||
CheckCondition(5000 == rocksdb_options_get_ttl(o));
|
||||
|
||||
rocksdb_options_set_skip_stats_update_on_db_open(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(o));
|
||||
|
||||
@@ -2039,6 +1974,8 @@ 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_access_hint_on_compaction_start(o, 3);
|
||||
CheckCondition(3 == rocksdb_options_get_access_hint_on_compaction_start(o));
|
||||
|
||||
rocksdb_options_set_use_adaptive_mutex(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_use_adaptive_mutex(o));
|
||||
@@ -2123,15 +2060,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(29.0 ==
|
||||
rocksdb_options_get_experimental_mempurge_threshold(o));
|
||||
|
||||
CheckCondition(rocksdb_statistics_level_disable_all ==
|
||||
rocksdb_options_get_statistics_level(o));
|
||||
rocksdb_options_enable_statistics(o);
|
||||
CheckCondition(rocksdb_statistics_level_disable_all !=
|
||||
rocksdb_options_get_statistics_level(o));
|
||||
rocksdb_options_set_statistics_level(o, rocksdb_statistics_level_all);
|
||||
CheckCondition(rocksdb_statistics_level_all ==
|
||||
rocksdb_options_get_statistics_level(o));
|
||||
|
||||
/* Blob Options */
|
||||
rocksdb_options_set_enable_blob_files(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_enable_blob_files(o));
|
||||
@@ -2234,6 +2162,8 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(18 == rocksdb_options_get_stats_dump_period_sec(copy));
|
||||
CheckCondition(5 == rocksdb_options_get_stats_persist_period_sec(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_advise_random_on_open(copy));
|
||||
CheckCondition(3 ==
|
||||
rocksdb_options_get_access_hint_on_compaction_start(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_use_adaptive_mutex(copy));
|
||||
CheckCondition(19 == rocksdb_options_get_bytes_per_sync(copy));
|
||||
CheckCondition(20 == rocksdb_options_get_wal_bytes_per_sync(copy));
|
||||
@@ -2359,16 +2289,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(2.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(o));
|
||||
|
||||
rocksdb_options_set_periodic_compaction_seconds(copy, 8000);
|
||||
CheckCondition(8000 ==
|
||||
rocksdb_options_get_periodic_compaction_seconds(copy));
|
||||
CheckCondition(100000 ==
|
||||
rocksdb_options_get_periodic_compaction_seconds(o));
|
||||
|
||||
rocksdb_options_set_ttl(copy, 8000);
|
||||
CheckCondition(8000 == rocksdb_options_get_ttl(copy));
|
||||
CheckCondition(5000 == rocksdb_options_get_ttl(o));
|
||||
|
||||
rocksdb_options_set_skip_stats_update_on_db_open(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_skip_stats_update_on_db_open(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(o));
|
||||
@@ -2516,6 +2436,11 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(0 == rocksdb_options_get_advise_random_on_open(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_advise_random_on_open(o));
|
||||
|
||||
rocksdb_options_set_access_hint_on_compaction_start(copy, 2);
|
||||
CheckCondition(2 ==
|
||||
rocksdb_options_get_access_hint_on_compaction_start(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_access_hint_on_compaction_start(o));
|
||||
|
||||
rocksdb_options_set_use_adaptive_mutex(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_use_adaptive_mutex(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_use_adaptive_mutex(o));
|
||||
@@ -2784,30 +2709,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_memory_allocator_destroy(allocator);
|
||||
}
|
||||
|
||||
StartPhase("stderr_logger");
|
||||
{
|
||||
rocksdb_options_t* o_no_prefix = rocksdb_options_create();
|
||||
rocksdb_logger_t* no_prefix_logger =
|
||||
rocksdb_logger_create_stderr_logger(3, NULL);
|
||||
rocksdb_options_set_info_log(o_no_prefix, no_prefix_logger);
|
||||
rocksdb_logger_t* no_prefix_info_log =
|
||||
rocksdb_options_get_info_log(o_no_prefix);
|
||||
CheckCondition(no_prefix_info_log != NULL);
|
||||
rocksdb_logger_destroy(no_prefix_logger);
|
||||
rocksdb_logger_destroy(no_prefix_info_log);
|
||||
rocksdb_options_destroy(o_no_prefix);
|
||||
|
||||
rocksdb_options_t* o_prefix = rocksdb_options_create();
|
||||
rocksdb_logger_t* prefix_logger =
|
||||
rocksdb_logger_create_stderr_logger(3, "some prefix");
|
||||
rocksdb_options_set_info_log(o_prefix, prefix_logger);
|
||||
rocksdb_logger_t* prefix_info_log = rocksdb_options_get_info_log(o_prefix);
|
||||
CheckCondition(prefix_info_log != NULL);
|
||||
rocksdb_logger_destroy(prefix_logger);
|
||||
rocksdb_logger_destroy(prefix_info_log);
|
||||
rocksdb_options_destroy(o_prefix);
|
||||
}
|
||||
|
||||
StartPhase("env");
|
||||
{
|
||||
rocksdb_env_t* e;
|
||||
@@ -3220,17 +3121,6 @@ int main(int argc, char** argv) {
|
||||
CheckTxnDBGetCF(txn_db, roptions, cfh, "cf_foo", NULL);
|
||||
CheckTxnDBPinGetCF(txn_db, roptions, cfh, "cf_foo", NULL);
|
||||
|
||||
// memory usage
|
||||
rocksdb_t* base_db = rocksdb_transactiondb_get_base_db(txn_db);
|
||||
rocksdb_memory_consumers_t* consumers = rocksdb_memory_consumers_create();
|
||||
rocksdb_memory_consumers_add_db(consumers, base_db);
|
||||
rocksdb_memory_usage_t* usage =
|
||||
rocksdb_approximate_memory_usage_create(consumers, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_approximate_memory_usage_destroy(usage);
|
||||
rocksdb_memory_consumers_destroy(consumers);
|
||||
rocksdb_transactiondb_close_base_db(base_db);
|
||||
|
||||
// flush
|
||||
rocksdb_flushoptions_t* flush_options = rocksdb_flushoptions_create();
|
||||
rocksdb_flushoptions_set_wait(flush_options, 1);
|
||||
@@ -3486,19 +3376,8 @@ int main(int argc, char** argv) {
|
||||
rocksdb_put(db, woptions, "key", 3, "value", 5, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_column_family_handle_t *cfh1, *cfh2;
|
||||
char** list_const_cf_names = (char**)malloc(2 * sizeof(char*));
|
||||
list_const_cf_names[0] = "txn_db_cf1";
|
||||
list_const_cf_names[1] = "txn_db_cf2";
|
||||
size_t cflen;
|
||||
rocksdb_column_family_handle_t** list_cfh = rocksdb_create_column_families(
|
||||
db, db_options, 2, (const char* const*)list_const_cf_names, &cflen,
|
||||
&err);
|
||||
free(list_const_cf_names);
|
||||
CheckNoError(err);
|
||||
assert(cflen == 2);
|
||||
cfh1 = list_cfh[0];
|
||||
cfh2 = list_cfh[1];
|
||||
rocksdb_create_column_families_destroy(list_cfh);
|
||||
cfh1 = rocksdb_create_column_family(db, db_options, "txn_db_cf1", &err);
|
||||
cfh2 = rocksdb_create_column_family(db, db_options, "txn_db_cf2", &err);
|
||||
txn = rocksdb_optimistictransaction_begin(otxn_db, woptions, otxn_options,
|
||||
NULL);
|
||||
rocksdb_transaction_put_cf(txn, cfh1, "key_cf1", 7, "val_cf1", 7, &err);
|
||||
@@ -3712,126 +3591,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_readoptions_destroy(ropts);
|
||||
}
|
||||
|
||||
StartPhase("statistics");
|
||||
{
|
||||
const uint32_t BYTES_WRITTEN_TICKER = 60;
|
||||
const uint32_t DB_WRITE_HIST = 1;
|
||||
|
||||
rocksdb_statistics_histogram_data_t* hist =
|
||||
rocksdb_statistics_histogram_data_create();
|
||||
{
|
||||
// zero by default
|
||||
CheckCondition(0.0 == rocksdb_statistics_histogram_data_get_median(hist));
|
||||
CheckCondition(0.0 == rocksdb_statistics_histogram_data_get_p95(hist));
|
||||
CheckCondition(0.0 == rocksdb_statistics_histogram_data_get_p99(hist));
|
||||
CheckCondition(0.0 ==
|
||||
rocksdb_statistics_histogram_data_get_average(hist));
|
||||
CheckCondition(0.0 ==
|
||||
rocksdb_statistics_histogram_data_get_std_dev(hist));
|
||||
CheckCondition(0.0 == rocksdb_statistics_histogram_data_get_max(hist));
|
||||
CheckCondition(0 == rocksdb_statistics_histogram_data_get_count(hist));
|
||||
CheckCondition(0 == rocksdb_statistics_histogram_data_get_sum(hist));
|
||||
CheckCondition(0.0 == rocksdb_statistics_histogram_data_get_min(hist));
|
||||
}
|
||||
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_enable_statistics(options);
|
||||
rocksdb_options_set_statistics_level(options, rocksdb_statistics_level_all);
|
||||
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
CheckCondition(0 == rocksdb_options_statistics_get_ticker_count(
|
||||
options, BYTES_WRITTEN_TICKER));
|
||||
rocksdb_options_statistics_get_histogram_data(options, DB_WRITE_HIST, hist);
|
||||
CheckCondition(0.0 == rocksdb_statistics_histogram_data_get_median(hist));
|
||||
CheckCondition(0.0 == rocksdb_statistics_histogram_data_get_p95(hist));
|
||||
CheckCondition(0.0 == rocksdb_statistics_histogram_data_get_p99(hist));
|
||||
CheckCondition(0.0 == rocksdb_statistics_histogram_data_get_average(hist));
|
||||
CheckCondition(0.0 == rocksdb_statistics_histogram_data_get_std_dev(hist));
|
||||
CheckCondition(0.0 == rocksdb_statistics_histogram_data_get_max(hist));
|
||||
CheckCondition(0 == rocksdb_statistics_histogram_data_get_count(hist));
|
||||
CheckCondition(0 == rocksdb_statistics_histogram_data_get_sum(hist));
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
char key = '0' + (char)i;
|
||||
rocksdb_put(db, woptions, &key, 1, "", 1, &err);
|
||||
CheckNoError(err);
|
||||
}
|
||||
CheckCondition(0 != rocksdb_options_statistics_get_ticker_count(
|
||||
options, BYTES_WRITTEN_TICKER));
|
||||
rocksdb_options_statistics_get_histogram_data(options, DB_WRITE_HIST, hist);
|
||||
CheckCondition(0.0 != rocksdb_statistics_histogram_data_get_median(hist));
|
||||
CheckCondition(0.0 != rocksdb_statistics_histogram_data_get_p95(hist));
|
||||
CheckCondition(0.0 != rocksdb_statistics_histogram_data_get_p99(hist));
|
||||
CheckCondition(0.0 != rocksdb_statistics_histogram_data_get_average(hist));
|
||||
CheckCondition(0.0 != rocksdb_statistics_histogram_data_get_std_dev(hist));
|
||||
CheckCondition(0.0 != rocksdb_statistics_histogram_data_get_max(hist));
|
||||
CheckCondition(0 != rocksdb_statistics_histogram_data_get_count(hist));
|
||||
CheckCondition(0 != rocksdb_statistics_histogram_data_get_sum(hist));
|
||||
|
||||
rocksdb_statistics_histogram_data_destroy(hist);
|
||||
}
|
||||
|
||||
StartPhase("wait_for_compact_options");
|
||||
{
|
||||
rocksdb_wait_for_compact_options_t* wco;
|
||||
wco = rocksdb_wait_for_compact_options_create();
|
||||
|
||||
rocksdb_wait_for_compact_options_set_abort_on_pause(wco, 1);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_wait_for_compact_options_get_abort_on_pause(wco));
|
||||
|
||||
rocksdb_wait_for_compact_options_set_flush(wco, 1);
|
||||
CheckCondition(1 == rocksdb_wait_for_compact_options_get_flush(wco));
|
||||
|
||||
rocksdb_wait_for_compact_options_set_close_db(wco, 1);
|
||||
CheckCondition(1 == rocksdb_wait_for_compact_options_get_close_db(wco));
|
||||
|
||||
rocksdb_wait_for_compact_options_set_timeout(wco, 342);
|
||||
CheckCondition(342 == rocksdb_wait_for_compact_options_get_timeout(wco));
|
||||
|
||||
rocksdb_wait_for_compact_options_destroy(wco);
|
||||
}
|
||||
StartPhase("wait_for_compact");
|
||||
{
|
||||
rocksdb_wait_for_compact_options_t* wco;
|
||||
wco = rocksdb_wait_for_compact_options_create();
|
||||
rocksdb_wait_for_compact_options_set_flush(wco, 1);
|
||||
|
||||
rocksdb_wait_for_compact(db, wco, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_wait_for_compact_options_destroy(wco);
|
||||
}
|
||||
|
||||
StartPhase("write_buffer_manager");
|
||||
{
|
||||
rocksdb_cache_t* lru;
|
||||
lru = rocksdb_cache_create_lru(100);
|
||||
|
||||
rocksdb_write_buffer_manager_t* write_buffer_manager;
|
||||
write_buffer_manager =
|
||||
rocksdb_write_buffer_manager_create_with_cache(200, lru, false);
|
||||
|
||||
CheckCondition(true ==
|
||||
rocksdb_write_buffer_manager_enabled(write_buffer_manager));
|
||||
CheckCondition(true == rocksdb_write_buffer_manager_cost_to_cache(
|
||||
write_buffer_manager));
|
||||
CheckCondition(
|
||||
200 == rocksdb_write_buffer_manager_buffer_size(write_buffer_manager));
|
||||
|
||||
rocksdb_write_buffer_manager_set_buffer_size(write_buffer_manager, 300);
|
||||
CheckCondition(
|
||||
300 == rocksdb_write_buffer_manager_buffer_size(write_buffer_manager));
|
||||
|
||||
rocksdb_write_buffer_manager_destroy(write_buffer_manager);
|
||||
rocksdb_cache_destroy(lru);
|
||||
}
|
||||
|
||||
StartPhase("cancel_all_background_work");
|
||||
rocksdb_cancel_all_background_work(db, 1);
|
||||
|
||||
|
||||
+58
-174
@@ -96,16 +96,16 @@ const Comparator* ColumnFamilyHandleImpl::GetComparator() const {
|
||||
return cfd()->user_comparator();
|
||||
}
|
||||
|
||||
void GetInternalTblPropCollFactory(
|
||||
void GetIntTblPropCollectorFactory(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
InternalTblPropCollFactories* internal_tbl_prop_coll_factories) {
|
||||
assert(internal_tbl_prop_coll_factories);
|
||||
IntTblPropCollectorFactories* int_tbl_prop_collector_factories) {
|
||||
assert(int_tbl_prop_collector_factories);
|
||||
|
||||
auto& collector_factories = ioptions.table_properties_collector_factories;
|
||||
for (size_t i = 0; i < ioptions.table_properties_collector_factories.size();
|
||||
++i) {
|
||||
assert(collector_factories[i]);
|
||||
internal_tbl_prop_coll_factories->emplace_back(
|
||||
int_tbl_prop_collector_factories->emplace_back(
|
||||
new UserKeyTablePropertiesCollectorFactory(collector_factories[i]));
|
||||
}
|
||||
}
|
||||
@@ -322,8 +322,8 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
}
|
||||
ROCKS_LOG_WARN(db_options.logger,
|
||||
"Adjust the value to "
|
||||
"level0_stop_writes_trigger(%d) "
|
||||
"level0_slowdown_writes_trigger(%d) "
|
||||
"level0_stop_writes_trigger(%d)"
|
||||
"level0_slowdown_writes_trigger(%d)"
|
||||
"level0_file_num_compaction_trigger(%d)",
|
||||
result.level0_stop_writes_trigger,
|
||||
result.level0_slowdown_writes_trigger,
|
||||
@@ -358,16 +358,16 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
|
||||
if (result.level_compaction_dynamic_level_bytes) {
|
||||
if (result.compaction_style != kCompactionStyleLevel) {
|
||||
ROCKS_LOG_INFO(db_options.info_log.get(),
|
||||
"level_compaction_dynamic_level_bytes only makes sense "
|
||||
ROCKS_LOG_WARN(db_options.info_log.get(),
|
||||
"level_compaction_dynamic_level_bytes only makes sense"
|
||||
"for level-based compaction");
|
||||
result.level_compaction_dynamic_level_bytes = false;
|
||||
} else if (result.cf_paths.size() > 1U) {
|
||||
// we don't yet know how to make both of this feature and multiple
|
||||
// DB path work.
|
||||
ROCKS_LOG_WARN(db_options.info_log.get(),
|
||||
"multiple cf_paths/db_paths and "
|
||||
"level_compaction_dynamic_level_bytes "
|
||||
"multiple cf_paths/db_paths and"
|
||||
"level_compaction_dynamic_level_bytes"
|
||||
"can't be used together");
|
||||
result.level_compaction_dynamic_level_bytes = false;
|
||||
}
|
||||
@@ -382,9 +382,8 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
|
||||
const uint64_t kAdjustedTtl = 30 * 24 * 60 * 60;
|
||||
if (result.ttl == kDefaultTtl) {
|
||||
if (is_block_based_table) {
|
||||
// FIFO also requires max_open_files=-1, which is checked in
|
||||
// ValidateOptions().
|
||||
if (is_block_based_table &&
|
||||
result.compaction_style != kCompactionStyleFIFO) {
|
||||
result.ttl = kAdjustedTtl;
|
||||
} else {
|
||||
result.ttl = 0;
|
||||
@@ -392,35 +391,40 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
}
|
||||
|
||||
const uint64_t kAdjustedPeriodicCompSecs = 30 * 24 * 60 * 60;
|
||||
if (result.compaction_style == kCompactionStyleLevel) {
|
||||
|
||||
// Turn on periodic compactions and set them to occur once every 30 days if
|
||||
// compaction filters are used and periodic_compaction_seconds is set to the
|
||||
// default value.
|
||||
if (result.compaction_style != kCompactionStyleFIFO) {
|
||||
if ((result.compaction_filter != nullptr ||
|
||||
result.compaction_filter_factory != nullptr) &&
|
||||
result.periodic_compaction_seconds == kDefaultPeriodicCompSecs &&
|
||||
is_block_based_table) {
|
||||
result.periodic_compaction_seconds = kAdjustedPeriodicCompSecs;
|
||||
}
|
||||
} else if (result.compaction_style == kCompactionStyleUniversal) {
|
||||
if (result.periodic_compaction_seconds == kDefaultPeriodicCompSecs &&
|
||||
is_block_based_table) {
|
||||
result.periodic_compaction_seconds = kAdjustedPeriodicCompSecs;
|
||||
}
|
||||
} else if (result.compaction_style == kCompactionStyleFIFO) {
|
||||
if (result.periodic_compaction_seconds != kDefaultPeriodicCompSecs) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options.info_log.get(),
|
||||
"periodic_compaction_seconds does not support FIFO compaction. You"
|
||||
"may want to set option TTL instead.");
|
||||
} else {
|
||||
// result.compaction_style == kCompactionStyleFIFO
|
||||
if (result.ttl == 0) {
|
||||
if (is_block_based_table) {
|
||||
if (result.periodic_compaction_seconds == kDefaultPeriodicCompSecs) {
|
||||
result.periodic_compaction_seconds = kAdjustedPeriodicCompSecs;
|
||||
}
|
||||
result.ttl = result.periodic_compaction_seconds;
|
||||
}
|
||||
} else if (result.periodic_compaction_seconds != 0) {
|
||||
result.ttl = std::min(result.ttl, result.periodic_compaction_seconds);
|
||||
}
|
||||
}
|
||||
|
||||
// For universal compaction, `ttl` and `periodic_compaction_seconds` mean the
|
||||
// same thing, take the stricter value.
|
||||
if (result.compaction_style == kCompactionStyleUniversal) {
|
||||
if (result.periodic_compaction_seconds == 0) {
|
||||
result.periodic_compaction_seconds = result.ttl;
|
||||
} else if (result.ttl != 0) {
|
||||
// TTL compactions would work similar to Periodic Compactions in Universal in
|
||||
// most of the cases. So, if ttl is set, execute the periodic compaction
|
||||
// codepath.
|
||||
if (result.compaction_style == kCompactionStyleUniversal && result.ttl != 0) {
|
||||
if (result.periodic_compaction_seconds != 0) {
|
||||
result.periodic_compaction_seconds =
|
||||
std::min(result.ttl, result.periodic_compaction_seconds);
|
||||
} else {
|
||||
result.periodic_compaction_seconds = result.ttl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,7 +480,6 @@ void SuperVersion::Init(ColumnFamilyData* new_cfd, MemTable* new_mem,
|
||||
mem = new_mem;
|
||||
imm = new_imm;
|
||||
current = new_current;
|
||||
full_history_ts_low = cfd->GetFullHistoryTsLow();
|
||||
cfd->Ref();
|
||||
mem->Ref();
|
||||
imm->Ref();
|
||||
@@ -572,7 +575,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
Ref();
|
||||
|
||||
// Convert user defined table properties collector factories to internal ones.
|
||||
GetInternalTblPropCollFactory(ioptions_, &internal_tbl_prop_coll_factories_);
|
||||
GetIntTblPropCollectorFactory(ioptions_, &int_tbl_prop_collector_factories_);
|
||||
|
||||
// if _dummy_versions is nullptr, then this is a dummy column family.
|
||||
if (_dummy_versions != nullptr) {
|
||||
@@ -833,8 +836,8 @@ std::unique_ptr<WriteControllerToken> SetupDelay(
|
||||
return write_controller->GetDelayToken(write_rate);
|
||||
}
|
||||
|
||||
int GetL0FileCountForCompactionSpeedup(int level0_file_num_compaction_trigger,
|
||||
int level0_slowdown_writes_trigger) {
|
||||
int GetL0ThresholdSpeedupCompaction(int level0_file_num_compaction_trigger,
|
||||
int level0_slowdown_writes_trigger) {
|
||||
// SanitizeOptions() ensures it.
|
||||
assert(level0_file_num_compaction_trigger <= level0_slowdown_writes_trigger);
|
||||
|
||||
@@ -864,43 +867,6 @@ int GetL0FileCountForCompactionSpeedup(int level0_file_num_compaction_trigger,
|
||||
return static_cast<int>(res);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t GetPendingCompactionBytesForCompactionSpeedup(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const VersionStorageInfo* vstorage) {
|
||||
// Compaction debt relatively large compared to the stable (bottommost) data
|
||||
// size indicates compaction fell behind.
|
||||
const uint64_t kBottommostSizeDivisor = 8;
|
||||
// Meaningful progress toward the slowdown trigger is another good indication.
|
||||
const uint64_t kSlowdownTriggerDivisor = 4;
|
||||
|
||||
uint64_t bottommost_files_size = 0;
|
||||
for (const auto& level_and_file : vstorage->BottommostFiles()) {
|
||||
bottommost_files_size += level_and_file.second->fd.GetFileSize();
|
||||
}
|
||||
|
||||
// Slowdown trigger might be zero but that means compaction speedup should
|
||||
// always happen (undocumented/historical), so no special treatment is needed.
|
||||
uint64_t slowdown_threshold =
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit /
|
||||
kSlowdownTriggerDivisor;
|
||||
|
||||
// Size of zero, however, should not be used to decide to speedup compaction.
|
||||
if (bottommost_files_size == 0) {
|
||||
return slowdown_threshold;
|
||||
}
|
||||
|
||||
uint64_t size_threshold = bottommost_files_size / kBottommostSizeDivisor;
|
||||
return std::min(size_threshold, slowdown_threshold);
|
||||
}
|
||||
|
||||
uint64_t GetMarkedFileCountForCompactionSpeedup() {
|
||||
// When just one file is marked, it is not clear that parallel compaction will
|
||||
// help the compaction that the user nicely requested to happen sooner. When
|
||||
// multiple files are marked, however, it is pretty clearly helpful, except
|
||||
// for the rare case in which a single compaction grabs all the marked files.
|
||||
return 2;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
std::pair<WriteStallCondition, WriteStallCause>
|
||||
@@ -1056,7 +1022,7 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
} else {
|
||||
assert(write_stall_condition == WriteStallCondition::kNormal);
|
||||
if (vstorage->l0_delay_trigger_count() >=
|
||||
GetL0FileCountForCompactionSpeedup(
|
||||
GetL0ThresholdSpeedupCompaction(
|
||||
mutable_cf_options.level0_file_num_compaction_trigger,
|
||||
mutable_cf_options.level0_slowdown_writes_trigger)) {
|
||||
write_controller_token_ =
|
||||
@@ -1066,32 +1032,22 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
"[%s] Increasing compaction threads because we have %d level-0 "
|
||||
"files ",
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count());
|
||||
} else if (mutable_cf_options.soft_pending_compaction_bytes_limit == 0) {
|
||||
} else if (vstorage->estimated_compaction_needed_bytes() >=
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit / 4) {
|
||||
// Increase compaction threads if bytes needed for compaction exceeds
|
||||
// 1/4 of threshold for slowing down.
|
||||
// If soft pending compaction byte limit is not set, always speed up
|
||||
// compaction.
|
||||
write_controller_token_ =
|
||||
write_controller->GetCompactionPressureToken();
|
||||
} else if (vstorage->estimated_compaction_needed_bytes() >=
|
||||
GetPendingCompactionBytesForCompactionSpeedup(
|
||||
mutable_cf_options, vstorage)) {
|
||||
write_controller_token_ =
|
||||
write_controller->GetCompactionPressureToken();
|
||||
ROCKS_LOG_INFO(
|
||||
ioptions_.logger,
|
||||
"[%s] Increasing compaction threads because of estimated pending "
|
||||
"compaction "
|
||||
"bytes %" PRIu64,
|
||||
name_.c_str(), vstorage->estimated_compaction_needed_bytes());
|
||||
} else if (uint64_t(vstorage->FilesMarkedForCompaction().size()) >=
|
||||
GetMarkedFileCountForCompactionSpeedup()) {
|
||||
write_controller_token_ =
|
||||
write_controller->GetCompactionPressureToken();
|
||||
ROCKS_LOG_INFO(
|
||||
ioptions_.logger,
|
||||
"[%s] Increasing compaction threads because we have %" PRIu64
|
||||
" files marked for compaction",
|
||||
name_.c_str(),
|
||||
uint64_t(vstorage->FilesMarkedForCompaction().size()));
|
||||
if (mutable_cf_options.soft_pending_compaction_bytes_limit > 0) {
|
||||
ROCKS_LOG_INFO(
|
||||
ioptions_.logger,
|
||||
"[%s] Increasing compaction threads because of estimated pending "
|
||||
"compaction "
|
||||
"bytes %" PRIu64,
|
||||
name_.c_str(), vstorage->estimated_compaction_needed_bytes());
|
||||
}
|
||||
} else {
|
||||
write_controller_token_.reset();
|
||||
}
|
||||
@@ -1166,7 +1122,7 @@ Compaction* ColumnFamilyData::PickCompaction(
|
||||
GetName(), mutable_options, mutable_db_options, current_->storage_info(),
|
||||
log_buffer);
|
||||
if (result != nullptr) {
|
||||
result->FinalizeInputInfo(current_);
|
||||
result->SetInputVersion(current_);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1179,13 +1135,13 @@ bool ColumnFamilyData::RangeOverlapWithCompaction(
|
||||
}
|
||||
|
||||
Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
const autovector<UserKeyRange>& ranges, SuperVersion* super_version,
|
||||
const autovector<Range>& ranges, SuperVersion* super_version,
|
||||
bool allow_data_in_errors, bool* overlap) {
|
||||
assert(overlap != nullptr);
|
||||
*overlap = false;
|
||||
// Create an InternalIterator over all unflushed memtables
|
||||
Arena arena;
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
// TODO: plumb Env::IOActivity
|
||||
ReadOptions read_opts;
|
||||
read_opts.total_order_seek = true;
|
||||
MergeIteratorBuilder merge_iter_builder(&internal_comparator_, &arena);
|
||||
@@ -1223,8 +1179,7 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
|
||||
if (status.ok()) {
|
||||
if (memtable_iter->Valid() &&
|
||||
ucmp->CompareWithoutTimestamp(seek_result.user_key,
|
||||
ranges[i].limit) <= 0) {
|
||||
ucmp->Compare(seek_result.user_key, ranges[i].limit) <= 0) {
|
||||
*overlap = true;
|
||||
} else if (range_del_agg.IsRangeOverlapped(ranges[i].start,
|
||||
ranges[i].limit)) {
|
||||
@@ -1251,7 +1206,7 @@ Compaction* ColumnFamilyData::CompactRange(
|
||||
compact_range_options, begin, end, compaction_end, conflict,
|
||||
max_file_num_to_ignore, trim_ts);
|
||||
if (result != nullptr) {
|
||||
result->FinalizeInputInfo(current_);
|
||||
result->SetInputVersion(current_);
|
||||
}
|
||||
TEST_SYNC_POINT("ColumnFamilyData::CompactRange:Return");
|
||||
return result;
|
||||
@@ -1332,6 +1287,8 @@ void ColumnFamilyData::InstallSuperVersion(
|
||||
new_superversion->Init(this, mem_, imm_.current(), current_);
|
||||
SuperVersion* old_superversion = super_version_;
|
||||
super_version_ = new_superversion;
|
||||
++super_version_number_;
|
||||
super_version_->version_number = super_version_number_;
|
||||
if (old_superversion == nullptr || old_superversion->current != current() ||
|
||||
old_superversion->mem != mem_ ||
|
||||
old_superversion->imm != imm_.current()) {
|
||||
@@ -1366,8 +1323,6 @@ void ColumnFamilyData::InstallSuperVersion(
|
||||
sv_context->superversions_to_free.push_back(old_superversion);
|
||||
}
|
||||
}
|
||||
++super_version_number_;
|
||||
super_version_->version_number = super_version_number_;
|
||||
}
|
||||
|
||||
void ColumnFamilyData::ResetThreadLocalSuperVersions() {
|
||||
@@ -1425,33 +1380,6 @@ Status ColumnFamilyData::ValidateOptions(
|
||||
}
|
||||
}
|
||||
|
||||
const auto* ucmp = cf_options.comparator;
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() > 0 &&
|
||||
!cf_options.persist_user_defined_timestamps) {
|
||||
if (db_options.atomic_flush) {
|
||||
return Status::NotSupported(
|
||||
"Not persisting user-defined timestamps feature is not supported"
|
||||
"in combination with atomic flush.");
|
||||
}
|
||||
if (db_options.allow_concurrent_memtable_write) {
|
||||
return Status::NotSupported(
|
||||
"Not persisting user-defined timestamps feature is not supported"
|
||||
" in combination with concurrent memtable write.");
|
||||
}
|
||||
const char* comparator_name = cf_options.comparator->Name();
|
||||
size_t name_size = strlen(comparator_name);
|
||||
const char* suffix = ".u64ts";
|
||||
size_t suffix_size = strlen(suffix);
|
||||
if (name_size <= suffix_size ||
|
||||
strcmp(comparator_name + name_size - suffix_size, suffix) != 0) {
|
||||
return Status::NotSupported(
|
||||
"Not persisting user-defined timestamps"
|
||||
"feature only support user-defined timestamps formatted as "
|
||||
"uint64_t.");
|
||||
}
|
||||
}
|
||||
|
||||
if (cf_options.enable_blob_garbage_collection) {
|
||||
if (cf_options.blob_garbage_collection_age_cutoff < 0.0 ||
|
||||
cf_options.blob_garbage_collection_age_cutoff > 1.0) {
|
||||
@@ -1591,34 +1519,6 @@ FSDirectory* ColumnFamilyData::GetDataDir(size_t path_id) const {
|
||||
return data_dirs_[path_id].get();
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::ShouldPostponeFlushToRetainUDT(
|
||||
uint64_t max_memtable_id) {
|
||||
const Comparator* ucmp = user_comparator();
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
if (ts_sz == 0 || ioptions_.persist_user_defined_timestamps) {
|
||||
return false;
|
||||
}
|
||||
// If users set the `persist_user_defined_timestamps` flag to false, they
|
||||
// should also set the `full_history_ts_low` flag to indicate the range of
|
||||
// user-defined timestamps to retain in memory. Otherwise, we do not
|
||||
// explicitly postpone flush to retain UDTs.
|
||||
const std::string& full_history_ts_low = GetFullHistoryTsLow();
|
||||
if (full_history_ts_low.empty()) {
|
||||
return false;
|
||||
}
|
||||
for (const Slice& table_newest_udt :
|
||||
imm()->GetTablesNewestUDT(max_memtable_id)) {
|
||||
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
|
||||
// needs postponing.
|
||||
if (ucmp->CompareTimestamp(table_newest_udt, full_history_ts_low) >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ColumnFamilyData::RecoverEpochNumbers() {
|
||||
assert(current_);
|
||||
auto* vstorage = current_->storage_info();
|
||||
@@ -1796,20 +1696,4 @@ const Comparator* GetColumnFamilyUserComparator(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ImmutableOptions& GetImmutableOptions(ColumnFamilyHandle* column_family) {
|
||||
assert(column_family);
|
||||
|
||||
ColumnFamilyHandleImpl* const handle =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
assert(handle);
|
||||
|
||||
const ColumnFamilyData* const cfd = handle->cfd();
|
||||
assert(cfd);
|
||||
|
||||
const ImmutableOptions* ioptions = cfd->ioptions();
|
||||
assert(ioptions);
|
||||
|
||||
return *ioptions;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+26
-37
@@ -169,10 +169,10 @@ class ColumnFamilyHandleImpl : public ColumnFamilyHandle {
|
||||
virtual ~ColumnFamilyHandleImpl();
|
||||
virtual ColumnFamilyData* cfd() const { return cfd_; }
|
||||
|
||||
uint32_t GetID() const override;
|
||||
const std::string& GetName() const override;
|
||||
Status GetDescriptor(ColumnFamilyDescriptor* desc) override;
|
||||
const Comparator* GetComparator() const override;
|
||||
virtual uint32_t GetID() const override;
|
||||
virtual const std::string& GetName() const override;
|
||||
virtual Status GetDescriptor(ColumnFamilyDescriptor* desc) override;
|
||||
virtual const Comparator* GetComparator() const override;
|
||||
|
||||
private:
|
||||
ColumnFamilyData* cfd_;
|
||||
@@ -193,7 +193,7 @@ class ColumnFamilyHandleInternal : public ColumnFamilyHandleImpl {
|
||||
internal_cfd_(nullptr) {}
|
||||
|
||||
void SetCFD(ColumnFamilyData* _cfd) { internal_cfd_ = _cfd; }
|
||||
ColumnFamilyData* cfd() const override { return internal_cfd_; }
|
||||
virtual ColumnFamilyData* cfd() const override { return internal_cfd_; }
|
||||
|
||||
private:
|
||||
ColumnFamilyData* internal_cfd_;
|
||||
@@ -211,12 +211,6 @@ struct SuperVersion {
|
||||
// Version number of the current SuperVersion
|
||||
uint64_t version_number;
|
||||
WriteStallCondition write_stall_condition;
|
||||
// Each time `full_history_ts_low` collapses history, a new SuperVersion is
|
||||
// installed. This field tracks the effective `full_history_ts_low` for that
|
||||
// SuperVersion, to be used by read APIs for sanity checks. This field is
|
||||
// immutable once SuperVersion is installed. For column family that doesn't
|
||||
// enable UDT feature, this is an empty string.
|
||||
std::string full_history_ts_low;
|
||||
|
||||
// should be called outside the mutex
|
||||
SuperVersion() = default;
|
||||
@@ -251,21 +245,22 @@ struct SuperVersion {
|
||||
autovector<MemTable*> to_delete;
|
||||
};
|
||||
|
||||
Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
|
||||
extern Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
|
||||
|
||||
Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options);
|
||||
extern Status CheckConcurrentWritesSupported(
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
|
||||
Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
extern Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
|
||||
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src);
|
||||
extern 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
|
||||
// into internal ones in int_tbl_prop_collector_factories. Add a system internal
|
||||
// one too.
|
||||
void GetInternalTblPropCollFactory(
|
||||
extern void GetIntTblPropCollectorFactory(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
InternalTblPropCollFactories* internal_tbl_prop_coll_factories);
|
||||
IntTblPropCollectorFactories* int_tbl_prop_collector_factories);
|
||||
|
||||
class ColumnFamilySet;
|
||||
|
||||
@@ -400,7 +395,7 @@ class ColumnFamilyData {
|
||||
// duration of this function.
|
||||
//
|
||||
// Thread-safe
|
||||
Status RangesOverlapWithMemtables(const autovector<UserKeyRange>& ranges,
|
||||
Status RangesOverlapWithMemtables(const autovector<Range>& ranges,
|
||||
SuperVersion* super_version,
|
||||
bool allow_data_in_errors, bool* overlap);
|
||||
|
||||
@@ -429,8 +424,8 @@ class ColumnFamilyData {
|
||||
return internal_comparator_;
|
||||
}
|
||||
|
||||
const InternalTblPropCollFactories* internal_tbl_prop_coll_factories() const {
|
||||
return &internal_tbl_prop_coll_factories_;
|
||||
const IntTblPropCollectorFactories* int_tbl_prop_collector_factories() const {
|
||||
return &int_tbl_prop_collector_factories_;
|
||||
}
|
||||
|
||||
SuperVersion* GetSuperVersion() { return super_version_; }
|
||||
@@ -511,12 +506,6 @@ class ColumnFamilyData {
|
||||
return full_history_ts_low_;
|
||||
}
|
||||
|
||||
// REQUIRES: DB mutex held.
|
||||
// Return true if flushing up to MemTables with ID `max_memtable_id`
|
||||
// should be postponed to retain user-defined timestamps according to the
|
||||
// user's setting. Called by background flush job.
|
||||
bool ShouldPostponeFlushToRetainUDT(uint64_t max_memtable_id);
|
||||
|
||||
ThreadLocalPtr* TEST_GetLocalSV() { return local_sv_.get(); }
|
||||
WriteBufferManager* write_buffer_mgr() { return write_buffer_manager_; }
|
||||
std::shared_ptr<CacheReservationManager>
|
||||
@@ -524,6 +513,8 @@ class ColumnFamilyData {
|
||||
return file_metadata_cache_res_mgr_;
|
||||
}
|
||||
|
||||
SequenceNumber GetFirstMemtableSequenceNumber() const;
|
||||
|
||||
static const uint32_t kDummyColumnFamilyDataId;
|
||||
|
||||
// Keep track of whether the mempurge feature was ever used.
|
||||
@@ -573,7 +564,7 @@ class ColumnFamilyData {
|
||||
std::atomic<bool> dropped_; // true if client dropped it
|
||||
|
||||
const InternalKeyComparator internal_comparator_;
|
||||
InternalTblPropCollFactories internal_tbl_prop_coll_factories_;
|
||||
IntTblPropCollectorFactories int_tbl_prop_collector_factories_;
|
||||
|
||||
const ColumnFamilyOptions initial_cf_options_;
|
||||
const ImmutableOptions ioptions_;
|
||||
@@ -853,17 +844,17 @@ class ColumnFamilyMemTablesImpl : public ColumnFamilyMemTables {
|
||||
// REQUIRES: Seek() called first
|
||||
// REQUIRES: use this function of DBImpl::column_family_memtables_ should be
|
||||
// under a DB mutex OR from a write thread
|
||||
MemTable* GetMemTable() const override;
|
||||
virtual MemTable* GetMemTable() const override;
|
||||
|
||||
// Returns column family handle for the selected column family
|
||||
// REQUIRES: use this function of DBImpl::column_family_memtables_ should be
|
||||
// under a DB mutex OR from a write thread
|
||||
ColumnFamilyHandle* GetColumnFamilyHandle() override;
|
||||
virtual ColumnFamilyHandle* GetColumnFamilyHandle() override;
|
||||
|
||||
// Cannot be called while another thread is calling Seek().
|
||||
// REQUIRES: use this function of DBImpl::column_family_memtables_ should be
|
||||
// under a DB mutex OR from a write thread
|
||||
ColumnFamilyData* current() override { return current_; }
|
||||
virtual ColumnFamilyData* current() override { return current_; }
|
||||
|
||||
private:
|
||||
ColumnFamilySet* column_family_set_;
|
||||
@@ -871,11 +862,9 @@ class ColumnFamilyMemTablesImpl : public ColumnFamilyMemTables {
|
||||
ColumnFamilyHandleInternal handle_;
|
||||
};
|
||||
|
||||
uint32_t GetColumnFamilyID(ColumnFamilyHandle* column_family);
|
||||
extern uint32_t GetColumnFamilyID(ColumnFamilyHandle* column_family);
|
||||
|
||||
const Comparator* GetColumnFamilyUserComparator(
|
||||
extern const Comparator* GetColumnFamilyUserComparator(
|
||||
ColumnFamilyHandle* column_family);
|
||||
|
||||
const ImmutableOptions& GetImmutableOptions(ColumnFamilyHandle* column_family);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+45
-462
@@ -8,7 +8,6 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
@@ -18,7 +17,6 @@
|
||||
#include "options/options_parser.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -28,7 +26,6 @@
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/string_util.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
@@ -66,9 +63,6 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
db_options_.create_if_missing = true;
|
||||
db_options_.fail_if_options_file_error = true;
|
||||
db_options_.env = env_;
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
EXPECT_OK(DestroyDB(dbname_, Options(db_options_, column_family_options_)));
|
||||
}
|
||||
|
||||
@@ -2171,57 +2165,13 @@ TEST_P(ColumnFamilyTest, FlushStaleColumnFamilies) {
|
||||
Close();
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct CountOptionsFilesFs : public FileSystemWrapper {
|
||||
explicit CountOptionsFilesFs(const std::shared_ptr<FileSystem>& t)
|
||||
: FileSystemWrapper(t) {}
|
||||
const char* Name() const override { return "CountOptionsFilesFs"; }
|
||||
|
||||
IOStatus NewWritableFile(const std::string& f, const FileOptions& file_opts,
|
||||
std::unique_ptr<FSWritableFile>* r,
|
||||
IODebugContext* dbg) override {
|
||||
if (f.find("OPTIONS-") != std::string::npos) {
|
||||
options_files_created.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
return FileSystemWrapper::NewWritableFile(f, file_opts, r, dbg);
|
||||
}
|
||||
|
||||
std::atomic<int> options_files_created{};
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_P(ColumnFamilyTest, CreateMissingColumnFamilies) {
|
||||
// Can't accidentally add CFs to an existing DB
|
||||
Open();
|
||||
Close();
|
||||
ASSERT_FALSE(db_options_.create_missing_column_families);
|
||||
ASSERT_NOK(TryOpen({"one", "two"}));
|
||||
|
||||
// Nor accidentally create in a new DB
|
||||
Destroy();
|
||||
db_options_.create_if_missing = true;
|
||||
ASSERT_NOK(TryOpen({"one", "two"}));
|
||||
|
||||
// Only with the option (new DB case)
|
||||
Status s = TryOpen({"one", "two"});
|
||||
ASSERT_TRUE(!s.ok());
|
||||
db_options_.create_missing_column_families = true;
|
||||
// Also setup to count number of options files created (see check below)
|
||||
auto my_fs =
|
||||
std::make_shared<CountOptionsFilesFs>(db_options_.env->GetFileSystem());
|
||||
auto my_env = std::make_unique<CompositeEnvWrapper>(db_options_.env, my_fs);
|
||||
SaveAndRestore<Env*> save_restore_env(&db_options_.env, my_env.get());
|
||||
|
||||
ASSERT_OK(TryOpen({"default", "one", "two"}));
|
||||
s = TryOpen({"default", "one", "two"});
|
||||
ASSERT_TRUE(s.ok());
|
||||
Close();
|
||||
|
||||
// An older version would write an updated options file for each column
|
||||
// family created under create_missing_column_families, which would be
|
||||
// quadratic I/O in the number of column families.
|
||||
ASSERT_EQ(my_fs->options_files_created.load(), 1);
|
||||
|
||||
// Add to existing DB case
|
||||
ASSERT_OK(TryOpen({"default", "one", "two", "three", "four"}));
|
||||
Close();
|
||||
ASSERT_EQ(my_fs->options_files_created.load(), 2);
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, SanitizeOptions) {
|
||||
@@ -2473,10 +2423,7 @@ void DropSingleColumnFamily(ColumnFamilyTest* cf_test, int cf_id,
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
// This test attempts to set up a race condition in a way that is no longer
|
||||
// possible, causing the test to hang. If DBImpl::options_mutex_ is removed
|
||||
// in the future, this test might become relevant again.
|
||||
TEST_P(ColumnFamilyTest, DISABLED_CreateAndDropRace) {
|
||||
TEST_P(ColumnFamilyTest, CreateAndDropRace) {
|
||||
const int kCfCount = 5;
|
||||
std::vector<ColumnFamilyOptions> cf_opts;
|
||||
std::vector<Comparator*> comparators;
|
||||
@@ -2538,53 +2485,6 @@ TEST_P(ColumnFamilyTest, DISABLED_CreateAndDropRace) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, CreateAndDropPeriodicRace) {
|
||||
// This is a mini-stress test looking for inconsistency between the set of
|
||||
// CFs in the DB, particularly whether any use preserve_internal_time_seconds,
|
||||
// and whether that is accurately reflected in the periodic task setup.
|
||||
constexpr size_t kNumThreads = 12;
|
||||
std::vector<std::thread> threads;
|
||||
bool last_cf_on = Random::GetTLSInstance()->OneIn(2);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::RegisterRecordSeqnoTimeWorker:BeforePeriodicTaskType",
|
||||
[&](void* /*arg*/) { std::this_thread::yield(); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_EQ(column_family_options_.preserve_internal_time_seconds, 0U);
|
||||
ColumnFamilyOptions other_opts = column_family_options_;
|
||||
ColumnFamilyOptions last_opts = column_family_options_;
|
||||
(last_cf_on ? last_opts : other_opts).preserve_internal_time_seconds =
|
||||
1000000;
|
||||
Open();
|
||||
|
||||
for (size_t i = 0; i < kNumThreads; i++) {
|
||||
threads.emplace_back([this, &other_opts, i]() {
|
||||
ColumnFamilyHandle* cfh;
|
||||
ASSERT_OK(db_->CreateColumnFamily(other_opts, std::to_string(i), &cfh));
|
||||
ASSERT_OK(db_->DropColumnFamily(cfh));
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(cfh));
|
||||
});
|
||||
}
|
||||
|
||||
ColumnFamilyHandle* last_cfh;
|
||||
ASSERT_OK(db_->CreateColumnFamily(last_opts, "last", &last_cfh));
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
bool task_enabled = dbfull()->TEST_GetPeriodicTaskScheduler().TEST_HasTask(
|
||||
PeriodicTaskType::kRecordSeqnoTime);
|
||||
ASSERT_EQ(last_cf_on, task_enabled);
|
||||
|
||||
ASSERT_OK(db_->DropColumnFamily(last_cfh));
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(last_cfh));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
const uint64_t kBaseRate = 800000u;
|
||||
db_options_.delayed_write_rate = kBaseRate;
|
||||
@@ -2604,92 +2504,90 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit = 2000;
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(400, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(400);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(450, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(450);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(205, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(205);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(202, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(202);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(198, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(198);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(399, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(399);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(599, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(599);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(2001, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(2001);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(3001, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(3001);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(390, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(390);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(100, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(100);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2708,7 +2606,7 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(0);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2720,14 +2618,14 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(200, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(200);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(0);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(0, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(0);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2746,7 +2644,7 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_EQ(kBaseRate, dbfull()->TEST_write_controler().delayed_write_rate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(60);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2755,14 +2653,14 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
vstorage->set_l0_delay_trigger_count(70);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(71);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(501, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(501);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2787,21 +2685,19 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupSingleColumnFamily) {
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit = 200;
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit = 2000;
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(45, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(45);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
@@ -2857,55 +2753,53 @@ TEST_P(ColumnFamilyTest, WriteStallTwoColumnFamilies) {
|
||||
MutableCFOptions mutable_cf_options1 = mutable_cf_options;
|
||||
mutable_cf_options1.soft_pending_compaction_bytes_limit = 500;
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(201, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(70, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(70);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(800, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(800);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(700, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(700);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2938,31 +2832,29 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupTwoColumnFamilies) {
|
||||
MutableCFOptions mutable_cf_options1 = mutable_cf_options;
|
||||
mutable_cf_options1.level0_slowdown_writes_trigger = 16;
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(60, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(60);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(30, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(30);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(70, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(70);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(20, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(20);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(3, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(3);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
@@ -2979,116 +2871,6 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupTwoColumnFamilies) {
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, CompactionSpeedupForCompactionDebt) {
|
||||
db_options_.max_background_compactions = 6;
|
||||
Open();
|
||||
ColumnFamilyData* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())->cfd();
|
||||
MutableCFOptions mutable_cf_options(column_family_options_);
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit =
|
||||
std::numeric_limits<uint64_t>::max();
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
{
|
||||
// No bottommost data, so debt ratio cannot trigger speedup.
|
||||
VersionStorageInfo* vstorage = cfd->current()->storage_info();
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */,
|
||||
dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
|
||||
// Add a tiny amount of bottommost data.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "bar"));
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, CompactionSpeedupForMarkedFiles) {
|
||||
const int kParallelismLimit = 3;
|
||||
class AlwaysCompactTpc : public TablePropertiesCollector {
|
||||
public:
|
||||
Status Finish(UserCollectedProperties* /* properties */) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
UserCollectedProperties GetReadableProperties() const override {
|
||||
return UserCollectedProperties{};
|
||||
}
|
||||
|
||||
const char* Name() const override { return "AlwaysCompactTpc"; }
|
||||
|
||||
bool NeedCompact() const override { return true; }
|
||||
};
|
||||
|
||||
class AlwaysCompactTpcf : public TablePropertiesCollectorFactory {
|
||||
public:
|
||||
TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollectorFactory::Context /* context */) override {
|
||||
return new AlwaysCompactTpc();
|
||||
}
|
||||
|
||||
const char* Name() const override { return "AlwaysCompactTpcf"; }
|
||||
};
|
||||
|
||||
column_family_options_.num_levels = 2;
|
||||
column_family_options_.table_properties_collector_factories.emplace_back(
|
||||
std::make_shared<AlwaysCompactTpcf>());
|
||||
db_options_.max_background_compactions = kParallelismLimit;
|
||||
Open();
|
||||
|
||||
// Make a nonempty last level. Only marked files in upper levels count.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "bar"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
WaitForCompaction();
|
||||
AssertFilesPerLevel("0,1", 0 /* cf */);
|
||||
|
||||
// Block the compaction thread pool so marked files accumulate in L0.
|
||||
test::SleepingBackgroundTask sleeping_tasks[kParallelismLimit];
|
||||
for (int i = 0; i < kParallelismLimit; i++) {
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
&sleeping_tasks[i], Env::Priority::LOW);
|
||||
sleeping_tasks[i].WaitUntilSleeping();
|
||||
}
|
||||
|
||||
// Zero marked upper-level files. No speedup.
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
AssertFilesPerLevel("0,1", 0 /* cf */);
|
||||
|
||||
// One marked upper-level file. No speedup.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "bar"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
AssertFilesPerLevel("1,1", 0 /* cf */);
|
||||
|
||||
// Two marked upper-level files. Speedup.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "bar"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_EQ(kParallelismLimit, dbfull()->TEST_BGCompactionsAllowed());
|
||||
AssertFilesPerLevel("2,1", 0 /* cf */);
|
||||
|
||||
for (int i = 0; i < kParallelismLimit; i++) {
|
||||
sleeping_tasks[i].WakeUp();
|
||||
sleeping_tasks[i].WaitUntilDone();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, CreateAndDestroyOptions) {
|
||||
std::unique_ptr<ColumnFamilyOptions> cfo(new ColumnFamilyOptions());
|
||||
ColumnFamilyHandle* cfh;
|
||||
@@ -3598,205 +3380,6 @@ TEST(ColumnFamilyTest, ValidateMemtableKVChecksumOption) {
|
||||
ASSERT_OK(ColumnFamilyData::ValidateOptions(db_options, cf_options));
|
||||
}
|
||||
|
||||
// Tests the flushing behavior of a column family to retain user-defined
|
||||
// timestamp when `persist_user_defined_timestamp` is false.
|
||||
class ColumnFamilyRetainUDTTest : public ColumnFamilyTestBase {
|
||||
public:
|
||||
ColumnFamilyRetainUDTTest() : ColumnFamilyTestBase(kLatestFormatVersion) {}
|
||||
|
||||
void SetUp() override {
|
||||
db_options_.allow_concurrent_memtable_write = false;
|
||||
column_family_options_.comparator =
|
||||
test::BytewiseComparatorWithU64TsWrapper();
|
||||
column_family_options_.persist_user_defined_timestamps = false;
|
||||
ColumnFamilyTestBase::SetUp();
|
||||
}
|
||||
|
||||
Status Put(int cf, const std::string& key, const std::string& ts,
|
||||
const std::string& value) {
|
||||
return db_->Put(WriteOptions(), handles_[cf], Slice(key), Slice(ts),
|
||||
Slice(value));
|
||||
}
|
||||
};
|
||||
|
||||
class TestTsComparator : public Comparator {
|
||||
public:
|
||||
TestTsComparator() : Comparator(8 /*ts_sz*/) {}
|
||||
|
||||
int Compare(const ROCKSDB_NAMESPACE::Slice& /*a*/,
|
||||
const ROCKSDB_NAMESPACE::Slice& /*b*/) const override {
|
||||
return 0;
|
||||
}
|
||||
const char* Name() const override { return "TestTs"; }
|
||||
void FindShortestSeparator(
|
||||
std::string* /*start*/,
|
||||
const ROCKSDB_NAMESPACE::Slice& /*limit*/) const override {}
|
||||
void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
|
||||
TEST_F(ColumnFamilyRetainUDTTest, SanityCheck) {
|
||||
Open();
|
||||
ColumnFamilyOptions cf_options;
|
||||
cf_options.persist_user_defined_timestamps = false;
|
||||
TestTsComparator test_comparator;
|
||||
cf_options.comparator = &test_comparator;
|
||||
ColumnFamilyHandle* handle;
|
||||
// Not persisting user-defined timestamps feature only supports user-defined
|
||||
// timestamps formatted as uint64_t.
|
||||
ASSERT_TRUE(
|
||||
db_->CreateColumnFamily(cf_options, "pikachu", &handle).IsNotSupported());
|
||||
|
||||
Destroy();
|
||||
// Not persisting user-defined timestamps feature doesn't work in combination
|
||||
// with atomic flush.
|
||||
db_options_.atomic_flush = true;
|
||||
ASSERT_TRUE(TryOpen({"default"}).IsNotSupported());
|
||||
|
||||
// Not persisting user-defined timestamps feature doesn't work in combination
|
||||
// with concurrent memtable write.
|
||||
db_options_.atomic_flush = false;
|
||||
db_options_.allow_concurrent_memtable_write = true;
|
||||
ASSERT_TRUE(TryOpen({"default"}).IsNotSupported());
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyRetainUDTTest, FullHistoryTsLowNotSet) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto reschedule_count = *static_cast<int*>(arg);
|
||||
ASSERT_EQ(1, reschedule_count);
|
||||
});
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Open();
|
||||
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(Flush(0));
|
||||
|
||||
// After flush, `full_history_ts_low` should be automatically advanced to
|
||||
// the effective cutoff timestamp: write_ts + 1
|
||||
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(ColumnFamilyRetainUDTTest, AllKeysExpired) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto reschedule_count = *static_cast<int*>(arg);
|
||||
ASSERT_EQ(1, reschedule_count);
|
||||
});
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Open();
|
||||
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.
|
||||
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(ColumnFamilyRetainUDTTest, NotAllKeysExpiredFlushToAvoidWriteStall) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto reschedule_count = *static_cast<int*>(arg);
|
||||
ASSERT_EQ(1, reschedule_count);
|
||||
});
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Open();
|
||||
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 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
|
||||
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(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
|
||||
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);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Open();
|
||||
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(Flush(0));
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -345,7 +345,7 @@ TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
return true;
|
||||
}
|
||||
std::string res;
|
||||
EXPECT_TRUE(db_->Get(ReadOptions(), "", &res).IsNotFound());
|
||||
db_->Get(ReadOptions(), "", &res);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+34
-119
@@ -13,8 +13,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "logging/logging.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/sst_partitioner.h"
|
||||
#include "test_util/sync_point.h"
|
||||
@@ -22,13 +20,17 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
int sstableKeyCompare(const Comparator* uc, const Slice& a, const Slice& b) {
|
||||
auto c = uc->CompareWithoutTimestamp(ExtractUserKey(a), ExtractUserKey(b));
|
||||
const uint64_t kRangeTombstoneSentinel =
|
||||
PackSequenceAndType(kMaxSequenceNumber, kTypeRangeDeletion);
|
||||
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const InternalKey& b) {
|
||||
auto c = user_cmp->CompareWithoutTimestamp(a.user_key(), b.user_key());
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
auto a_footer = ExtractInternalKeyFooter(a);
|
||||
auto b_footer = ExtractInternalKeyFooter(b);
|
||||
auto a_footer = ExtractInternalKeyFooter(a.Encode());
|
||||
auto b_footer = ExtractInternalKeyFooter(b.Encode());
|
||||
if (a_footer == kRangeTombstoneSentinel) {
|
||||
if (b_footer != kRangeTombstoneSentinel) {
|
||||
return -1;
|
||||
@@ -113,42 +115,6 @@ void Compaction::GetBoundaryKeys(
|
||||
}
|
||||
}
|
||||
|
||||
void Compaction::GetBoundaryInternalKeys(
|
||||
VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs, InternalKey* smallest_key,
|
||||
InternalKey* largest_key, int exclude_level) {
|
||||
bool initialized = false;
|
||||
const InternalKeyComparator* icmp = vstorage->InternalComparator();
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
if (inputs[i].files.empty() || inputs[i].level == exclude_level) {
|
||||
continue;
|
||||
}
|
||||
if (inputs[i].level == 0) {
|
||||
// we need to consider all files on level 0
|
||||
for (const auto* f : inputs[i].files) {
|
||||
if (!initialized || icmp->Compare(f->smallest, *smallest_key) < 0) {
|
||||
*smallest_key = f->smallest;
|
||||
}
|
||||
if (!initialized || icmp->Compare(f->largest, *largest_key) > 0) {
|
||||
*largest_key = f->largest;
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
} else {
|
||||
// we only need to consider the first and last file
|
||||
if (!initialized ||
|
||||
icmp->Compare(inputs[i].files[0]->smallest, *smallest_key) < 0) {
|
||||
*smallest_key = inputs[i].files[0]->smallest;
|
||||
}
|
||||
if (!initialized ||
|
||||
icmp->Compare(inputs[i].files.back()->largest, *largest_key) > 0) {
|
||||
*largest_key = inputs[i].files.back()->largest;
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> Compaction::PopulateWithAtomicBoundaries(
|
||||
VersionStorageInfo* vstorage, std::vector<CompactionInputFiles> inputs) {
|
||||
const Comparator* ucmp = vstorage->InternalComparator()->user_comparator();
|
||||
@@ -238,38 +204,6 @@ bool Compaction::IsFullCompaction(
|
||||
return num_files_in_compaction == total_num_files;
|
||||
}
|
||||
|
||||
Status Compaction::InitInputTableProperties() {
|
||||
if (!input_table_properties_.empty()) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status s;
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
assert(input_version_);
|
||||
for (size_t i = 0; i < num_input_levels(); ++i) {
|
||||
for (const FileMetaData* fmd : *(this->inputs(i))) {
|
||||
std::shared_ptr<const TableProperties> tp;
|
||||
std::string file_name =
|
||||
TableFileName(immutable_options_.cf_paths, fmd->fd.GetNumber(),
|
||||
fmd->fd.GetPathId());
|
||||
s = input_version_->GetTableProperties(read_options, &tp, fmd,
|
||||
&file_name);
|
||||
if (s.ok()) {
|
||||
input_table_properties_[file_name] = tp;
|
||||
} else {
|
||||
ROCKS_LOG_ERROR(immutable_options_.info_log,
|
||||
"Unable to load table properties for file %" PRIu64
|
||||
" --- %s\n",
|
||||
fmd->fd.GetNumber(), s.ToString().c_str());
|
||||
input_table_properties_.clear();
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Compaction::Compaction(
|
||||
VersionStorageInfo* vstorage, const ImmutableOptions& _immutable_options,
|
||||
const MutableCFOptions& _mutable_cf_options,
|
||||
@@ -350,9 +284,11 @@ Compaction::Compaction(
|
||||
|
||||
// for the non-bottommost levels, it tries to build files match the target
|
||||
// file size, but not guaranteed. It could be 2x the size of the target size.
|
||||
max_output_file_size_ = bottommost_level_ || grandparents_.empty()
|
||||
? target_output_file_size_
|
||||
: 2 * target_output_file_size_;
|
||||
max_output_file_size_ =
|
||||
bottommost_level_ || grandparents_.empty() ||
|
||||
!_immutable_options.level_compaction_dynamic_file_size
|
||||
? target_output_file_size_
|
||||
: 2 * target_output_file_size_;
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 1; i < inputs_.size(); ++i) {
|
||||
@@ -431,14 +367,9 @@ void Compaction::PopulatePenultimateLevelOutputRange() {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: should make use of `penultimate_output_range_type_`.
|
||||
// FIXME: when last level's input range does not overlap with
|
||||
// penultimate level, and penultimate level input is empty,
|
||||
// this call will not set penultimate_level_smallest_ or
|
||||
// penultimate_level_largest_. No keys will be compacted up.
|
||||
GetBoundaryInternalKeys(input_vstorage_, inputs_,
|
||||
&penultimate_level_smallest_,
|
||||
&penultimate_level_largest_, exclude_level);
|
||||
GetBoundaryKeys(input_vstorage_, inputs_,
|
||||
&penultimate_level_smallest_user_key_,
|
||||
&penultimate_level_largest_user_key_, exclude_level);
|
||||
}
|
||||
|
||||
Compaction::~Compaction() {
|
||||
@@ -463,11 +394,23 @@ bool Compaction::OverlapPenultimateLevelOutputRange(
|
||||
if (!SupportsPerKeyPlacement()) {
|
||||
return false;
|
||||
}
|
||||
const Comparator* ucmp =
|
||||
input_vstorage_->InternalComparator()->user_comparator();
|
||||
|
||||
// See FIXME in Compaction::PopulatePenultimateLevelOutputRange().
|
||||
// We do not compact any key up in this case.
|
||||
if (penultimate_level_smallest_.size() == 0 ||
|
||||
penultimate_level_largest_.size() == 0) {
|
||||
return ucmp->CompareWithoutTimestamp(
|
||||
smallest_key, penultimate_level_largest_user_key_) <= 0 &&
|
||||
ucmp->CompareWithoutTimestamp(
|
||||
largest_key, penultimate_level_smallest_user_key_) >= 0;
|
||||
}
|
||||
|
||||
// key includes timestamp if user-defined timestamp is enabled.
|
||||
bool Compaction::WithinPenultimateLevelOutputRange(const Slice& key) const {
|
||||
if (!SupportsPerKeyPlacement()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (penultimate_level_smallest_user_key_.empty() ||
|
||||
penultimate_level_largest_user_key_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -475,28 +418,9 @@ bool Compaction::OverlapPenultimateLevelOutputRange(
|
||||
input_vstorage_->InternalComparator()->user_comparator();
|
||||
|
||||
return ucmp->CompareWithoutTimestamp(
|
||||
smallest_key, penultimate_level_largest_.user_key()) <= 0 &&
|
||||
key, penultimate_level_smallest_user_key_) >= 0 &&
|
||||
ucmp->CompareWithoutTimestamp(
|
||||
largest_key, penultimate_level_smallest_.user_key()) >= 0;
|
||||
}
|
||||
|
||||
// key includes timestamp if user-defined timestamp is enabled.
|
||||
bool Compaction::WithinPenultimateLevelOutputRange(
|
||||
const ParsedInternalKey& ikey) const {
|
||||
if (!SupportsPerKeyPlacement()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (penultimate_level_smallest_.size() == 0 ||
|
||||
penultimate_level_largest_.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const InternalKeyComparator* icmp = input_vstorage_->InternalComparator();
|
||||
|
||||
// op_type of a key can change during compaction, e.g. Merge -> Put.
|
||||
return icmp->CompareKeySeq(ikey, penultimate_level_smallest_.Encode()) >= 0 &&
|
||||
icmp->CompareKeySeq(ikey, penultimate_level_largest_.Encode()) <= 0;
|
||||
key, penultimate_level_largest_user_key_) <= 0;
|
||||
}
|
||||
|
||||
bool Compaction::InputCompressionMatchesOutput() const {
|
||||
@@ -822,17 +746,8 @@ std::unique_ptr<CompactionFilter> Compaction::CreateCompactionFilter() const {
|
||||
CompactionFilter::Context context;
|
||||
context.is_full_compaction = is_full_compaction_;
|
||||
context.is_manual_compaction = is_manual_compaction_;
|
||||
context.input_start_level = start_level_;
|
||||
context.column_family_id = cfd_->GetID();
|
||||
context.reason = TableFileCreationReason::kCompaction;
|
||||
context.input_table_properties = GetInputTableProperties();
|
||||
if (context.input_table_properties.empty()) {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_options_.info_log,
|
||||
"Unable to set `input_table_properties` of `CompactionFilter::Context` "
|
||||
"for compaction.");
|
||||
}
|
||||
|
||||
return cfd_->ioptions()->compaction_filter_factory->CreateCompactionFilter(
|
||||
context);
|
||||
}
|
||||
|
||||
+23
-50
@@ -31,19 +31,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
// that key never appears in the database. We don't want adjacent sstables to
|
||||
// be considered overlapping if they are separated by the range tombstone
|
||||
// sentinel.
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const Slice&, const Slice&);
|
||||
inline int sstableKeyCompare(const Comparator* user_cmp, const Slice& a,
|
||||
const InternalKey& b) {
|
||||
return sstableKeyCompare(user_cmp, a, b.Encode());
|
||||
}
|
||||
inline int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const Slice& b) {
|
||||
return sstableKeyCompare(user_cmp, a.Encode(), b);
|
||||
}
|
||||
inline int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const InternalKey& b) {
|
||||
return sstableKeyCompare(user_cmp, a.Encode(), b.Encode());
|
||||
}
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const InternalKey& b);
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey* a,
|
||||
const InternalKey& b);
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
@@ -289,14 +278,7 @@ class Compaction {
|
||||
// is the sum of all input file sizes.
|
||||
uint64_t OutputFilePreallocationSize() const;
|
||||
|
||||
// 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();
|
||||
}
|
||||
void SetInputVersion(Version* input_version);
|
||||
|
||||
struct InputLevelSummaryBuffer {
|
||||
char buffer[128];
|
||||
@@ -333,26 +315,26 @@ class Compaction {
|
||||
int output_level, VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs);
|
||||
|
||||
const TablePropertiesCollection& GetInputTableProperties() const {
|
||||
return input_table_properties_;
|
||||
}
|
||||
|
||||
// TODO(hx235): consider making this function symmetric to
|
||||
// InitInputTableProperties()
|
||||
void SetOutputTableProperties(
|
||||
const std::string& file_name,
|
||||
const std::shared_ptr<const TableProperties>& tp) {
|
||||
output_table_properties_[file_name] = tp;
|
||||
}
|
||||
|
||||
const TablePropertiesCollection& GetOutputTableProperties() const {
|
||||
TablePropertiesCollection GetOutputTableProperties() const {
|
||||
return output_table_properties_;
|
||||
}
|
||||
|
||||
void SetOutputTableProperties(TablePropertiesCollection tp) {
|
||||
output_table_properties_ = std::move(tp);
|
||||
}
|
||||
|
||||
Slice GetSmallestUserKey() const { return smallest_user_key_; }
|
||||
|
||||
Slice GetLargestUserKey() const { return largest_user_key_; }
|
||||
|
||||
Slice GetPenultimateLevelSmallestUserKey() const {
|
||||
return penultimate_level_smallest_user_key_;
|
||||
}
|
||||
|
||||
Slice GetPenultimateLevelLargestUserKey() const {
|
||||
return penultimate_level_largest_user_key_;
|
||||
}
|
||||
|
||||
PenultimateOutputRangeType GetPenultimateOutputRangeType() const {
|
||||
return penultimate_output_range_type_;
|
||||
}
|
||||
@@ -375,8 +357,10 @@ class Compaction {
|
||||
// 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.
|
||||
// TODO: currently it doesn't support moving data from the last level to the
|
||||
// penultimate level
|
||||
// key includes timestamp if user-defined timestamp is enabled.
|
||||
bool WithinPenultimateLevelOutputRange(const ParsedInternalKey& ikey) const;
|
||||
bool WithinPenultimateLevelOutputRange(const Slice& key) const;
|
||||
|
||||
CompactionReason compaction_reason() const { return compaction_reason_; }
|
||||
|
||||
@@ -433,10 +417,6 @@ class Compaction {
|
||||
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);
|
||||
|
||||
@@ -446,13 +426,6 @@ class Compaction {
|
||||
Slice* smallest_key, Slice* largest_key,
|
||||
int exclude_level = -1);
|
||||
|
||||
// get the smallest and largest internal key present in files to be compacted
|
||||
static void GetBoundaryInternalKeys(
|
||||
VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs,
|
||||
InternalKey* smallest_key, InternalKey* largest_key,
|
||||
int exclude_level = -1);
|
||||
|
||||
// populate penultimate level output range, which will be used to determine if
|
||||
// a key is safe to output to the penultimate level (details see
|
||||
// `Compaction::WithinPenultimateLevelOutputRange()`.
|
||||
@@ -534,7 +507,7 @@ class Compaction {
|
||||
// Does input compression match the output compression?
|
||||
bool InputCompressionMatchesOutput() const;
|
||||
|
||||
TablePropertiesCollection input_table_properties_;
|
||||
// table properties of output files
|
||||
TablePropertiesCollection output_table_properties_;
|
||||
|
||||
// smallest user keys in compaction
|
||||
@@ -565,8 +538,8 @@ class Compaction {
|
||||
// Key range for penultimate level output
|
||||
// includes timestamp if user-defined timestamp is enabled.
|
||||
// penultimate_output_range_type_ shows the range type
|
||||
InternalKey penultimate_level_smallest_;
|
||||
InternalKey penultimate_level_largest_;
|
||||
Slice penultimate_level_smallest_user_key_;
|
||||
Slice penultimate_level_largest_user_key_;
|
||||
PenultimateOutputRangeType penultimate_output_range_type_ =
|
||||
PenultimateOutputRangeType::kNotSupported;
|
||||
};
|
||||
@@ -595,6 +568,6 @@ struct PerKeyPlacementContext {
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
// Return sum of sizes of all files in `files`.
|
||||
uint64_t TotalFileSize(const std::vector<FileMetaData*>& files);
|
||||
extern uint64_t TotalFileSize(const std::vector<FileMetaData*>& files);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "db/blob/prefetch_buffer_collection.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "db/wide/wide_column_serialization.h"
|
||||
#include "db/wide/wide_columns_helper.h"
|
||||
#include "logging/logging.h"
|
||||
#include "port/likely.h"
|
||||
#include "rocksdb/listener.h"
|
||||
@@ -32,8 +31,7 @@ CompactionIterator::CompactionIterator(
|
||||
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
|
||||
bool enforce_single_del_contracts,
|
||||
const std::atomic<bool>& manual_compaction_canceled,
|
||||
bool must_count_input_entries, const Compaction* compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
const Compaction* compaction, const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const std::shared_ptr<Logger> info_log,
|
||||
const std::string* full_history_ts_low,
|
||||
@@ -47,9 +45,8 @@ CompactionIterator::CompactionIterator(
|
||||
manual_compaction_canceled,
|
||||
std::unique_ptr<CompactionProxy>(
|
||||
compaction ? new RealCompaction(compaction) : nullptr),
|
||||
must_count_input_entries, compaction_filter, shutting_down, info_log,
|
||||
full_history_ts_low, preserve_time_min_seqno,
|
||||
preclude_last_level_min_seqno) {}
|
||||
compaction_filter, shutting_down, info_log, full_history_ts_low,
|
||||
preserve_time_min_seqno, preclude_last_level_min_seqno) {}
|
||||
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
@@ -61,14 +58,15 @@ CompactionIterator::CompactionIterator(
|
||||
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,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const std::shared_ptr<Logger> info_log,
|
||||
const std::string* full_history_ts_low,
|
||||
const SequenceNumber preserve_time_min_seqno,
|
||||
const SequenceNumber preclude_last_level_min_seqno)
|
||||
: input_(input, cmp, must_count_input_entries),
|
||||
: input_(input, cmp,
|
||||
!compaction || compaction->DoesInputReferenceBlobFiles()),
|
||||
cmp_(cmp),
|
||||
merge_helper_(merge_helper),
|
||||
snapshots_(snapshots),
|
||||
@@ -424,13 +422,16 @@ bool CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
return false;
|
||||
} else if (decision == CompactionFilter::Decision::kChangeWideColumnEntity) {
|
||||
WideColumns sorted_columns;
|
||||
sorted_columns.reserve(new_columns.size());
|
||||
|
||||
sorted_columns.reserve(new_columns.size());
|
||||
for (const auto& column : new_columns) {
|
||||
sorted_columns.emplace_back(column.first, column.second);
|
||||
}
|
||||
|
||||
WideColumnsHelper::SortColumns(sorted_columns);
|
||||
std::sort(sorted_columns.begin(), sorted_columns.end(),
|
||||
[](const WideColumn& lhs, const WideColumn& rhs) {
|
||||
return lhs.name().compare(rhs.name()) < 0;
|
||||
});
|
||||
|
||||
{
|
||||
const Status s = WideColumnSerialization::Serialize(
|
||||
@@ -1227,7 +1228,7 @@ void CompactionIterator::DecideOutputLevel() {
|
||||
// not from this compaction.
|
||||
// TODO: add statistic for declined output_to_penultimate_level
|
||||
bool safe_to_penultimate_level =
|
||||
compaction_->WithinPenultimateLevelOutputRange(ikey_);
|
||||
compaction_->WithinPenultimateLevelOutputRange(ikey_.user_key);
|
||||
if (!safe_to_penultimate_level) {
|
||||
output_to_penultimate_level_ = false;
|
||||
// It could happen when disable/enable `last_level_temperature` while
|
||||
@@ -1256,13 +1257,10 @@ 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();
|
||||
}
|
||||
if (compaction_ != nullptr && compaction_->SupportsPerKeyPlacement()) {
|
||||
DecideOutputLevel();
|
||||
}
|
||||
|
||||
// Zeroing out the sequence number leads to better compression.
|
||||
|
||||
@@ -38,18 +38,15 @@ class SequenceIterWrapper : public InternalIterator {
|
||||
bool Valid() const override { return inner_iter_->Valid(); }
|
||||
Status status() const override { return inner_iter_->status(); }
|
||||
void Next() override {
|
||||
if (!inner_iter_->IsDeleteRangeSentinelKey()) {
|
||||
num_itered_++;
|
||||
}
|
||||
num_itered_++;
|
||||
inner_iter_->Next();
|
||||
}
|
||||
void Seek(const Slice& target) override {
|
||||
if (!need_count_entries_) {
|
||||
has_num_itered_ = false;
|
||||
inner_iter_->Seek(target);
|
||||
} else {
|
||||
// Need to count total number of entries,
|
||||
// so we do Next() rather than Seek().
|
||||
// For flush cases, we need to count total number of entries, so we
|
||||
// do Next() rather than Seek().
|
||||
while (inner_iter_->Valid() &&
|
||||
icmp_.Compare(inner_iter_->key(), target) < 0) {
|
||||
Next();
|
||||
@@ -65,8 +62,7 @@ class SequenceIterWrapper : public InternalIterator {
|
||||
void SeekForPrev(const Slice& /* target */) override { assert(false); }
|
||||
void SeekToLast() override { assert(false); }
|
||||
|
||||
uint64_t NumItered() const { return num_itered_; }
|
||||
bool HasNumItered() const { return has_num_itered_; }
|
||||
uint64_t num_itered() const { return num_itered_; }
|
||||
bool IsDeleteRangeSentinelKey() const override {
|
||||
assert(Valid());
|
||||
return inner_iter_->IsDeleteRangeSentinelKey();
|
||||
@@ -77,7 +73,6 @@ class SequenceIterWrapper : public InternalIterator {
|
||||
InternalIterator* inner_iter_; // not owned
|
||||
uint64_t num_itered_ = 0;
|
||||
bool need_count_entries_;
|
||||
bool has_num_itered_ = true;
|
||||
};
|
||||
|
||||
class CompactionIterator {
|
||||
@@ -119,8 +114,7 @@ class CompactionIterator {
|
||||
virtual bool SupportsPerKeyPlacement() const = 0;
|
||||
|
||||
// `key` includes timestamp if user-defined timestamp is enabled.
|
||||
virtual bool WithinPenultimateLevelOutputRange(
|
||||
const ParsedInternalKey&) const = 0;
|
||||
virtual bool WithinPenultimateLevelOutputRange(const Slice& key) const = 0;
|
||||
};
|
||||
|
||||
class RealCompaction : public CompactionProxy {
|
||||
@@ -187,19 +181,14 @@ class CompactionIterator {
|
||||
// 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);
|
||||
bool WithinPenultimateLevelOutputRange(const Slice& key) const override {
|
||||
return compaction_->WithinPenultimateLevelOutputRange(key);
|
||||
}
|
||||
|
||||
private:
|
||||
const Compaction* compaction_;
|
||||
};
|
||||
|
||||
// @param must_count_input_entries if true, `NumInputEntryScanned()` will
|
||||
// return the number of input keys scanned. If false, `NumInputEntryScanned()`
|
||||
// will return this number if no Seek was called on `input`. User should call
|
||||
// `HasNumInputEntryScanned()` first in this case.
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
@@ -210,7 +199,7 @@ class CompactionIterator {
|
||||
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
|
||||
bool enforce_single_del_contracts,
|
||||
const std::atomic<bool>& manual_compaction_canceled,
|
||||
bool must_count_input_entries, const Compaction* compaction = nullptr,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
@@ -230,7 +219,6 @@ class CompactionIterator {
|
||||
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,
|
||||
@@ -265,8 +253,7 @@ class CompactionIterator {
|
||||
return current_user_key_;
|
||||
}
|
||||
const CompactionIterationStats& iter_stats() const { return iter_stats_; }
|
||||
bool HasNumInputEntryScanned() const { return input_.HasNumItered(); }
|
||||
uint64_t NumInputEntryScanned() const { return input_.NumItered(); }
|
||||
uint64_t num_input_entry_scanned() const { return input_.num_itered(); }
|
||||
// If the current key should be placed on penultimate level, only valid if
|
||||
// per_key_placement is supported
|
||||
bool output_to_penultimate_level() const {
|
||||
|
||||
@@ -184,9 +184,8 @@ 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 WithinPenultimateLevelOutputRange(const Slice& key) const override {
|
||||
return (!key.starts_with("unsafe_pb"));
|
||||
}
|
||||
|
||||
bool key_not_exists_beyond_output_level = false;
|
||||
@@ -294,8 +293,8 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
nullptr /* blob_file_builder */, true /*allow_data_in_errors*/,
|
||||
true /*enforce_single_del_contracts*/,
|
||||
/*manual_compaction_canceled=*/kManualCompactionCanceledFalse_,
|
||||
std::move(compaction), /*must_count_input_entries=*/false, filter,
|
||||
&shutting_down_, /*info_log=*/nullptr, full_history_ts_low));
|
||||
std::move(compaction), filter, &shutting_down_, /*info_log=*/nullptr,
|
||||
full_history_ts_low));
|
||||
}
|
||||
|
||||
void AddSnapshot(SequenceNumber snapshot,
|
||||
|
||||
+95
-194
@@ -288,47 +288,45 @@ void CompactionJob::Prepare() {
|
||||
|
||||
if (preserve_time_duration > 0) {
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
// Setup seqno_to_time_mapping_ with relevant time range.
|
||||
seqno_to_time_mapping_.SetMaxTimeSpan(preserve_time_duration);
|
||||
// setup seqno_time_mapping_
|
||||
seqno_time_mapping_.SetMaxTimeDuration(preserve_time_duration);
|
||||
for (const auto& each_level : *c->inputs()) {
|
||||
for (const auto& fmd : each_level.files) {
|
||||
std::shared_ptr<const TableProperties> tp;
|
||||
Status s =
|
||||
cfd->current()->GetTableProperties(read_options, &tp, fmd, nullptr);
|
||||
if (s.ok()) {
|
||||
s = seqno_to_time_mapping_.DecodeFrom(tp->seqno_to_time_mapping);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"Problem reading or processing seqno-to-time mapping: %s",
|
||||
s.ToString().c_str());
|
||||
seqno_time_mapping_.Add(tp->seqno_to_time_mapping)
|
||||
.PermitUncheckedError();
|
||||
seqno_time_mapping_.Add(fmd->fd.smallest_seqno,
|
||||
fmd->oldest_ancester_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto status = seqno_time_mapping_.Sort();
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"Invalid sequence number to time mapping: Status: %s",
|
||||
status.ToString().c_str());
|
||||
}
|
||||
int64_t _current_time = 0;
|
||||
Status s = db_options_.clock->GetCurrentTime(&_current_time);
|
||||
if (!s.ok()) {
|
||||
status = db_options_.clock->GetCurrentTime(&_current_time);
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"Failed to get current time in compaction: Status: %s",
|
||||
s.ToString().c_str());
|
||||
status.ToString().c_str());
|
||||
// preserve all time information
|
||||
preserve_time_min_seqno_ = 0;
|
||||
preclude_last_level_min_seqno_ = 0;
|
||||
seqno_to_time_mapping_.Enforce();
|
||||
} else {
|
||||
seqno_to_time_mapping_.Enforce(_current_time);
|
||||
seqno_time_mapping_.TruncateOldEntries(_current_time);
|
||||
uint64_t preserve_time =
|
||||
static_cast<uint64_t>(_current_time) > preserve_time_duration
|
||||
? _current_time - preserve_time_duration
|
||||
: 0;
|
||||
// GetProximalSeqnoBeforeTime tells us the last seqno known to have been
|
||||
// written at or before the given time. + 1 to get the minimum we should
|
||||
// preserve without excluding anything that might have been written on or
|
||||
// after the given time.
|
||||
preserve_time_min_seqno_ =
|
||||
seqno_to_time_mapping_.GetProximalSeqnoBeforeTime(preserve_time) + 1;
|
||||
seqno_time_mapping_.GetOldestSequenceNum(preserve_time);
|
||||
if (c->immutable_options()->preclude_last_level_data_seconds > 0) {
|
||||
uint64_t preclude_last_level_time =
|
||||
static_cast<uint64_t>(_current_time) >
|
||||
@@ -337,21 +335,9 @@ void CompactionJob::Prepare() {
|
||||
c->immutable_options()->preclude_last_level_data_seconds
|
||||
: 0;
|
||||
preclude_last_level_min_seqno_ =
|
||||
seqno_to_time_mapping_.GetProximalSeqnoBeforeTime(
|
||||
preclude_last_level_time) +
|
||||
1;
|
||||
seqno_time_mapping_.GetOldestSequenceNum(preclude_last_level_time);
|
||||
}
|
||||
}
|
||||
// For accuracy of the GetProximalSeqnoBeforeTime queries above, we only
|
||||
// limit the capacity after them.
|
||||
// Here If we set capacity to the per-SST limit, we could be throwing away
|
||||
// fidelity when a compaction output file has a narrower seqno range than
|
||||
// all the inputs. If we only limit capacity for each compaction output, we
|
||||
// could be doing a lot of unnecessary recomputation in a large compaction
|
||||
// (up to quadratic in number of files). Thus, we do soemthing in the
|
||||
// middle: enforce a resonably large constant size limit substantially
|
||||
// larger than kMaxSeqnoTimePairsPerSST.
|
||||
seqno_to_time_mapping_.SetCapacity(kMaxSeqnoToTimeEntries);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,6 +436,14 @@ void CompactionJob::ReleaseSubcompactionResources() {
|
||||
ShrinkSubcompactionResources(extra_num_subcompaction_threads_reserved_);
|
||||
}
|
||||
|
||||
struct RangeWithSize {
|
||||
Range range;
|
||||
uint64_t size;
|
||||
|
||||
RangeWithSize(const Slice& a, const Slice& b, uint64_t s = 0)
|
||||
: range(a, b), size(s) {}
|
||||
};
|
||||
|
||||
void CompactionJob::GenSubcompactionBoundaries() {
|
||||
// The goal is to find some boundary keys so that we can evenly partition
|
||||
// the compaction input data into max_subcompactions ranges.
|
||||
@@ -476,8 +470,7 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
// overlap with N-1 other ranges. Since we requested a relatively large number
|
||||
// (128) of ranges from each input files, even N range overlapping would
|
||||
// cause relatively small inaccuracy.
|
||||
ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
read_options.rate_limiter_priority = GetRateLimiterPriority();
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
auto* c = compact_->compaction;
|
||||
if (c->max_subcompactions() <= 1 &&
|
||||
!(c->immutable_options()->compaction_pri == kRoundRobin &&
|
||||
@@ -729,9 +722,8 @@ Status CompactionJob::Run() {
|
||||
// use_direct_io_for_flush_and_compaction is true, we will regard this
|
||||
// verification as user reads since the goal is to cache it here for
|
||||
// further user reads
|
||||
ReadOptions verify_table_read_options(Env::IOActivity::kCompaction);
|
||||
verify_table_read_options.rate_limiter_priority =
|
||||
GetRateLimiterPriority();
|
||||
const ReadOptions verify_table_read_options(
|
||||
Env::IOActivity::kCompaction);
|
||||
InternalIterator* iter = cfd->table_cache()->NewIterator(
|
||||
verify_table_read_options, file_options_,
|
||||
cfd->internal_comparator(), files_output[file_idx]->meta,
|
||||
@@ -752,6 +744,7 @@ Status CompactionJob::Run() {
|
||||
|
||||
if (s.ok() && paranoid_file_checks_) {
|
||||
OutputValidator validator(cfd->internal_comparator(),
|
||||
/*_enable_order_check=*/true,
|
||||
/*_enable_hash=*/true);
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
s = validator.Add(iter->key(), iter->value());
|
||||
@@ -803,49 +796,15 @@ Status CompactionJob::Run() {
|
||||
auto fn =
|
||||
TableFileName(state.compaction->immutable_options()->cf_paths,
|
||||
output.meta.fd.GetNumber(), output.meta.fd.GetPathId());
|
||||
compact_->compaction->SetOutputTableProperties(fn,
|
||||
output.table_properties);
|
||||
tp[fn] = output.table_properties;
|
||||
}
|
||||
}
|
||||
compact_->compaction->SetOutputTableProperties(std::move(tp));
|
||||
|
||||
// Finish up all bookkeeping to unify the subcompaction results.
|
||||
// Finish up all book-keeping to unify the subcompaction results
|
||||
compact_->AggregateCompactionStats(compaction_stats_, *compaction_job_stats_);
|
||||
uint64_t num_input_range_del = 0;
|
||||
bool ok = UpdateCompactionStats(&num_input_range_del);
|
||||
// (Sub)compactions returned ok, do sanity check on the number of input keys.
|
||||
if (status.ok() && ok && compaction_job_stats_->has_num_input_records) {
|
||||
size_t ts_sz = compact_->compaction->column_family_data()
|
||||
->user_comparator()
|
||||
->timestamp_size();
|
||||
// When trim_ts_ is non-empty, CompactionIterator takes
|
||||
// HistoryTrimmingIterator as input iterator and sees a trimmed view of
|
||||
// input keys. So the number of keys it processed is not suitable for
|
||||
// verification here.
|
||||
// TODO: support verification when trim_ts_ is non-empty.
|
||||
if (!(ts_sz > 0 && !trim_ts_.empty())) {
|
||||
assert(compaction_stats_.stats.num_input_records > 0);
|
||||
// TODO: verify the number of range deletion entries.
|
||||
uint64_t expected =
|
||||
compaction_stats_.stats.num_input_records - num_input_range_del;
|
||||
uint64_t actual = compaction_job_stats_->num_input_records;
|
||||
if (expected != actual) {
|
||||
char scratch[2345];
|
||||
compact_->compaction->Summary(scratch, sizeof(scratch));
|
||||
std::string msg =
|
||||
"Compaction number of input keys does not match "
|
||||
"number of keys processed. Expected " +
|
||||
std::to_string(expected) + " but processed " +
|
||||
std::to_string(actual) + ". Compaction summary: " + scratch;
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log, "[%s] [JOB %d] Compaction with status: %s",
|
||||
compact_->compaction->column_family_data()->GetName().c_str(),
|
||||
job_context_->job_id, msg.c_str());
|
||||
if (db_options_.compaction_verify_record_count) {
|
||||
status = Status::Corruption(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
UpdateCompactionStats();
|
||||
|
||||
RecordCompactionIOStats();
|
||||
LogFlush(db_options_.info_log);
|
||||
TEST_SYNC_POINT("CompactionJob::Run():End");
|
||||
@@ -854,8 +813,7 @@ Status CompactionJob::Run() {
|
||||
return status;
|
||||
}
|
||||
|
||||
Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options,
|
||||
bool* compaction_released) {
|
||||
Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
assert(compact_);
|
||||
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
@@ -871,7 +829,7 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options,
|
||||
compaction_stats_);
|
||||
|
||||
if (status.ok()) {
|
||||
status = InstallCompactionResults(mutable_cf_options, compaction_released);
|
||||
status = InstallCompactionResults(mutable_cf_options);
|
||||
}
|
||||
if (!versions_->io_status().ok()) {
|
||||
io_status_ = versions_->io_status();
|
||||
@@ -1134,24 +1092,21 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
// (b) CompactionFilter::Decision::kRemoveAndSkipUntil.
|
||||
read_options.total_order_seek = true;
|
||||
|
||||
const WriteOptions write_options(Env::IOPriority::IO_LOW,
|
||||
Env::IOActivity::kCompaction);
|
||||
|
||||
// Remove the timestamps from boundaries because boundaries created in
|
||||
// GenSubcompactionBoundaries doesn't strip away the timestamp.
|
||||
size_t ts_sz = cfd->user_comparator()->timestamp_size();
|
||||
if (start.has_value()) {
|
||||
read_options.iterate_lower_bound = &(*start);
|
||||
read_options.iterate_lower_bound = &start.value();
|
||||
if (ts_sz > 0) {
|
||||
start_without_ts = StripTimestampFromUserKey(*start, ts_sz);
|
||||
read_options.iterate_lower_bound = &(*start_without_ts);
|
||||
start_without_ts = StripTimestampFromUserKey(start.value(), ts_sz);
|
||||
read_options.iterate_lower_bound = &start_without_ts.value();
|
||||
}
|
||||
}
|
||||
if (end.has_value()) {
|
||||
read_options.iterate_upper_bound = &(*end);
|
||||
read_options.iterate_upper_bound = &end.value();
|
||||
if (ts_sz > 0) {
|
||||
end_without_ts = StripTimestampFromUserKey(*end, ts_sz);
|
||||
read_options.iterate_upper_bound = &(*end_without_ts);
|
||||
end_without_ts = StripTimestampFromUserKey(end.value(), ts_sz);
|
||||
read_options.iterate_upper_bound = &end_without_ts.value();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1183,7 +1138,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
|
||||
if (start.has_value()) {
|
||||
start_ikey.SetInternalKey(*start, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
start_ikey.SetInternalKey(start.value(), kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
if (ts_sz > 0) {
|
||||
start_ikey.UpdateInternalKey(kMaxSequenceNumber, kValueTypeForSeek,
|
||||
&ts_slice);
|
||||
@@ -1192,7 +1148,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
start_user_key = start_ikey.GetUserKey();
|
||||
}
|
||||
if (end.has_value()) {
|
||||
end_ikey.SetInternalKey(*end, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
end_ikey.SetInternalKey(end.value(), kMaxSequenceNumber, kValueTypeForSeek);
|
||||
if (ts_sz > 0) {
|
||||
end_ikey.UpdateInternalKey(kMaxSequenceNumber, kValueTypeForSeek,
|
||||
&ts_slice);
|
||||
@@ -1271,17 +1227,18 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
? new BlobFileBuilder(
|
||||
versions_, fs_.get(),
|
||||
sub_compact->compaction->immutable_options(),
|
||||
mutable_cf_options, &file_options_, &write_options, db_id_,
|
||||
db_session_id_, job_id_, cfd->GetID(), cfd->GetName(),
|
||||
mutable_cf_options, &file_options_, db_id_, db_session_id_,
|
||||
job_id_, cfd->GetID(), cfd->GetName(), Env::IOPriority::IO_LOW,
|
||||
write_hint_, io_tracer_, blob_callback_,
|
||||
BlobFileCreationReason::kCompaction, &blob_file_paths,
|
||||
sub_compact->Current().GetBlobFileAdditionsPtr())
|
||||
: nullptr);
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run():Inprogress");
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::Run():PausingManualCompaction:1",
|
||||
static_cast<void*>(const_cast<std::atomic<bool>*>(
|
||||
&manual_compaction_canceled_)));
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:1",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<bool>*>(&manual_compaction_canceled_)));
|
||||
|
||||
const std::string* const full_history_ts_low =
|
||||
full_history_ts_low_.empty() ? nullptr : &full_history_ts_low_;
|
||||
@@ -1296,8 +1253,6 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
/*expect_valid_internal_key=*/true, range_del_agg.get(),
|
||||
blob_file_builder.get(), db_options_.allow_data_in_errors,
|
||||
db_options_.enforce_single_del_contracts, manual_compaction_canceled_,
|
||||
sub_compact->compaction
|
||||
->DoesInputReferenceBlobFiles() /* must_count_input_entries */,
|
||||
sub_compact->compaction, compaction_filter, shutting_down_,
|
||||
db_options_.info_log, full_history_ts_low, preserve_time_min_seqno_,
|
||||
preclude_last_level_min_seqno_);
|
||||
@@ -1329,25 +1284,19 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
Status status;
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::ProcessKeyValueCompaction()::Processing",
|
||||
static_cast<void*>(const_cast<Compaction*>(sub_compact->compaction)));
|
||||
uint64_t last_cpu_micros = prev_cpu_micros;
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<Compaction*>(sub_compact->compaction)));
|
||||
while (status.ok() && !cfd->IsDropped() && c_iter->Valid()) {
|
||||
// Invariant: c_iter.status() is guaranteed to be OK if c_iter->Valid()
|
||||
// returns true.
|
||||
assert(!end.has_value() ||
|
||||
cfd->user_comparator()->Compare(c_iter->user_key(), *end) < 0);
|
||||
assert(!end.has_value() || cfd->user_comparator()->Compare(
|
||||
c_iter->user_key(), end.value()) < 0);
|
||||
|
||||
if (c_iter_stats.num_input_records % kRecordStatsEvery ==
|
||||
kRecordStatsEvery - 1) {
|
||||
RecordDroppedKeys(c_iter_stats, &sub_compact->compaction_job_stats);
|
||||
c_iter->ResetRecordCounts();
|
||||
RecordCompactionIOStats();
|
||||
|
||||
uint64_t cur_cpu_micros = db_options_.clock->CPUMicros();
|
||||
assert(cur_cpu_micros >= last_cpu_micros);
|
||||
RecordTick(stats_, COMPACTION_CPU_TOTAL_TIME,
|
||||
cur_cpu_micros - last_cpu_micros);
|
||||
last_cpu_micros = cur_cpu_micros;
|
||||
}
|
||||
|
||||
// Add current compaction_iterator key to target compaction output, if the
|
||||
@@ -1360,32 +1309,16 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
break;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::Run():PausingManualCompaction:2",
|
||||
static_cast<void*>(const_cast<std::atomic<bool>*>(
|
||||
&manual_compaction_canceled_)));
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:2",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<bool>*>(&manual_compaction_canceled_)));
|
||||
c_iter->Next();
|
||||
if (c_iter->status().IsManualCompactionPaused()) {
|
||||
break;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool stop = false;
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::ProcessKeyValueCompaction()::stop",
|
||||
static_cast<void*>(&stop));
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
|
||||
// This number may not be accurate when CompactionIterator was created
|
||||
// with `must_count_input_entries=false`.
|
||||
assert(!sub_compact->compaction->DoesInputReferenceBlobFiles() ||
|
||||
c_iter->HasNumInputEntryScanned());
|
||||
sub_compact->compaction_job_stats.has_num_input_records =
|
||||
c_iter->HasNumInputEntryScanned();
|
||||
sub_compact->compaction_job_stats.num_input_records =
|
||||
c_iter->NumInputEntryScanned();
|
||||
sub_compact->compaction_job_stats.num_blobs_read =
|
||||
c_iter_stats.num_blobs_read;
|
||||
sub_compact->compaction_job_stats.total_blob_bytes_read =
|
||||
@@ -1454,11 +1387,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
sub_compact->Current().UpdateBlobStats();
|
||||
}
|
||||
|
||||
uint64_t cur_cpu_micros = db_options_.clock->CPUMicros();
|
||||
sub_compact->compaction_job_stats.cpu_micros =
|
||||
cur_cpu_micros - prev_cpu_micros;
|
||||
RecordTick(stats_, COMPACTION_CPU_TOTAL_TIME,
|
||||
cur_cpu_micros - last_cpu_micros);
|
||||
db_options_.clock->CPUMicros() - prev_cpu_micros;
|
||||
|
||||
if (measure_io_stats_) {
|
||||
sub_compact->compaction_job_stats.file_write_nanos +=
|
||||
@@ -1580,7 +1510,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
|
||||
const uint64_t current_entries = outputs.NumEntries();
|
||||
|
||||
s = outputs.Finish(s, seqno_to_time_mapping_);
|
||||
s = outputs.Finish(s, seqno_time_mapping_);
|
||||
|
||||
if (s.ok()) {
|
||||
// With accurate smallest and largest key, we can get a slightly more
|
||||
@@ -1708,14 +1638,12 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
}
|
||||
|
||||
Status CompactionJob::InstallCompactionResults(
|
||||
const MutableCFOptions& mutable_cf_options, bool* compaction_released) {
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
assert(compact_);
|
||||
|
||||
db_mutex_->AssertHeld();
|
||||
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
const WriteOptions write_options(Env::IOActivity::kCompaction);
|
||||
|
||||
auto* compaction = compact_->compaction;
|
||||
assert(compaction);
|
||||
|
||||
@@ -1792,16 +1720,9 @@ Status CompactionJob::InstallCompactionResults(
|
||||
}
|
||||
}
|
||||
|
||||
auto manifest_wcb = [&compaction, &compaction_released](const Status& s) {
|
||||
compaction->ReleaseCompactionFiles(s);
|
||||
*compaction_released = true;
|
||||
};
|
||||
|
||||
return versions_->LogAndApply(
|
||||
compaction->column_family_data(), mutable_cf_options, read_options,
|
||||
write_options, edit, db_mutex_, db_directory_,
|
||||
/*new_descriptor_log=*/false,
|
||||
/*column_family_options=*/nullptr, manifest_wcb);
|
||||
return versions_->LogAndApply(compaction->column_family_data(),
|
||||
mutable_cf_options, read_options, edit,
|
||||
db_mutex_, db_directory_);
|
||||
}
|
||||
|
||||
void CompactionJob::RecordCompactionIOStats() {
|
||||
@@ -1897,18 +1818,16 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
uint64_t current_time = static_cast<uint64_t>(temp_current_time);
|
||||
InternalKey tmp_start, tmp_end;
|
||||
if (sub_compact->start.has_value()) {
|
||||
tmp_start.SetMinPossibleForUserKey(*(sub_compact->start));
|
||||
tmp_start.SetMinPossibleForUserKey(sub_compact->start.value());
|
||||
}
|
||||
if (sub_compact->end.has_value()) {
|
||||
tmp_end.SetMinPossibleForUserKey(*(sub_compact->end));
|
||||
tmp_end.SetMinPossibleForUserKey(sub_compact->end.value());
|
||||
}
|
||||
uint64_t oldest_ancester_time =
|
||||
sub_compact->compaction->MinInputFileOldestAncesterTime(
|
||||
sub_compact->start.has_value() ? &tmp_start : nullptr,
|
||||
sub_compact->end.has_value() ? &tmp_end : nullptr);
|
||||
if (oldest_ancester_time == std::numeric_limits<uint64_t>::max()) {
|
||||
// TODO: fix DBSSTTest.GetTotalSstFilesSize and use
|
||||
// kUnknownOldestAncesterTime
|
||||
oldest_ancester_time = current_time;
|
||||
}
|
||||
|
||||
@@ -1936,6 +1855,8 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
}
|
||||
|
||||
outputs.AddOutput(std::move(meta), cfd->internal_comparator(),
|
||||
sub_compact->compaction->mutable_cf_options()
|
||||
->check_flush_compaction_key_order,
|
||||
paranoid_file_checks_);
|
||||
}
|
||||
|
||||
@@ -1948,17 +1869,12 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
sub_compact->compaction->immutable_options()->listeners;
|
||||
outputs.AssignFileWriter(new WritableFileWriter(
|
||||
std::move(writable_file), fname, fo_copy, db_options_.clock, io_tracer_,
|
||||
db_options_.stats, Histograms::SST_WRITE_MICROS, listeners,
|
||||
db_options_.file_checksum_gen_factory.get(),
|
||||
db_options_.stats, listeners, db_options_.file_checksum_gen_factory.get(),
|
||||
tmp_set.Contains(FileType::kTableFile), false));
|
||||
|
||||
// TODO(hx235): pass in the correct `oldest_key_time` instead of `0`
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
const WriteOptions write_options(Env::IOActivity::kCompaction);
|
||||
TableBuilderOptions tboptions(
|
||||
*cfd->ioptions(), *(sub_compact->compaction->mutable_cf_options()),
|
||||
read_options, write_options, cfd->internal_comparator(),
|
||||
cfd->internal_tbl_prop_coll_factories(),
|
||||
cfd->internal_comparator(), cfd->int_tbl_prop_collector_factories(),
|
||||
sub_compact->compaction->output_compression(),
|
||||
sub_compact->compaction->output_compression_opts(), cfd->GetID(),
|
||||
cfd->GetName(), sub_compact->compaction->output_level(),
|
||||
@@ -1988,53 +1904,24 @@ void CopyPrefix(const Slice& src, size_t prefix_length, std::string* dst) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool CompactionJob::UpdateCompactionStats(uint64_t* num_input_range_del) {
|
||||
|
||||
void CompactionJob::UpdateCompactionStats() {
|
||||
assert(compact_);
|
||||
|
||||
Compaction* compaction = compact_->compaction;
|
||||
compaction_stats_.stats.num_input_files_in_non_output_levels = 0;
|
||||
compaction_stats_.stats.num_input_files_in_output_level = 0;
|
||||
|
||||
bool has_error = false;
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
const auto& input_table_properties = compaction->GetInputTableProperties();
|
||||
for (int input_level = 0;
|
||||
input_level < static_cast<int>(compaction->num_input_levels());
|
||||
++input_level) {
|
||||
size_t num_input_files = compaction->num_input_files(input_level);
|
||||
uint64_t* bytes_read;
|
||||
if (compaction->level(input_level) != compaction->output_level()) {
|
||||
compaction_stats_.stats.num_input_files_in_non_output_levels +=
|
||||
static_cast<int>(num_input_files);
|
||||
bytes_read = &compaction_stats_.stats.bytes_read_non_output_levels;
|
||||
UpdateCompactionInputStatsHelper(
|
||||
&compaction_stats_.stats.num_input_files_in_non_output_levels,
|
||||
&compaction_stats_.stats.bytes_read_non_output_levels, input_level);
|
||||
} else {
|
||||
compaction_stats_.stats.num_input_files_in_output_level +=
|
||||
static_cast<int>(num_input_files);
|
||||
bytes_read = &compaction_stats_.stats.bytes_read_output_level;
|
||||
}
|
||||
for (size_t i = 0; i < num_input_files; ++i) {
|
||||
const FileMetaData* file_meta = compaction->input(input_level, i);
|
||||
*bytes_read += file_meta->fd.GetFileSize();
|
||||
uint64_t file_input_entries = file_meta->num_entries;
|
||||
uint64_t file_num_range_del = file_meta->num_range_deletions;
|
||||
if (file_input_entries == 0) {
|
||||
uint64_t file_number = file_meta->fd.GetNumber();
|
||||
// Try getting info from table property
|
||||
std::string fn =
|
||||
TableFileName(compaction->immutable_options()->cf_paths,
|
||||
file_number, file_meta->fd.GetPathId());
|
||||
const auto& tp = input_table_properties.find(fn);
|
||||
if (tp != input_table_properties.end()) {
|
||||
file_input_entries = tp->second->num_entries;
|
||||
file_num_range_del = tp->second->num_range_deletions;
|
||||
} else {
|
||||
has_error = true;
|
||||
}
|
||||
}
|
||||
compaction_stats_.stats.num_input_records += file_input_entries;
|
||||
if (num_input_range_del) {
|
||||
*num_input_range_del += file_num_range_del;
|
||||
}
|
||||
UpdateCompactionInputStatsHelper(
|
||||
&compaction_stats_.stats.num_input_files_in_output_level,
|
||||
&compaction_stats_.stats.bytes_read_output_level, input_level);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2044,7 +1931,21 @@ bool CompactionJob::UpdateCompactionStats(uint64_t* num_input_range_del) {
|
||||
|
||||
compaction_stats_.stats.num_dropped_records =
|
||||
compaction_stats_.DroppedRecords();
|
||||
return !has_error;
|
||||
}
|
||||
|
||||
void CompactionJob::UpdateCompactionInputStatsHelper(int* num_files,
|
||||
uint64_t* bytes_read,
|
||||
int input_level) {
|
||||
const Compaction* compaction = compact_->compaction;
|
||||
auto num_input_files = compaction->num_input_files(input_level);
|
||||
*num_files += static_cast<int>(num_input_files);
|
||||
|
||||
for (size_t i = 0; i < num_input_files; ++i) {
|
||||
const auto* file_meta = compaction->input(input_level, i);
|
||||
*bytes_read += file_meta->fd.GetFileSize();
|
||||
compaction_stats_.stats.num_input_records +=
|
||||
static_cast<uint64_t>(file_meta->num_entries);
|
||||
}
|
||||
}
|
||||
|
||||
void CompactionJob::UpdateCompactionJobStats(
|
||||
|
||||
@@ -186,30 +186,13 @@ class CompactionJob {
|
||||
|
||||
// REQUIRED: mutex held
|
||||
// Add compaction input/output to the current version
|
||||
// Releases compaction file through Compaction::ReleaseCompactionFiles().
|
||||
// Sets *compaction_released to true if compaction is released.
|
||||
Status Install(const MutableCFOptions& mutable_cf_options,
|
||||
bool* compaction_released);
|
||||
Status Install(const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Return the IO status
|
||||
IOStatus io_status() const { return io_status_; }
|
||||
|
||||
protected:
|
||||
// Update the following stats in compaction_stats_.stats
|
||||
// - num_input_files_in_non_output_levels
|
||||
// - num_input_files_in_output_level
|
||||
// - bytes_read_non_output_levels
|
||||
// - bytes_read_output_level
|
||||
// - num_input_records
|
||||
// - bytes_read_blob
|
||||
// - num_dropped_records
|
||||
//
|
||||
// @param num_input_range_del if non-null, will be set to the number of range
|
||||
// deletion entries in this compaction input.
|
||||
//
|
||||
// Returns true iff compaction_stats_.stats.num_input_records and
|
||||
// num_input_range_del are calculated successfully.
|
||||
bool UpdateCompactionStats(uint64_t* num_input_range_del = nullptr);
|
||||
void UpdateCompactionStats();
|
||||
void LogCompaction();
|
||||
virtual void RecordCompactionIOStats();
|
||||
void CleanupCompaction();
|
||||
@@ -276,8 +259,7 @@ class CompactionJob {
|
||||
const Slice& next_table_min_key,
|
||||
const Slice* comp_start_user_key,
|
||||
const Slice* comp_end_user_key);
|
||||
Status InstallCompactionResults(const MutableCFOptions& mutable_cf_options,
|
||||
bool* compaction_released);
|
||||
Status InstallCompactionResults(const MutableCFOptions& mutable_cf_options);
|
||||
Status OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
CompactionOutputs& outputs);
|
||||
void UpdateCompactionJobStats(
|
||||
@@ -285,6 +267,9 @@ class CompactionJob {
|
||||
void RecordDroppedKeys(const CompactionIterationStats& c_iter_stats,
|
||||
CompactionJobStats* compaction_job_stats = nullptr);
|
||||
|
||||
void UpdateCompactionInputStatsHelper(int* num_files, uint64_t* bytes_read,
|
||||
int input_level);
|
||||
|
||||
void NotifyOnSubcompactionBegin(SubcompactionState* sub_compact);
|
||||
|
||||
void NotifyOnSubcompactionCompleted(SubcompactionState* sub_compact);
|
||||
@@ -350,7 +335,7 @@ class CompactionJob {
|
||||
|
||||
// Stores the sequence number to time mapping gathered from all input files
|
||||
// it also collects the smallest_seqno -> oldest_ancester_time from the SST.
|
||||
SeqnoToTimeMapping seqno_to_time_mapping_;
|
||||
SeqnoToTimeMapping seqno_time_mapping_;
|
||||
|
||||
// Minimal sequence number for preserving the time information. The time info
|
||||
// older than this sequence number won't be preserved after the compaction and
|
||||
|
||||
@@ -215,9 +215,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
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)),
|
||||
/*io_tracer=*/nullptr, /*db_id*/ "", /*db_session_id*/ "")),
|
||||
shutting_down_(false),
|
||||
mock_table_factory_(new mock::MockTableFactory()),
|
||||
error_handler_(nullptr, db_options_, &mutex_),
|
||||
@@ -295,14 +293,11 @@ class CompactionJobTestBase : public testing::Test {
|
||||
Status s = WritableFileWriter::Create(fs_, table_name, FileOptions(),
|
||||
&file_writer, nullptr);
|
||||
ASSERT_OK(s);
|
||||
const ReadOptions read_options;
|
||||
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(),
|
||||
cfd_->int_tbl_prop_collector_factories(),
|
||||
CompressionType::kNoCompression,
|
||||
CompressionOptions(), 0 /* column_family_id */,
|
||||
kDefaultColumnFamilyName, -1 /* level */),
|
||||
@@ -397,7 +392,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
mutex_.Lock();
|
||||
EXPECT_OK(versions_->LogAndApply(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), mutable_cf_options_,
|
||||
read_options_, write_options_, &edit, &mutex_, nullptr));
|
||||
read_options_, &edit, &mutex_, nullptr));
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
@@ -545,14 +540,13 @@ 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,
|
||||
/*db_id=*/"", /*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*/ ""));
|
||||
compaction_job_stats_.Reset();
|
||||
ASSERT_OK(SetIdentityFile(WriteOptions(), env_, dbname_));
|
||||
ASSERT_OK(SetIdentityFile(env_, dbname_));
|
||||
|
||||
VersionEdit new_db;
|
||||
new_db.SetLogNumber(0);
|
||||
@@ -571,11 +565,11 @@ class CompactionJobTestBase : public testing::Test {
|
||||
log::Writer log(std::move(file_writer), 0, false);
|
||||
std::string record;
|
||||
new_db.EncodeTo(&record);
|
||||
s = log.AddRecord(WriteOptions(), record);
|
||||
s = log.AddRecord(record);
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
// Make "CURRENT" file that points to the new manifest file.
|
||||
s = SetCurrentFile(WriteOptions(), fs_.get(), dbname_, 1, nullptr);
|
||||
s = SetCurrentFile(fs_.get(), dbname_, 1, nullptr);
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
@@ -650,7 +644,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
mutable_cf_options_.max_compaction_bytes, 0, kNoCompression,
|
||||
cfd->GetLatestMutableCFOptions()->compression_opts,
|
||||
Temperature::kUnknown, max_subcompactions, grandparents, true);
|
||||
compaction.FinalizeInputInfo(cfd->current());
|
||||
compaction.SetInputVersion(cfd->current());
|
||||
|
||||
assert(db_options_.info_log);
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
|
||||
@@ -661,12 +655,11 @@ class CompactionJobTestBase : public testing::Test {
|
||||
ASSERT_TRUE(full_history_ts_low_.empty() ||
|
||||
ucmp_->timestamp_size() == full_history_ts_low_.size());
|
||||
const std::atomic<bool> kManualCompactionCanceledFalse{false};
|
||||
JobContext job_context(1, false /* create_superversion */);
|
||||
CompactionJob compaction_job(
|
||||
0, &compaction, db_options_, mutable_db_options_, env_options_,
|
||||
versions_.get(), &shutting_down_, &log_buffer, nullptr, nullptr,
|
||||
nullptr, nullptr, &mutex_, &error_handler_, snapshots,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, &job_context,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, nullptr,
|
||||
table_cache_, &event_logger, false, false, dbname_,
|
||||
&compaction_job_stats_, Env::Priority::USER, nullptr /* IOTracer */,
|
||||
/*manual_compaction_canceled=*/kManualCompactionCanceledFalse,
|
||||
@@ -680,9 +673,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_OK(compaction_job.io_status());
|
||||
mutex_.Lock();
|
||||
bool compaction_released = false;
|
||||
ASSERT_OK(compaction_job.Install(*cfd->GetLatestMutableCFOptions(),
|
||||
&compaction_released));
|
||||
ASSERT_OK(compaction_job.Install(*cfd->GetLatestMutableCFOptions()));
|
||||
ASSERT_OK(compaction_job.io_status());
|
||||
mutex_.Unlock();
|
||||
log_buffer.FlushBufferToLog();
|
||||
@@ -739,7 +730,6 @@ class CompactionJobTestBase : public testing::Test {
|
||||
MutableCFOptions mutable_cf_options_;
|
||||
MutableDBOptions mutable_db_options_;
|
||||
const ReadOptions read_options_;
|
||||
const WriteOptions write_options_;
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
WriteController write_controller_;
|
||||
WriteBufferManager write_buffer_manager_;
|
||||
@@ -1531,15 +1521,13 @@ TEST_F(CompactionJobTest, VerifyPenultimateLevelOutput) {
|
||||
{files0, files1, files2, files3}, input_levels,
|
||||
/*verify_func=*/[&](Compaction& comp) {
|
||||
for (char c = 'a'; c <= 'z'; c++) {
|
||||
std::string c_str;
|
||||
c_str = c;
|
||||
const Slice key(c_str);
|
||||
if (c == 'a') {
|
||||
ParsedInternalKey pik("a", 0U, kTypeValue);
|
||||
ASSERT_FALSE(comp.WithinPenultimateLevelOutputRange(pik));
|
||||
ASSERT_FALSE(comp.WithinPenultimateLevelOutputRange(key));
|
||||
} else {
|
||||
std::string c_str{c};
|
||||
// WithinPenultimateLevelOutputRange checks internal key range.
|
||||
// 'z' is the last key, so set seqno properly.
|
||||
ParsedInternalKey pik(c_str, c == 'z' ? 12U : 0U, kTypeValue);
|
||||
ASSERT_TRUE(comp.WithinPenultimateLevelOutputRange(pik));
|
||||
ASSERT_TRUE(comp.WithinPenultimateLevelOutputRange(key));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1752,9 +1740,23 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutForMaxCompactionBytes) {
|
||||
class CompactionJobDynamicFileSizeTest
|
||||
: public CompactionJobTestBase,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
public:
|
||||
CompactionJobDynamicFileSizeTest()
|
||||
: CompactionJobTestBase(
|
||||
test::PerThreadDBPath("compaction_job_dynamic_file_size_test"),
|
||||
BytewiseComparator(), [](uint64_t /*ts*/) { return ""; },
|
||||
/*test_io_priority=*/false, TableTypeForTest::kMockTable) {}
|
||||
};
|
||||
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutForMaxCompactionBytes) {
|
||||
// dynamic_file_size option should have no impact on cutting for max
|
||||
// compaction bytes.
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
|
||||
NewDB();
|
||||
mutable_cf_options_.target_file_size_base = 80;
|
||||
mutable_cf_options_.max_compaction_bytes = 21;
|
||||
@@ -1828,7 +1830,10 @@ TEST_F(CompactionJobTest, CutForMaxCompactionBytes) {
|
||||
{expected_file1, expected_file2});
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutToSkipGrandparentFile) {
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutToSkipGrandparentFile) {
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
|
||||
NewDB();
|
||||
// Make sure the grandparent level file size (10) qualifies skipping.
|
||||
// Currently, it has to be > 1/8 of target file size.
|
||||
@@ -1863,15 +1868,28 @@ TEST_F(CompactionJobTest, CutToSkipGrandparentFile) {
|
||||
mock::MakeMockFile({{KeyStr("x", 4U, kTypeValue), "val"},
|
||||
{KeyStr("z", 6U, kTypeValue), "val3"}});
|
||||
|
||||
auto expected_file_disable_dynamic_file_size =
|
||||
mock::MakeMockFile({{KeyStr("a", 5U, kTypeValue), "val2"},
|
||||
{KeyStr("c", 3U, kTypeValue), "val"},
|
||||
{KeyStr("x", 4U, kTypeValue), "val"},
|
||||
{KeyStr("z", 6U, kTypeValue), "val3"}});
|
||||
|
||||
SetLastSequence(6U);
|
||||
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);
|
||||
if (enable_dyanmic_file_size) {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2});
|
||||
} else {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file_disable_dynamic_file_size});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutToAlignGrandparentBoundary) {
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutToAlignGrandparentBoundary) {
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
NewDB();
|
||||
|
||||
// MockTable has 1 byte per entry by default and each file is 10 bytes.
|
||||
@@ -1938,15 +1956,40 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundary) {
|
||||
}
|
||||
expected_file2.emplace_back(KeyStr("s", 4U, kTypeValue), "val");
|
||||
|
||||
mock::KVVector expected_file_disable_dynamic_file_size1;
|
||||
for (char i = 0; i < 10; i++) {
|
||||
expected_file_disable_dynamic_file_size1.emplace_back(
|
||||
KeyStr(std::string(1, ch + i), i + 10, kTypeValue),
|
||||
"val" + std::to_string(i));
|
||||
}
|
||||
|
||||
mock::KVVector expected_file_disable_dynamic_file_size2;
|
||||
for (char i = 10; i < 12; i++) {
|
||||
expected_file_disable_dynamic_file_size2.emplace_back(
|
||||
KeyStr(std::string(1, ch + i), i + 10, kTypeValue),
|
||||
"val" + std::to_string(i));
|
||||
}
|
||||
|
||||
expected_file_disable_dynamic_file_size2.emplace_back(
|
||||
KeyStr("s", 4U, kTypeValue), "val");
|
||||
|
||||
SetLastSequence(22U);
|
||||
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);
|
||||
if (enable_dyanmic_file_size) {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2});
|
||||
} else {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file_disable_dynamic_file_size1,
|
||||
expected_file_disable_dynamic_file_size2});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
NewDB();
|
||||
|
||||
// MockTable has 1 byte per entry by default and each file is 10 bytes.
|
||||
@@ -1983,9 +2026,13 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
AddMockFile(file5, 2);
|
||||
|
||||
mock::KVVector expected_file1;
|
||||
mock::KVVector expected_file_disable_dynamic_file_size;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
expected_file1.emplace_back(KeyStr("a", 100 - i, kTypeValue),
|
||||
"val" + std::to_string(100 - i));
|
||||
expected_file_disable_dynamic_file_size.emplace_back(
|
||||
KeyStr("a", 100 - i, kTypeValue), "val" + std::to_string(100 - i));
|
||||
}
|
||||
|
||||
// make sure `b` is cut in a separated file (so internally it's not using
|
||||
@@ -1994,6 +2041,9 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
auto expected_file2 =
|
||||
mock::MakeMockFile({{KeyStr("b", 90U, kTypeValue), "valb"}});
|
||||
|
||||
expected_file_disable_dynamic_file_size.emplace_back(
|
||||
KeyStr("b", 90U, kTypeValue), "valb");
|
||||
|
||||
SetLastSequence(122U);
|
||||
const std::vector<int> input_levels = {0, 1};
|
||||
auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
@@ -2004,13 +2054,20 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
for (int i = 80; i <= 100; i++) {
|
||||
snapshots.emplace_back(i);
|
||||
}
|
||||
if (enable_dyanmic_file_size) {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2}, snapshots);
|
||||
} else {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file_disable_dynamic_file_size}, snapshots);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutForMaxCompactionBytesSameKey) {
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutForMaxCompactionBytesSameKey) {
|
||||
// dynamic_file_size option should have no impact on cutting for max
|
||||
// compaction bytes.
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
|
||||
NewDB();
|
||||
mutable_cf_options_.target_file_size_base = 80;
|
||||
@@ -2067,6 +2124,9 @@ TEST_F(CompactionJobTest, CutForMaxCompactionBytesSameKey) {
|
||||
{expected_file1, expected_file2, expected_file3}, snapshots);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CompactionJobDynamicFileSizeTest,
|
||||
CompactionJobDynamicFileSizeTest, testing::Bool());
|
||||
|
||||
class CompactionJobTimestampTest : public CompactionJobTestBase {
|
||||
public:
|
||||
CompactionJobTimestampTest()
|
||||
|
||||
@@ -18,18 +18,16 @@ void CompactionOutputs::NewBuilder(const TableBuilderOptions& tboptions) {
|
||||
builder_.reset(NewTableBuilder(tboptions, file_writer_.get()));
|
||||
}
|
||||
|
||||
Status CompactionOutputs::Finish(
|
||||
const Status& intput_status,
|
||||
const SeqnoToTimeMapping& seqno_to_time_mapping) {
|
||||
Status CompactionOutputs::Finish(const Status& intput_status,
|
||||
const SeqnoToTimeMapping& seqno_time_mapping) {
|
||||
FileMetaData* meta = GetMetaData();
|
||||
assert(meta != nullptr);
|
||||
Status s = intput_status;
|
||||
if (s.ok()) {
|
||||
SeqnoToTimeMapping relevant_mapping;
|
||||
relevant_mapping.CopyFromSeqnoRange(
|
||||
seqno_to_time_mapping, meta->fd.smallest_seqno, meta->fd.largest_seqno);
|
||||
relevant_mapping.SetCapacity(kMaxSeqnoTimePairsPerSST);
|
||||
builder_->SetSeqnoTimeTableProperties(relevant_mapping,
|
||||
std::string seqno_time_mapping_str;
|
||||
seqno_time_mapping.Encode(seqno_time_mapping_str, meta->fd.smallest_seqno,
|
||||
meta->fd.largest_seqno, meta->file_creation_time);
|
||||
builder_->SetSeqnoTimeTableProperties(seqno_time_mapping_str,
|
||||
meta->oldest_ancester_time);
|
||||
s = builder_->Finish();
|
||||
|
||||
@@ -62,15 +60,12 @@ IOStatus CompactionOutputs::WriterSyncClose(const Status& input_status,
|
||||
Statistics* statistics,
|
||||
bool use_fsync) {
|
||||
IOStatus io_s;
|
||||
IOOptions opts;
|
||||
io_s = WritableFileWriter::PrepareIOOptions(
|
||||
WriteOptions(Env::IOActivity::kCompaction), opts);
|
||||
if (input_status.ok() && io_s.ok()) {
|
||||
if (input_status.ok()) {
|
||||
StopWatch sw(clock, statistics, COMPACTION_OUTFILE_SYNC_MICROS);
|
||||
io_s = file_writer_->Sync(opts, use_fsync);
|
||||
io_s = file_writer_->Sync(use_fsync);
|
||||
}
|
||||
if (input_status.ok() && io_s.ok()) {
|
||||
io_s = file_writer_->Close(opts);
|
||||
io_s = file_writer_->Close();
|
||||
}
|
||||
|
||||
if (input_status.ok() && io_s.ok()) {
|
||||
@@ -132,6 +127,11 @@ size_t CompactionOutputs::UpdateGrandparentBoundaryInfo(
|
||||
if (grandparents.empty()) {
|
||||
return curr_key_boundary_switched_num;
|
||||
}
|
||||
assert(!internal_key.empty());
|
||||
InternalKey ikey;
|
||||
ikey.DecodeFrom(internal_key);
|
||||
assert(ikey.Valid());
|
||||
|
||||
const Comparator* ucmp = compaction_->column_family_data()->user_comparator();
|
||||
|
||||
// Move the grandparent_index_ to the file containing the current user_key.
|
||||
@@ -139,7 +139,7 @@ size_t CompactionOutputs::UpdateGrandparentBoundaryInfo(
|
||||
// index points to the last file containing the key.
|
||||
while (grandparent_index_ < grandparents.size()) {
|
||||
if (being_grandparent_gap_) {
|
||||
if (sstableKeyCompare(ucmp, internal_key,
|
||||
if (sstableKeyCompare(ucmp, ikey,
|
||||
grandparents[grandparent_index_]->smallest) < 0) {
|
||||
break;
|
||||
}
|
||||
@@ -152,13 +152,13 @@ size_t CompactionOutputs::UpdateGrandparentBoundaryInfo(
|
||||
being_grandparent_gap_ = false;
|
||||
} else {
|
||||
int cmp_result = sstableKeyCompare(
|
||||
ucmp, internal_key, grandparents[grandparent_index_]->largest);
|
||||
ucmp, ikey, grandparents[grandparent_index_]->largest);
|
||||
// If it's same key, make sure grandparent_index_ is pointing to the last
|
||||
// one.
|
||||
if (cmp_result < 0 ||
|
||||
(cmp_result == 0 &&
|
||||
(grandparent_index_ == grandparents.size() - 1 ||
|
||||
sstableKeyCompare(ucmp, internal_key,
|
||||
sstableKeyCompare(ucmp, ikey,
|
||||
grandparents[grandparent_index_ + 1]->smallest) <
|
||||
0))) {
|
||||
break;
|
||||
@@ -320,6 +320,7 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
|
||||
being_grandparent_gap_ ? 2 : 3;
|
||||
if (compaction_->immutable_options()->compaction_style ==
|
||||
kCompactionStyleLevel &&
|
||||
compaction_->immutable_options()->level_compaction_dynamic_file_size &&
|
||||
num_grandparent_boundaries_crossed >=
|
||||
num_skippable_boundaries_crossed &&
|
||||
grandparent_overlapped_bytes_ - previous_overlapped_bytes >
|
||||
@@ -341,6 +342,7 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
|
||||
// improvement.
|
||||
if (compaction_->immutable_options()->compaction_style ==
|
||||
kCompactionStyleLevel &&
|
||||
compaction_->immutable_options()->level_compaction_dynamic_file_size &&
|
||||
current_output_file_size_ >=
|
||||
((compaction_->target_output_file_size() + 99) / 100) *
|
||||
(50 + std::min(grandparent_boundary_switched_num_ * 5,
|
||||
|
||||
@@ -30,9 +30,11 @@ class CompactionOutputs {
|
||||
// compaction output file
|
||||
struct Output {
|
||||
Output(FileMetaData&& _meta, const InternalKeyComparator& _icmp,
|
||||
bool _enable_hash, bool _finished, uint64_t precalculated_hash)
|
||||
bool _enable_order_check, bool _enable_hash, bool _finished,
|
||||
uint64_t precalculated_hash)
|
||||
: meta(std::move(_meta)),
|
||||
validator(_icmp, _enable_hash, precalculated_hash),
|
||||
validator(_icmp, _enable_order_check, _enable_hash,
|
||||
precalculated_hash),
|
||||
finished(_finished) {}
|
||||
FileMetaData meta;
|
||||
OutputValidator validator;
|
||||
@@ -47,10 +49,10 @@ class CompactionOutputs {
|
||||
|
||||
// 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);
|
||||
bool enable_order_check, bool enable_hash,
|
||||
bool finished = false, uint64_t precalculated_hash = 0) {
|
||||
outputs_.emplace_back(std::move(meta), icmp, enable_order_check,
|
||||
enable_hash, finished, precalculated_hash);
|
||||
}
|
||||
|
||||
// Set new table builder for the current output
|
||||
@@ -105,7 +107,7 @@ class CompactionOutputs {
|
||||
|
||||
// Finish the current output file
|
||||
Status Finish(const Status& intput_status,
|
||||
const SeqnoToTimeMapping& seqno_to_time_mapping);
|
||||
const SeqnoToTimeMapping& seqno_time_mapping);
|
||||
|
||||
// Update output table properties from table builder
|
||||
void UpdateTableProperties() {
|
||||
@@ -204,10 +206,10 @@ class CompactionOutputs {
|
||||
// We may only split the output when the cursor is in the range. Split
|
||||
if ((!end.has_value() ||
|
||||
icmp->user_comparator()->Compare(
|
||||
ExtractUserKey(output_split_key->Encode()), *end) < 0) &&
|
||||
(!start.has_value() ||
|
||||
icmp->user_comparator()->Compare(
|
||||
ExtractUserKey(output_split_key->Encode()), *start) > 0)) {
|
||||
ExtractUserKey(output_split_key->Encode()), end.value()) < 0) &&
|
||||
(!start.has_value() || icmp->user_comparator()->Compare(
|
||||
ExtractUserKey(output_split_key->Encode()),
|
||||
start.value()) > 0)) {
|
||||
local_output_split_key_ = output_split_key;
|
||||
}
|
||||
}
|
||||
|
||||
Loaded 100 of 925 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user