Compare commits

..
12 Commits
Author SHA1 Message Date
anand76 01393464ea Update HISTORY for 9.3.2 2024-08-05 10:40:16 -07:00
anand76 97a152b841 Make transaction name conflict check more robust (#12895)
Summary:
The `PessimisticTransaction::SetName()` code checks for an existing txn of the given name before registering the new txn. However, this is not atomic, which could result in a race condition if two txns try to register with the same name. Both might succeed and lead to unpredictable behavior. This PR makes the test and set atomic.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12895

Reviewed By: pdillinger

Differential Revision: D60460482

Pulled By: anand1976

fbshipit-source-id: e8afeb2356e1b8f4e8df785cb73532739f82579d
2024-08-05 10:37:59 -07:00
Yu Zhang 537ccd2fbe update HISTORY.md and version.h for 9.3.2 2024-08-05 09:25:52 -07:00
Yu Zhang 86e421fba9 Fix file deletions in DestroyDB not rate limited (#12891)
Summary:
Make `DestroyDB` slowly delete files if it's configured and enabled via `SstFileManager`.

It's currently not available mainly because of DeleteScheduler's logic related to tracked total_size_ and total_trash_size_. These accounting and logic should not be applied to `DestroyDB`. This PR adds a `DeleteUnaccountedDBFile` util for this purpose which deletes files without accounting it.  This util also supports assigning a file to a specified trash bucket so that user can later wait for a specific trash bucket to be empty. For `DestroyDB`, files with more than 1 hard links will be deleted immediately.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12891

Test Plan: Added unit tests, existing tests.

Reviewed By: anand1976

Differential Revision: D60300220

Pulled By: jowlyzhang

fbshipit-source-id: 8b18109a177a3a9532f6dc2e40e08310c08ca3c7
2024-08-05 09:24:20 -07:00
Andrew Kryczka c5201abc4a update HISTORY.md and version.h for 9.3.1 2024-06-04 11:43:02 -07:00
Fan Zhang(DevX) 4eda1b79fa add export_file to rockdb TARGETS generator and re-gen
Summary:
we are converting the implicit loads to explicit loads, then remove the hidden loads in fbcode macroes.
details see https://fb.workplace.com/groups/devx.build.bffs/permalink/7481848805183560/

Reviewed By: JakobDegen

Differential Revision: D57800976

fbshipit-source-id: a893aa2aa9237704ba9eb998cba210222c95dd2f
2024-06-04 11:42:26 -07:00
Andrew Kryczka 030f7c4a7b include bug fixes in 9.3.0 from after the branch cut until now 2024-06-02 16:52:07 -07:00
anand76 6c60c8f3f3 Fix stale memory access with FSBuffer and tiered sec cache (#12712)
Summary:
A `BlockBasedTable` with `TieredSecondaryCache` containing a NVM cache inserts blocks  into the compressed cache and the corresponding compressed block into the NVM cache.  The `BlockFetcher` is used to get the uncompressed and compressed blocks by calling `ReadBlockContents()` and `GetUncompressedBlock()` respectively. If the file system supports FSBuffer (i.e returning a FS allocated buffer rather than caller provided), that buffer gets freed between the two calls. This PR fixes it by making the FSBuffer unique pointer a member rather than local variable.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12712

Test Plan:
1. Add a unit test
2. Release validation stress test

Reviewed By: jaykorean

Differential Revision: D57974026

Pulled By: anand1976

fbshipit-source-id: cfa895914e74b4f628413b40e6e39d8d8e5286bd
2024-06-02 16:50:41 -07:00
Andrew Kryczka 2660754dbc Fix recycled WAL detection when wal_compression is enabled (#12643)
Summary:
I think the point of the `if (end_of_buffer_offset_ - buffer_.size() == 0)` was to only set `recycled_` when the first record was read. However, the condition was false when reading the first record when the WAL began with a  `kSetCompressionType` record because we had already dropped the `kSetCompressionType` record from `buffer_`. To fix this, I used `first_record_read_` instead.

Also, it was pretty confusing to treat the WAL as non-recycled when a recyclable record first appeared in a non-first record. I changed it to return an error if that happens.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12643

Reviewed By: hx235

Differential Revision: D57238099

Pulled By: ajkr

fbshipit-source-id: e20a2a0c9cf0c9510a7b6af463650a05d559239e
2024-06-02 16:50:29 -07:00
Peter Dillinger 94a867c845 Disallow memtable flush and sst ingest while WAL is locked (#12652)
Summary:
We recently noticed that some memtable flushed and file
ingestions could proceed during LockWAL, in violation of its stated
contract. (Note: we aren't 100% sure its actually needed by MySQL, but
we want it to be in a clean state nonetheless.)

Despite earlier skepticism that this could be done safely (https://github.com/facebook/rocksdb/issues/12666), I
found a place to wait to wait for LockWAL to be cleared before allowing
these operations to proceed: WaitForPendingWrites()

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12652

Test Plan:
Added to unit tests. Extended how db_stress validates LockWAL
and re-enabled combination of ingestion and LockWAL in crash test, in
follow-up to https://github.com/facebook/rocksdb/issues/12642

Ran blackbox_crash_test for a long while with relevant features
amplified.

Suggested follow-up: fix FaultInjectionTestFS to report file sizes
consistent with what the user has requested to be flushed.

Reviewed By: jowlyzhang

Differential Revision: D57622142

Pulled By: pdillinger

fbshipit-source-id: aef265fce69465618974b4ec47f4636257c676ce
2024-06-02 16:50:15 -07:00
Hui Xiao 1b5474c8e1 Fix unreleased bug fix .md name (#12672)
Summary:
Context/Summary: as above

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12672

Test Plan: no code change

Reviewed By: ajkr

Differential Revision: D57505136

Pulled By: hx235

fbshipit-source-id: 0e216dc5974e9be10027b444eb6b4034f679dfd8
2024-06-02 16:49:53 -07:00
Andrew Kryczka b135fa7a8b 9.3.0 release notes 2024-06-02 16:21:22 -07:00
807 changed files with 25858 additions and 94817 deletions

No files matched your search

+905
View File
@@ -0,0 +1,905 @@
version: 2.1
orbs:
win: circleci/windows@5.0.0
commands:
install-cmake-on-macos:
steps:
- run:
name: Install cmake on macos
command: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
install-jdk8-on-macos:
steps:
- run:
name: Install JDK 8 on macos
command: |
HOMEBREW_NO_AUTO_UPDATE=1 brew tap bell-sw/liberica
HOMEBREW_NO_AUTO_UPDATE=1 brew install --cask liberica-jdk8
increase-max-open-files-on-macos:
steps:
- run:
name: Increase max open files
command: |
sudo sysctl -w kern.maxfiles=1048576
sudo sysctl -w kern.maxfilesperproc=1048576
sudo launchctl limit maxfiles 1048576
pre-steps:
steps:
- checkout
- run:
name: Setup Environment Variables
command: |
echo "export GTEST_THROW_ON_FAILURE=0" >> $BASH_ENV
echo "export GTEST_OUTPUT=\"xml:/tmp/test-results/\"" >> $BASH_ENV
echo "export SKIP_FORMAT_BUCK_CHECKS=1" >> $BASH_ENV
echo "export GTEST_COLOR=1" >> $BASH_ENV
echo "export CTEST_OUTPUT_ON_FAILURE=1" >> $BASH_ENV
echo "export CTEST_TEST_TIMEOUT=300" >> $BASH_ENV
echo "export ZLIB_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zlib" >> $BASH_ENV
echo "export BZIP2_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/bzip2" >> $BASH_ENV
echo "export SNAPPY_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/snappy" >> $BASH_ENV
echo "export LZ4_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/lz4" >> $BASH_ENV
echo "export ZSTD_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zstd" >> $BASH_ENV
windows-build-steps:
steps:
- checkout
- run:
name: "Install thirdparty dependencies"
command: |
echo "Installing CMake..."
choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' -y
choco install liberica8jdk -y
mkdir $Env:THIRDPARTY_HOME
cd $Env:THIRDPARTY_HOME
echo "Building Snappy dependency..."
curl https://github.com/google/snappy/archive/refs/tags/1.1.8.zip -O snappy-1.1.8.zip
unzip -q snappy-1.1.8.zip
cd snappy-1.1.8
mkdir build
cd build
& $Env:CMAKE_BIN -G "$Env:CMAKE_GENERATOR" ..
msbuild.exe Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
- run:
name: "Build RocksDB"
command: |
$env:Path = $env:JAVA_HOME + ";" + $env:Path
mkdir build
cd build
& $Env:CMAKE_BIN -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DJNI=1 ..
cd ..
echo "Building with VS version: $Env:CMAKE_GENERATOR"
msbuild.exe build/rocksdb.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
- run:
name: "Test RocksDB"
shell: powershell.exe
command: |
build_tools\run_ci_db_test.ps1 -SuiteRun arena_test,db_basic_test,db_test,db_test2,db_merge_operand_test,bloom_test,c_test,coding_test,crc32c_test,dynamic_bloom_test,env_basic_test,env_test,hash_test,random_test -Concurrency 16
- run:
name: "Test RocksJava"
command: |
cd build\java
& $Env:CTEST_BIN -C Debug -j 16
pre-steps-macos:
steps:
- pre-steps
post-steps:
steps:
- store_test_results: # store test result if there's any
path: /tmp/test-results
- store_artifacts: # store LOG for debugging if there's any
path: LOG
- run: # on fail, compress Test Logs for diagnosing the issue
name: Compress Test Logs
command: tar -cvzf t.tar.gz t
when: on_fail
- store_artifacts: # on fail, store Test Logs for diagnosing the issue
path: t.tar.gz
destination: test_logs
when: on_fail
- run: # store core dumps if there's any
command: |
mkdir -p /tmp/core_dumps
cp core.* /tmp/core_dumps
when: on_fail
- store_artifacts:
path: /tmp/core_dumps
when: on_fail
post-pmd-steps:
steps:
- store_artifacts:
path: /home/circleci/project/java/target/pmd.xml
when: on_fail
- store_artifacts:
path: /home/circleci/project/java/target/site
when: on_fail
upgrade-cmake:
steps:
- run:
name: Upgrade cmake
command: |
sudo apt remove --purge cmake
sudo snap install cmake --classic
install-gflags:
steps:
- run:
name: Install gflags
command: |
sudo apt-get update -y && sudo apt-get install -y libgflags-dev
install-gflags-on-macos:
steps:
- run:
name: Install gflags on macos
command: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install gflags
install-maven:
steps:
- run:
name: Install Maven
command: |
wget --no-check-certificate https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz
tar zxf apache-maven-3.9.6-bin.tar.gz
echo "export M2_HOME=$(pwd)/apache-maven-3.9.6" >> $BASH_ENV
echo 'export PATH=$M2_HOME/bin:$PATH' >> $BASH_ENV
setup-folly:
steps:
- run:
name: Checkout folly sources
command: |
make checkout_folly
build-folly:
steps:
- run:
name: Build folly and dependencies
command: |
make build_folly
build-for-benchmarks:
steps:
- pre-steps
- run:
name: "Linux build for benchmarks"
command: #sized for the resource-class rocksdb-benchmark-sys1
make V=1 J=8 -j8 release
perform-benchmarks:
steps:
- run:
name: "Test low-variance benchmarks"
command: ./tools/benchmark_ci.py --db_dir /tmp/rocksdb-benchmark-datadir --output_dir /tmp/benchmark-results --num_keys 20000000
environment:
LD_LIBRARY_PATH: /usr/local/lib
# How long to run parts of the test(s)
DURATION_RO: 300
DURATION_RW: 500
# Keep threads within physical capacity of server (much lower than default)
NUM_THREADS: 1
MAX_BACKGROUND_JOBS: 4
# Don't run a couple of "optional" initial tests
CI_TESTS_ONLY: "true"
# Reduce configured size of levels to ensure more levels in the leveled compaction LSM tree
WRITE_BUFFER_SIZE_MB: 16
TARGET_FILE_SIZE_BASE_MB: 16
MAX_BYTES_FOR_LEVEL_BASE_MB: 64
# The benchmark host has 32GB memory
# The following values are tailored to work with that
# Note, tests may not exercise the targeted issues if the memory is increased on new test hosts.
COMPRESSION_TYPE: "none"
CACHE_INDEX_AND_FILTER_BLOCKS: 1
MIN_LEVEL_TO_COMPRESS: 3
CACHE_SIZE_MB: 10240
MB_WRITE_PER_SEC: 2
post-benchmarks:
steps:
- store_artifacts: # store the benchmark output
path: /tmp/benchmark-results
destination: test_logs
- run:
name: Send benchmark report to visualisation
command: |
set +e
set +o pipefail
./build_tools/benchmark_log_tool.py --tsvfile /tmp/benchmark-results/report.tsv --esdocument https://search-rocksdb-bench-k2izhptfeap2hjfxteolsgsynm.us-west-2.es.amazonaws.com/bench_test3_rix/_doc
true
executors:
linux-docker:
docker:
# The image configuration is build_tools/ubuntu20_image/Dockerfile
# To update and build the image:
# $ cd build_tools/ubuntu20_image
# $ docker build -t zjay437/rocksdb:0.5 .
# $ docker push zjay437/rocksdb:0.5
# `zjay437` is the account name for zjay@meta.com which readwrite token is shared internally. To login:
# $ docker login --username zjay437
# Or please feel free to change it to your docker hub account for hosting the image, meta employee should already have the account and able to login with SSO.
# To avoid impacting the existing CI runs, please bump the version every time creating a new image
# to run the CI image environment locally:
# $ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -it zjay437/rocksdb:0.5 bash
# option `--cap-add=SYS_PTRACE --security-opt seccomp=unconfined` is used to enable gdb to attach an existing process
- image: zjay437/rocksdb:0.6
linux-java-docker:
docker:
# This is the Docker Image used for building RocksJava releases, see: https://github.com/evolvedbinary/docker-rocksjava
- image: evolvedbinary/rocksjava:centos6_x64-be
jobs:
build-macos:
macos:
xcode: 14.3.1
resource_class: macos.m1.medium.gen1
environment:
ROCKSDB_DISABLE_JEMALLOC: 1 # jemalloc cause env_test hang, disable it for now
steps:
- increase-max-open-files-on-macos
- install-gflags-on-macos
- pre-steps-macos
- run: ulimit -S -n `ulimit -H -n` && make V=1 J=16 -j16 all
- post-steps
build-macos-cmake:
macos:
xcode: 14.3.1
resource_class: macos.m1.medium.gen1
parameters:
run_even_tests:
description: run even or odd tests, used to split tests to 2 groups
type: boolean
default: true
steps:
- increase-max-open-files-on-macos
- install-cmake-on-macos
- install-gflags-on-macos
- pre-steps-macos
- run:
name: "cmake generate project file"
command: ulimit -S -n `ulimit -H -n` && mkdir build && cd build && cmake -DWITH_GFLAGS=1 ..
- run:
name: "Build tests"
command: cd build && make V=1 -j16
- when:
condition: << parameters.run_even_tests >>
steps:
- run:
name: "Run even tests"
command: ulimit -S -n `ulimit -H -n` && cd build && ctest -j16 -I 0,,2
- when:
condition:
not: << parameters.run_even_tests >>
steps:
- run:
name: "Run odd tests"
command: ulimit -S -n `ulimit -H -n` && cd build && ctest -j16 -I 1,,2
- post-steps
build-linux:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: make V=1 J=32 -j32 check
- post-steps
build-linux-encrypted_env-no_compression:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: ENCRYPTED_ENV=1 ROCKSDB_DISABLE_SNAPPY=1 ROCKSDB_DISABLE_ZLIB=1 ROCKSDB_DISABLE_BZIP=1 ROCKSDB_DISABLE_LZ4=1 ROCKSDB_DISABLE_ZSTD=1 make V=1 J=32 -j32 check
- run: |
./sst_dump --help | grep -E -q 'Supported compression types: kNoCompression$' # Verify no compiled in compression
- post-steps
build-linux-static_lib-alt_namespace-status_checked:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: ASSERT_STATUS_CHECKED=1 TEST_UINT128_COMPAT=1 ROCKSDB_MODIFY_NPHASH=1 LIB_MODE=static OPT="-DROCKSDB_NAMESPACE=alternative_rocksdb_ns" make V=1 -j24 check
- post-steps
build-linux-release:
executor: linux-docker
resource_class: 2xlarge
steps:
- checkout # check out the code in the project directory
- run: make V=1 -j32 LIB_MODE=shared release
- run: ls librocksdb.so # ensure shared lib built
- run: ./db_stress --version # ensure with gflags
- run: make clean
- run: make V=1 -j32 release
- run: ls librocksdb.a # ensure static lib built
- run: ./db_stress --version # ensure with gflags
- run: make clean
- run: apt-get remove -y libgflags-dev
- run: make V=1 -j32 LIB_MODE=shared release
- run: ls librocksdb.so # ensure shared lib built
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
- run: make clean
- run: make V=1 -j32 release
- run: ls librocksdb.a # ensure static lib built
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
- post-steps
build-linux-release-rtti:
executor: linux-docker
resource_class: xlarge
steps:
- checkout # check out the code in the project directory
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j16 static_lib tools db_bench
- run: ./db_stress --version # ensure with gflags
- run: make clean
- run: apt-get remove -y libgflags-dev
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j16 static_lib tools db_bench
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
build-linux-clang-no_test_run:
executor: linux-docker
resource_class: xlarge
steps:
- checkout # check out the code in the project directory
- run: CC=clang CXX=clang++ USE_CLANG=1 PORTABLE=1 make V=1 -j16 all
- post-steps
build-linux-clang10-asan:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check # aligned new doesn't work for reason we haven't figured out
- post-steps
build-linux-clang10-mini-tsan:
executor: linux-docker
resource_class: 2xlarge+
steps:
- pre-steps
- run: COMPILE_WITH_TSAN=1 CC=clang-13 CXX=clang++-13 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check
- post-steps
build-linux-clang10-ubsan:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: COMPILE_WITH_UBSAN=1 OPT="-fsanitize-blacklist=.circleci/ubsan_suppression_list.txt" CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 ubsan_check # aligned new doesn't work for reason we haven't figured out
- post-steps
build-linux-valgrind:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: PORTABLE=1 make V=1 -j32 valgrind_test
- post-steps
build-linux-clang10-clang-analyze:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 CLANG_ANALYZER="/usr/bin/clang++-10" CLANG_SCAN_BUILD=scan-build-10 USE_CLANG=1 make V=1 -j32 analyze # aligned new doesn't work for reason we haven't figured out. For unknown, reason passing "clang++-10" as CLANG_ANALYZER doesn't work, and we need a full path.
- post-steps
- run:
name: "compress test report"
command: tar -cvzf scan_build_report.tar.gz scan_build_report
when: on_fail
- store_artifacts:
path: scan_build_report.tar.gz
destination: scan_build_report
when: on_fail
build-linux-runner:
machine: true
resource_class: facebook/rocksdb-benchmark-sys1
steps:
- pre-steps
- run:
name: "Checked Linux build (Runner)"
command: make V=1 J=8 -j8 check
environment:
LD_LIBRARY_PATH: /usr/local/lib
- post-steps
build-linux-cmake-with-folly:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- setup-folly
- build-folly
- run: (mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make V=1 -j20 && ctest -j20)
- post-steps
build-linux-cmake-with-folly-lite-no-test:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- setup-folly
- run: (mkdir build && cd build && cmake -DUSE_FOLLY_LITE=1 -DWITH_GFLAGS=1 .. && make V=1 -j20)
- post-steps
build-linux-cmake-with-benchmark:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: mkdir build && cd build && cmake -DWITH_GFLAGS=1 -DWITH_BENCHMARK=1 .. && make V=1 -j20 && ctest -j20
- post-steps
build-linux-unity-and-headers:
docker: # executor type
- image: gcc:latest
environment:
EXTRA_CXXFLAGS: -mno-avx512f # Warnings-as-error in avx512fintrin.h, would be used on newer hardware
resource_class: large
steps:
- checkout # check out the code in the project directory
- run: apt-get update -y && apt-get install -y libgflags-dev
- run:
name: "Unity build"
command: make V=1 -j8 unity_test
no_output_timeout: 20m
- run: make V=1 -j8 -k check-headers # could be moved to a different build
- post-steps
build-linux-gcc-7-with-folly:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- setup-folly
- build-folly
- run: USE_FOLLY=1 LIB_MODE=static CC=gcc-7 CXX=g++-7 V=1 make -j32 check # TODO: LIB_MODE only to work around unresolved linker failures
- post-steps
build-linux-gcc-7-with-folly-lite-no-test:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- setup-folly
- run: USE_FOLLY_LITE=1 CC=gcc-7 CXX=g++-7 V=1 make -j32 all
- post-steps
build-linux-gcc-8-no_test_run:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: CC=gcc-8 CXX=g++-8 V=1 make -j32 all
- post-steps
build-linux-cmake-with-folly-coroutines:
executor: linux-docker
resource_class: 2xlarge
environment:
CC: gcc-10
CXX: g++-10
steps:
- pre-steps
- setup-folly
- build-folly
- run: (mkdir build && cd build && cmake -DUSE_COROUTINES=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make V=1 -j20 && ctest -j20)
- post-steps
build-linux-gcc-10-cxx20-no_test_run:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: CC=gcc-10 CXX=g++-10 V=1 ROCKSDB_CXX_STANDARD=c++20 make -j32 all
- post-steps
build-linux-gcc-11-no_test_run:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: LIB_MODE=static CC=gcc-11 CXX=g++-11 V=1 make -j32 all microbench # TODO: LIB_MODE only to work around unresolved linker failures
- post-steps
build-linux-clang-13-no_test_run:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j32 all microbench
- post-steps
# Ensure ASAN+UBSAN with folly, and full testsuite with clang 13
build-linux-clang-13-asan-ubsan-with-folly:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- setup-folly
- build-folly
- run: CC=clang-13 CXX=clang++-13 LIB_MODE=static USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check # TODO: LIB_MODE only to work around unresolved linker failures
- post-steps
# This job is only to make sure the microbench tests are able to run, the benchmark result is not meaningful as the CI host is changing.
build-linux-run-microbench:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: DEBUG_LEVEL=0 make -j32 run_microbench
- post-steps
build-linux-mini-crashtest:
executor: linux-docker
resource_class: large
steps:
- pre-steps
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000' blackbox_crash_test_with_atomic_flush
- post-steps
build-linux-crashtest-tiered-storage-bb:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run:
name: "run crashtest"
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800' blackbox_crash_test_with_tiered_storage
no_output_timeout: 100m
- post-steps
build-linux-crashtest-tiered-storage-wb:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run:
name: "run crashtest"
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800' whitebox_crash_test_with_tiered_storage
no_output_timeout: 100m
- post-steps
build-windows-vs2022-avx2:
executor:
name: win/server-2022
size: 2xlarge
environment:
THIRDPARTY_HOME: C:/Users/circleci/thirdparty
CMAKE_HOME: C:/Program Files/CMake
CMAKE_BIN: C:/Program Files/CMake/bin/cmake.exe
CTEST_BIN: C:/Program Files/CMake/bin/ctest.exe
JAVA_HOME: C:/Program Files/BellSoft/LibericaJDK-8
SNAPPY_HOME: C:/Users/circleci/thirdparty/snappy-1.1.8
SNAPPY_INCLUDE: C:/Users/circleci/thirdparty/snappy-1.1.8;C:/Users/circleci/thirdparty/snappy-1.1.8/build
SNAPPY_LIB_DEBUG: C:/Users/circleci/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
CMAKE_GENERATOR: Visual Studio 17 2022
CMAKE_PORTABLE: AVX2
steps:
- windows-build-steps
build-windows-vs2022:
executor:
name: win/server-2022
size: 2xlarge
environment:
THIRDPARTY_HOME: C:/Users/circleci/thirdparty
CMAKE_HOME: C:/Program Files/CMake
CMAKE_BIN: C:/Program Files/CMake/bin/cmake.exe
CTEST_BIN: C:/Program Files/CMake/bin/ctest.exe
JAVA_HOME: C:/Program Files/BellSoft/LibericaJDK-8
SNAPPY_HOME: C:/Users/circleci/thirdparty/snappy-1.1.8
SNAPPY_INCLUDE: C:/Users/circleci/thirdparty/snappy-1.1.8;C:/Users/circleci/thirdparty/snappy-1.1.8/build
SNAPPY_LIB_DEBUG: C:/Users/circleci/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
CMAKE_GENERATOR: Visual Studio 17 2022
CMAKE_PORTABLE: 1
steps:
- windows-build-steps
build-windows-vs2019:
executor:
name: win/server-2019
size: 2xlarge
environment:
THIRDPARTY_HOME: C:/Users/circleci/thirdparty
CMAKE_HOME: C:/Program Files/CMake
CMAKE_BIN: C:/Program Files/CMake/bin/cmake.exe
CTEST_BIN: C:/Program Files/CMake/bin/ctest.exe
JAVA_HOME: C:/Program Files/BellSoft/LibericaJDK-8
SNAPPY_HOME: C:/Users/circleci/thirdparty/snappy-1.1.8
SNAPPY_INCLUDE: C:/Users/circleci/thirdparty/snappy-1.1.8;C:/Users/circleci/thirdparty/snappy-1.1.8/build
SNAPPY_LIB_DEBUG: C:/Users/circleci/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
CMAKE_GENERATOR: Visual Studio 16 2019
CMAKE_PORTABLE: 1
steps:
- windows-build-steps
build-linux-java:
executor: linux-java-docker
resource_class: large
steps:
- pre-steps
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- run:
name: "Test RocksDBJava"
command: scl enable devtoolset-7 'make V=1 J=8 -j8 jtest'
- post-steps
build-linux-java-pmd:
executor: linux-java-docker
resource_class: large
steps:
- pre-steps
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- install-maven
- run:
name: "PMD RocksDBJava"
command: scl enable devtoolset-7 'make V=1 J=8 -j8 jpmd'
- post-pmd-steps
build-linux-java-static:
executor: linux-java-docker
resource_class: large
steps:
- pre-steps
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- run:
name: "Build RocksDBJava Static Library"
command: scl enable devtoolset-7 'make V=1 J=8 -j8 rocksdbjavastatic'
- post-steps
build-macos-java:
macos:
xcode: 14.3.1
resource_class: macos.m1.medium.gen1
environment:
JAVA_HOME: /Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home
ROCKSDB_DISABLE_JEMALLOC: 1 # jemalloc causes java 8 crash
steps:
- increase-max-open-files-on-macos
- install-gflags-on-macos
- install-jdk8-on-macos
- pre-steps-macos
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- run:
name: "Test RocksDBJava"
command: make V=1 J=16 -j16 jtest
no_output_timeout: 20m
- post-steps
build-macos-java-static:
macos:
xcode: 14.3.1
resource_class: macos.m1.medium.gen1
environment:
JAVA_HOME: /Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home
steps:
- increase-max-open-files-on-macos
- install-gflags-on-macos
- install-cmake-on-macos
- install-jdk8-on-macos
- pre-steps-macos
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- run:
name: "Build RocksDBJava x86 and ARM Static Libraries"
command: make V=1 J=16 -j16 rocksdbjavastaticosx
no_output_timeout: 20m
- post-steps
build-macos-java-static-universal:
macos:
xcode: 14.3.1
resource_class: macos.m1.medium.gen1
environment:
JAVA_HOME: /Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home
steps:
- increase-max-open-files-on-macos
- install-gflags-on-macos
- install-cmake-on-macos
- install-jdk8-on-macos
- pre-steps-macos
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- run:
name: "Build RocksDBJava Universal Binary Static Library"
command: make V=1 J=16 -j16 rocksdbjavastaticosx_ub
no_output_timeout: 20m
- post-steps
build-examples:
executor: linux-docker
resource_class: large
steps:
- pre-steps
- run:
name: "Build examples"
command: |
make V=1 -j4 static_lib && cd examples && make V=1 -j4
- post-steps
build-cmake-mingw:
executor: linux-docker
resource_class: large
steps:
- pre-steps
- run: update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
- run:
name: "Build cmake-mingw"
command: |
export PATH=$JAVA_HOME/bin:$PATH
echo "JAVA_HOME=${JAVA_HOME}"
which java && java -version
which javac && javac -version
mkdir build && cd build && cmake -DJNI=1 -DWITH_GFLAGS=OFF .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb rocksdbjni
- post-steps
build-linux-non-shm:
executor: linux-docker
resource_class: 2xlarge
environment:
TEST_TMPDIR: /tmp/rocksdb_test_tmp
steps:
- pre-steps
- run: make V=1 -j32 check
- post-steps
build-linux-arm-test-full:
machine:
image: ubuntu-2004:202111-02
resource_class: arm.large
steps:
- pre-steps
- install-gflags
- run: make V=1 J=4 -j4 check
- post-steps
build-linux-arm:
machine:
image: ubuntu-2004:202111-02
resource_class: arm.large
steps:
- pre-steps
- install-gflags
- run: ROCKSDBTESTS_PLATFORM_DEPENDENT=only make V=1 J=4 -j4 all_but_some_tests check_some
- post-steps
build-linux-arm-cmake-no_test_run:
machine:
image: ubuntu-2004:202111-02
resource_class: arm.large
environment:
JAVA_HOME: /usr/lib/jvm/java-8-openjdk-arm64
steps:
- pre-steps
- install-gflags
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- run:
name: "Build with cmake"
command: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=0 -DWITH_GFLAGS=1 -DWITH_BENCHMARK_TOOLS=0 -DWITH_TOOLS=0 -DWITH_CORE_TOOLS=1 ..
make -j4
- run:
name: "Build Java with cmake"
command: |
rm -rf build
mkdir build
cd build
cmake -DJNI=1 -DCMAKE_BUILD_TYPE=Release -DWITH_GFLAGS=1 ..
make -j4 rocksdb rocksdbjni
- post-steps
build-format-compatible:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run:
name: "test"
command: |
export TEST_TMPDIR=/dev/shm/rocksdb
rm -rf /dev/shm/rocksdb
mkdir /dev/shm/rocksdb
tools/check_format_compatible.sh
- post-steps
build-fuzzers:
executor: linux-docker
resource_class: large
steps:
- pre-steps
- run:
name: "Build rocksdb lib"
command: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j4 static_lib
- run:
name: "Build fuzzers"
command: cd fuzz && make sst_file_writer_fuzzer db_fuzzer db_map_fuzzer
- post-steps
benchmark-linux: #use a private Circle CI runner (resource_class) to run the job
machine: true
resource_class: facebook/rocksdb-benchmark-sys1
steps:
- build-for-benchmarks
- perform-benchmarks
- post-benchmarks
workflows: # Only jobs that haven't been successfully migrated to GitHub Actions
version: 2
jobs-linux-arm:
jobs:
- build-linux-arm
benchmark-linux:
triggers:
- schedule:
cron: "0 * * * *"
filters:
branches:
only:
- main
jobs:
- benchmark-linux
nightly:
triggers:
- schedule:
cron: "0 9 * * *"
filters:
branches:
only:
- main
jobs:
- build-linux-arm-test-full
+6
View File
@@ -0,0 +1,6 @@
# Supress UBSAN warnings related to stl_tree.h, e.g.
# UndefinedBehaviorSanitizer: undefined-behavior /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_tree.h:1505:43 in
# /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_tree.h:1505:43:
# runtime error: upcast of address 0x000001fa8820 with insufficient space for an object of type
# 'std::_Rb_tree_node<std::pair<const std::__cxx11::basic_string<char>, rocksdb::(anonymous namespace)::LockHoldingInfo> >'
src:*bits/stl_tree.h
-10
View File
@@ -1,17 +1,7 @@
name: build-folly
description: Build folly and dependencies (skipped if cache hit)
inputs:
cache-hit:
description: Whether the folly cache was hit
required: true
runs:
using: composite
steps:
- name: Build folly and dependencies
if: ${{ inputs.cache-hit != 'true' }}
run: make build_folly
shell: bash
- name: Skip folly build (using cached version)
if: ${{ inputs.cache-hit == 'true' }}
run: echo "Folly build skipped - using cached version"
shell: bash
-33
View File
@@ -1,33 +0,0 @@
name: cache-folly
description: Cache folly build to speed up CI
outputs:
cache-hit:
description: Whether the cache was hit
value: ${{ steps.cache-folly-build.outputs.cache-hit }}
runs:
using: composite
steps:
- name: Extract FOLLY_MK_HASH
id: extract-folly-hash
shell: bash
run: |
FOLLY_MK_HASH=$(md5sum folly.mk | cut -d' ' -f1)
echo "hash=$FOLLY_MK_HASH" >> $GITHUB_OUTPUT
- name: Extract FOLLY_INSTALL_DIR
id: extract-folly-install-dir
shell: bash
run: |
FOLLY_INSTALL_DIR=$(cd third-party/folly && python3 build/fbcode_builder/getdeps.py show-inst-dir)
echo "dir=$(echo $FOLLY_INSTALL_DIR | sed 's|installed/folly|installed|')" >> $GITHUB_OUTPUT
- name: Cache folly build
id: cache-folly-build
uses: actions/cache@v4
with:
# Cache the folly build directory
path: ${{ steps.extract-folly-install-dir.outputs.dir }}
# Key is based on:
# - OS and architecture
# - The docker image, which may not always be specified/known
# - Hash of folly.mk, which includes the folly repository commit hash
# NOTE: this is still only intended for DEBUG folly builds
key: folly-build-${{ runner.os }}-${{ runner.arch }}-${{ github.job_container.image }}-${{ steps.extract-folly-hash.outputs.hash }}
+4 -4
View File
@@ -4,8 +4,8 @@ runs:
steps:
- name: Install Maven
run: |
wget --no-check-certificate https://archive.apache.org/dist/maven/maven-3/3.9.11/binaries/apache-maven-3.9.11-bin.tar.gz
tar zxf apache-maven-3.9.11-bin.tar.gz
echo "export M2_HOME=$(pwd)/apache-maven-3.9.11" >> $GITHUB_ENV
echo "$(pwd)/apache-maven-3.9.11/bin" >> $GITHUB_PATH
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 -5
View File
@@ -3,9 +3,5 @@ runs:
using: composite
steps:
- name: Checkout folly sources
run: |
make checkout_folly
shell: bash
- name: Install patchelf
run: apt-get update -y && apt-get install -y patchelf
run: make checkout_folly
shell: bash
+9 -24
View File
@@ -4,16 +4,6 @@ runs:
steps:
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.3.1
- name: Cache ccache directory
id: ccache-cache
uses: actions/cache@v4
with:
path: C:\a\rocksdb\rocksdb\.ccache
key: rocksdb-build-${{ runner.os }}-${{ runner.arch }}-ccache-${{ hashFiles('CMakeLists.txt', 'cmake/**/*.cmake') }}-v1
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
max-size: "10GB"
- name: Custom steps
env:
THIRDPARTY_HOME: ${{ github.workspace }}/thirdparty
@@ -21,9 +11,9 @@ runs:
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.2.2
SNAPPY_INCLUDE: ${{ github.workspace }}/thirdparty/snappy-1.2.2;${{ github.workspace }}/thirdparty/snappy-1.2.2/build
SNAPPY_LIB_DEBUG: ${{ github.workspace }}/thirdparty/snappy-1.2.2/build/Debug/snappy.lib
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 =====================
@@ -32,14 +22,14 @@ runs:
mkdir $Env:THIRDPARTY_HOME
cd $Env:THIRDPARTY_HOME
echo "Building Snappy dependency..."
curl -Lo snappy-1.2.2.zip https://github.com/google/snappy/archive/refs/tags/1.2.2.zip
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.2.2.zip
unzip -q snappy-1.1.8.zip
if(!$?) { Exit $LASTEXITCODE }
cd snappy-1.2.2
cd snappy-1.1.8
mkdir build
cd build
& cmake -G "$Env:CMAKE_GENERATOR" .. -DSNAPPY_BUILD_TESTS=OFF -DSNAPPY_BUILD_BENCHMARKS=OFF
& cmake -G "$Env:CMAKE_GENERATOR" ..
if(!$?) { Exit $LASTEXITCODE }
msbuild Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
if(!$?) { Exit $LASTEXITCODE }
@@ -48,12 +38,11 @@ runs:
$env:Path = $env:JAVA_HOME + ";" + $env:Path
mkdir build
cd build
& cmake -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DWIN_CI=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DXPRESS=1 -DJNI=1 ..
& 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"
# use more parallel processes than the number of processes available, as most of the compile command would be cache hit
msbuild build/rocksdb.sln /m:32 /p:LinkIncremental=false -property:Configuration=Debug -property:Platform=x64
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
@@ -63,7 +52,3 @@ runs:
& ctest -C Debug -j 16
if(!$?) { Exit $LASTEXITCODE }
shell: pwsh
- name: Show ccache stats
shell: pwsh
run: |
ccache --show-stats -v
+5 -5
View File
@@ -1,13 +1,13 @@
name: facebook/rocksdb/benchmark-linux
on: workflow_dispatch
permissions: {}
# FIXME: Disabled temporarily
# schedule:
# - cron: 7 */2 * * * # At minute 7 past every 2nd hour
jobs:
# FIXME: when this job is fixed, it should be given a cron schedule like
# schedule:
# - cron: 0 * * * *
# workflow_dispatch:
benchmark-linux:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: ubuntu-latest # FIXME: change this back to self-hosted when ready
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/build-for-benchmarks"
-1
View File
@@ -1,6 +1,5 @@
name: facebook/rocksdb/nightly
on: workflow_dispatch
permissions: {}
jobs:
# These jobs would be in nightly but are failing or otherwise broken for
# some reason.
+29 -88
View File
@@ -3,14 +3,13 @@ on:
schedule:
- cron: 0 9 * * *
workflow_dispatch:
permissions: {}
jobs:
build-format-compatible:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
@@ -27,12 +26,24 @@ jobs:
git config --global --add safe.directory /__w/rocksdb/rocksdb
tools/check_format_compatible.sh
- uses: "./.github/actions/post-steps"
build-linux-run-microbench:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- run: DEBUG_LEVEL=0 make -j32 run_microbench
- uses: "./.github/actions/post-steps"
build-linux-non-shm:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
env:
TEST_TMPDIR: "/tmp/rocksdb_test_tmp"
@@ -41,55 +52,31 @@ jobs:
- uses: "./.github/actions/pre-steps"
- run: make V=1 -j32 check
- uses: "./.github/actions/post-steps"
build-linux-clang-18-asan-ubsan-with-folly:
build-linux-clang-13-asan-ubsan-with-folly:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.0
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
env:
CC: clang-18
CXX: clang++-18
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- uses: "./.github/actions/setup-folly"
- uses: "./.github/actions/build-folly"
- run: LIB_MODE=static USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check
- run: CC=clang-13 CXX=clang++-13 LIB_MODE=static USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check
- uses: "./.github/actions/post-steps"
build-linux-cmake-with-folly:
build-linux-valgrind:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
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/cache-folly"
id: cache-folly
- uses: "./.github/actions/build-folly"
with:
cache-hit: ${{ steps.cache-folly.outputs.cache-hit }}
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make VERBOSE=1 -j20 && ctest -j20)"
- uses: "./.github/actions/post-steps"
build-linux-release-with-folly:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- uses: "./.github/actions/setup-folly"
- run: "DEBUG_LEVEL=0 make -j20 build_folly"
- run: "USE_FOLLY=1 LIB_MODE=static DEBUG_LEVEL=0 V=1 make -j20 release"
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 -DCMAKE_BUILD_TYPE=Release .. && make VERBOSE=1 -j20 && ctest -j20)"
- run: PORTABLE=1 make V=1 -j32 valgrind_test
- uses: "./.github/actions/post-steps"
build-windows-vs2022-avx2:
if: ${{ github.repository_owner == 'facebook' }}
@@ -100,6 +87,15 @@ jobs:
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/windows-build-steps"
build-windows-vs2022:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: windows-2022
env:
CMAKE_GENERATOR: Visual Studio 17 2022
CMAKE_PORTABLE: 1
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/windows-build-steps"
build-linux-arm-test-full:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
@@ -110,58 +106,3 @@ jobs:
- run: sudo apt-get update && sudo apt-get install -y build-essential libgflags-dev
- run: make V=1 J=4 -j4 check
- uses: "./.github/actions/post-steps"
build-linux-arm-crashtest:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 4-core-ubuntu-arm
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- run: sudo apt-get update && sudo apt-get install -y build-essential libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev libzstd-dev
- run: sudo mount -o remount,size=16G /dev/shm
- run: sudo dd bs=1048576 count=4096 if=/dev/zero of=/swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=1800 --max_key=2500000' blackbox_crash_test_with_atomic_flush
- run: rm -rf /dev/shm/rocksdb.*
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=1800 --max_key=2500000' blackbox_crash_test_with_multiops_wc_txn
- uses: "./.github/actions/post-steps"
build-examples:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
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: ghcr.io/facebook/rocksdb_ubuntu:24.0
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- name: Build rocksdb lib
run: CC=clang-18 CXX=clang++-18 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-cmake-with-folly-lite:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
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 -DCMAKE_CXX_FLAGS=-DGLOG_USE_GLOG_EXPORT .. && make VERBOSE=1 -j20 && ctest -j20)"
- uses: "./.github/actions/post-steps"
-1
View File
@@ -1,6 +1,5 @@
name: facebook/rocksdb/pr-jobs-candidate
on: workflow_dispatch
permissions: {}
jobs:
# These jobs would be in pr-jobs but are failing or otherwise broken for
# some reason.
+183 -111
View File
@@ -1,6 +1,5 @@
name: facebook/rocksdb/pr-jobs
on: [push, pull_request]
permissions: {}
jobs:
# NOTE: multiple workflows would be recommended, but the current GHA UI in
# PRs doesn't make it clear when there's an overall error with a workflow,
@@ -31,7 +30,7 @@ jobs:
# ======================== Fast Initial Checks ====================== #
check-format-and-targets:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.1.0
with:
@@ -45,28 +44,20 @@ jobs:
- name: Install argparse
run: pip install argparse
- name: Download clang-format-diff.py
run: wget https://rocksdb-deps.s3.us-west-2.amazonaws.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
run: wget https://raw.githubusercontent.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
- name: Check format
run: VERBOSE_CHECK=1 make check-format
- name: Compare buckify output
run: make check-buck-targets
- name: Simple source code checks
run: make check-sources
- name: Sanity check check_format_compatible.sh
run: |-
export TEST_TMPDIR=/dev/shm/rocksdb
rm -rf /dev/shm/rocksdb
mkdir /dev/shm/rocksdb
git reset --hard
git config --global --add safe.directory /__w/rocksdb/rocksdb
SANITY_CHECK=1 LONG_TEST=1 tools/check_format_compatible.sh
# ========================= Linux With Tests ======================== #
build-linux:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
@@ -78,7 +69,7 @@ jobs:
runs-on:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.0
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
@@ -92,79 +83,101 @@ jobs:
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-make-with-folly:
build-linux-cmake-with-folly:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
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/cache-folly"
id: cache-folly
- uses: "./.github/actions/build-folly"
with:
cache-hit: ${{ steps.cache-folly.outputs.cache-hit }}
- run: USE_FOLLY=1 LIB_MODE=static V=1 make -j32 check
- 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-make-with-folly-lite-no-test:
build-linux-cmake-with-folly-lite-no-test:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
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 EXTRA_CXXFLAGS=-DGLOG_USE_GLOG_EXPORT V=1 make -j32 all
- 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: ghcr.io/facebook/rocksdb_ubuntu:22.1
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/cache-folly"
id: cache-folly
- uses: "./.github/actions/build-folly"
with:
cache-hit: ${{ steps.cache-folly.outputs.cache-hit }}
- run: "(mkdir build && cd build && cmake -DUSE_COROUTINES=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make VERBOSE=1 -j20 && ctest -j20)"
- 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-no-thread-status:
build-linux-cmake-with-benchmark:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
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 -DCMAKE_CXX_FLAGS=-DNROCKSDB_THREAD_STATUS .. && make VERBOSE=1 -j20 && ctest -j20
- 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: ghcr.io/facebook/rocksdb_ubuntu:22.1
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 built-in compression types: kNoCompression$' # Verify no compiled in compression\n"
- 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:
@@ -172,82 +185,141 @@ jobs:
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
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: "./trace_analyzer --version" # A tool dependent on gflags that can run in release build
- run: "./db_stress --version"
- run: make clean
- run: USE_RTTI=1 make V=1 -j32 release
- run: make V=1 -j32 release
- run: ls librocksdb.a
- run: "./trace_analyzer --version"
- 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 ./trace_analyzer --version; then false; else true; fi
- run: if ./db_stress --version; then false; else true; fi
- run: make clean
- run: USE_RTTI=1 make V=1 -j32 release
- run: make V=1 -j32 release
- run: ls librocksdb.a
- run: if ./trace_analyzer --version; then false; else true; fi
- run: if ./db_stress --version; then false; else true; fi
- uses: "./.github/actions/post-steps"
build-linux-clang-13-no_test_run:
build-linux-release-rtti:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 8-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
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"
# FIXME: get back to "all microbench" targets
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 EXTRA_CXXFLAGS=-stdlib=libc++ EXTRA_LDFLAGS=-stdlib=libc++ make -j32 shared_lib
- run: make clean
# FIXME: get back to "release" target
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 EXTRA_CXXFLAGS=-stdlib=libc++ EXTRA_LDFLAGS=-stdlib=libc++ DEBUG_LEVEL=0 make -j32 shared_lib
- name: Build examples
run: make V=1 -j4 static_lib && cd examples && make V=1 -j4
- uses: "./.github/actions/post-steps"
build-linux-clang-18-no_test_run:
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: ghcr.io/facebook/rocksdb_ubuntu:24.0
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- run: CC=clang-18 CXX=clang++-18 USE_CLANG=1 make -j32 all microbench
- run: make clean
- run: CC=clang-18 CXX=clang++-18 USE_CLANG=1 DEBUG_LEVEL=0 make -j32 release
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j32 all microbench
- uses: "./.github/actions/post-steps"
build-linux-gcc-14-no_test_run:
build-linux-gcc-8-no_test_run:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.0
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- run: CC=gcc-14 CXX=g++-14 V=1 make -j32 all microbench
- 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-clang18-clang-analyze:
build-linux-clang10-clang-analyze:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.0
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- run: CC=clang-18 CXX=clang++-18 ROCKSDB_DISABLE_ALIGNED_NEW=1 CLANG_ANALYZER="/usr/bin/clang++-18" CLANG_SCAN_BUILD=scan-build-18 USE_CLANG=1 make V=1 -j32 analyze
- 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
@@ -275,7 +347,7 @@ jobs:
runs-on:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
@@ -283,98 +355,104 @@ jobs:
- 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-clang18-asan-ubsan:
build-linux-clang10-asan:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 32-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.0
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 COMPILE_WITH_UBSAN=1 CC=clang-18 CXX=clang++-18 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j40 check
- 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-clang18-mini-tsan:
build-linux-clang10-ubsan:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 32-core-ubuntu
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.0
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-18 CXX=clang++-18 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check
- run: COMPILE_WITH_UBSAN=1 OPT="-fsanitize-blacklist=.circleci/ubsan_suppression_list.txt" CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 ubsan_check
- uses: "./.github/actions/post-steps"
build-linux-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: ghcr.io/facebook/rocksdb_ubuntu:22.1
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_USE_STD_SEMAPHORES -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" make V=1 -j24 check
- 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-15-xlarge
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: 16.4.0
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 -j8 all
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-15-xlarge
runs-on: macos-13
strategy:
matrix:
run_sharded_tests: [0, 1, 2, 3]
run_even_tests: [true, false]
steps:
- uses: actions/checkout@v4.1.0
- uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: 16.4.0
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 VERBOSE=1 -j8
- name: Run shard 0 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 0,,4
if: ${{ matrix.run_sharded_tests == 0 }}
- name: Run shard 1 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 1,,4
if: ${{ matrix.run_sharded_tests == 1 }}
- name: Run shard 2 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 2,,4
if: ${{ matrix.run_sharded_tests == 2 }}
- name: Run shard 3 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 3,,4
if: ${{ matrix.run_sharded_tests == 3 }}
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-vs2022:
build-windows-vs2019:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: windows-8-core
runs-on: windows-2019
env:
CMAKE_GENERATOR: Visual Studio 17 2022
CMAKE_GENERATOR: Visual Studio 16 2019
CMAKE_PORTABLE: 1
steps:
- uses: actions/checkout@v4.1.0
@@ -385,13 +463,11 @@ jobs:
runs-on:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
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.
# NOTE: replaced evolvedbinary/rocksjava:centos7_x64-be with ghcr.io/facebook/rocksdb_ubuntu:22.1
# until a more appropriate docker image with C++20 support is made.
- name: Checkout
env:
GH_TOKEN: ${{ github.token }}
@@ -408,21 +484,18 @@ jobs:
which java && java -version
which javac && javac -version
- name: Test RocksDBJava
# NOTE: replaced scl enable devtoolset-7 'make V=1 J=8 -j8 jtest'
run: make V=1 J=8 -j8 jtest
# post-steps skipped because of compatibility issues with docker image
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: ghcr.io/facebook/rocksdb_ubuntu:22.1
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.
# NOTE: replaced evolvedbinary/rocksjava:centos7_x64-be with ghcr.io/facebook/rocksdb_ubuntu:22.1
# until a more appropriate docker image with C++20 support is made.
- name: Checkout
env:
GH_TOKEN: ${{ github.token }}
@@ -439,12 +512,11 @@ jobs:
which java && java -version
which javac && javac -version
- name: Build RocksDBJava Static Library
# NOTE: replaced scl enable devtoolset-7 'make V=1 J=8 -j8 rocksdbjavastatic'
run: make V=1 J=8 -j8 rocksdbjavastatic
# post-steps skipped because of compatibility issues with docker image
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-15-xlarge
runs-on: macos-13
env:
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
ROCKSDB_DISABLE_JEMALLOC: 1
@@ -452,7 +524,7 @@ jobs:
- uses: actions/checkout@v4.1.0
- uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: 16.4.0
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"
@@ -467,14 +539,14 @@ jobs:
- uses: "./.github/actions/post-steps"
build-macos-java-static:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: macos-15-xlarge
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: 16.4.0
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"
@@ -489,14 +561,14 @@ jobs:
- uses: "./.github/actions/post-steps"
build-macos-java-static-universal:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: macos-15-xlarge
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: 16.4.0
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"
@@ -514,7 +586,7 @@ jobs:
runs-on:
labels: 4-core-ubuntu
container:
image: evolvedbinary/rocksjava:alpine3_x64-be
image: evolvedbinary/rocksjava:rockylinux8_x64-be
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
-20
View File
@@ -1,20 +0,0 @@
name: facebook/rocksdb/weekly
on:
schedule:
- cron: 0 9 * * 0
workflow_dispatch:
permissions: {}
jobs:
build-linux-valgrind:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
timeout-minutes: 840
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- run: make V=1 -j20 valgrind_test
- uses: "./.github/actions/post-steps"
-4
View File
@@ -47,9 +47,6 @@ package/
unity.a
tags
etags
GPATH
GRTAGS
GTAGS
rocksdb_dump
rocksdb_undump
db_test2
@@ -91,7 +88,6 @@ buckifier/__pycache__
.arcconfig
compile_commands.json
meta_gen_cpp_compile_commands.json
clang-format-diff.py
.py3/
+24 -92
View File
@@ -27,12 +27,12 @@
#
# Linux:
#
# 1. Install a recent toolchain if you're on a older distro. C++20 required (GCC >= 11, Clang >= 10)
# 1. Install a recent toolchain if you're on a older distro. C++17 required (GCC >= 7, Clang >= 5)
# 2. mkdir build; cd build
# 3. cmake ..
# 4. make -j
cmake_minimum_required(VERSION 3.12)
cmake_minimum_required(VERSION 3.10)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/")
include(ReadVersion)
@@ -80,7 +80,6 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING
"Default BUILD_TYPE is ${default_build_type}" FORCE)
endif()
message(STATUS "CMAKE_BUILD_TYPE is set to ${CMAKE_BUILD_TYPE}")
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
@@ -101,7 +100,7 @@ endif()
option(ROCKSDB_BUILD_SHARED "Build shared versions of the RocksDB libraries" ON)
if( NOT DEFINED CMAKE_CXX_STANDARD )
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 17)
endif()
include(CMakeDependentOption)
@@ -133,9 +132,7 @@ else()
option(WITH_GFLAGS "build with GFlags" ON)
endif()
set(GFLAGS_LIB)
# Skip all gflags detection and setup when USE_FOLLY or USE_COROUTINES is enabled
# since Folly provides its own gflags (USE_COROUTINES automatically sets USE_FOLLY)
if(WITH_GFLAGS AND NOT USE_FOLLY AND NOT USE_COROUTINES)
if(WITH_GFLAGS)
# Config with namespace available since gflags 2.2.2
option(GFLAGS_USE_TARGET_NAMESPACE "Use gflags import target with namespace." ON)
find_package(gflags CONFIG)
@@ -154,9 +151,6 @@ else()
include_directories(${GFLAGS_INCLUDE_DIR})
list(APPEND THIRDPARTY_LIBS ${GFLAGS_LIB})
add_definitions(-DGFLAGS=1)
elseif(WITH_GFLAGS AND (USE_FOLLY OR USE_COROUTINES))
# Still set the DGFLAGS=1 define when using Folly since Folly provides gflags
add_definitions(-DGFLAGS=1)
endif()
if(WITH_SNAPPY)
@@ -209,20 +203,9 @@ if(WIN32 AND MSVC)
endif()
endif()
option(WIN_CI "Accelerate build speed and reduce build artifect size for github CI with MSVC" OFF)
if(MSVC)
if(WIN_CI)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /nologo /EHsc /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /W4 /wd4127 /wd4996 /wd4100 /wd4324 /wd4702")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W4 /wd4127 /wd4996 /wd4100 /wd4324")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Release")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DNDEBUG")
message(STATUS "Setting /DNDEBUG as CMAKE_BUILD_TYPE is set to ${CMAKE_BUILD_TYPE}")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W4 /wd4127 /wd4996 /wd4100 /wd4324")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wextra -Wall -pthread")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wshadow -Wno-unused-parameter -Wno-unused-variable -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers -Wno-strict-aliasing -Wno-invalid-offsetof")
@@ -284,8 +267,8 @@ endif(CMAKE_SYSTEM_PROCESSOR MATCHES "s390x")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "loongarch64")
CHECK_C_COMPILER_FLAG("-march=loongarch64" HAS_LOONGARCH64)
if(HAS_LOONGARCH64)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=loongarch64 -mtune=loongarch64")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=loongarch64 -mtune=loongarch64")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=loongarch64 -mtune=loongarch64")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=loongarch64 -mtune=loongarch64")
endif(HAS_LOONGARCH64)
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "loongarch64")
@@ -330,7 +313,8 @@ if(NOT MSVC)
endif()
# Check if -latomic is required or not
if (NOT MSVC AND NOT APPLE)
if (NOT MSVC)
set(CMAKE_REQUIRED_FLAGS "--std=c++17")
CHECK_CXX_SOURCE_COMPILES("
#include <atomic>
std::atomic<uint64_t> x(0);
@@ -467,33 +451,24 @@ else()
endif()
endif()
# Used to run optimized debug build and tests so we can run faster
# Used to run CI build and tests so we can run faster
option(OPTDBG "Build optimized debug build with MSVC" OFF)
option(WITH_RUNTIME_DEBUG "build with debug version of runtime library" ON)
if(MSVC)
if (WIN_CI)
if(OPTDBG)
message(STATUS "Debug optimization is enabled")
set(CMAKE_CXX_FLAGS_DEBUG "/Oxt")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG:FASTLINK")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG:FASTLINK")
else()
if(OPTDBG)
message(STATUS "Debug optimization is enabled")
set(CMAKE_CXX_FLAGS_DEBUG "/Oxt")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1")
# Minimal Build is deprecated after MSVC 2015
if( MSVC_VERSION GREATER 1900 )
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gm-")
else()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1")
# Minimal Build is deprecated after MSVC 2015
if( MSVC_VERSION GREATER 1900 )
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gm-")
else()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gm")
endif()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gm")
endif()
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
endif()
endif()
if(WITH_RUNTIME_DEBUG)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /${RUNTIME_LIBRARY}d")
else()
@@ -501,6 +476,8 @@ if(MSVC)
endif()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oxt /Zp8 /Gm- /Gy /${RUNTIME_LIBRARY}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
endif()
if(CMAKE_COMPILER_IS_GNUCXX)
@@ -511,8 +488,6 @@ if(CMAKE_SYSTEM_NAME MATCHES "Cygwin")
add_definitions(-fno-builtin-memcmp -DCYGWIN)
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
add_definitions(-DOS_MACOSX)
elseif(CMAKE_SYSTEM_NAME MATCHES "iOS")
add_definitions(-DOS_MACOSX -DIOS_CROSS_COMPILE)
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
add_definitions(-DOS_LINUX)
elseif(CMAKE_SYSTEM_NAME MATCHES "SunOS")
@@ -640,7 +615,7 @@ if(USE_FOLLY)
FMT_INST_PATH)
exec_program(ls ARGS -d ${FOLLY_INST_PATH}/../gflags* OUTPUT_VARIABLE
GFLAGS_INST_PATH)
set(Boost_DIR ${BOOST_INST_PATH}/lib/cmake/Boost-1.83.0)
set(Boost_DIR ${BOOST_INST_PATH}/lib/cmake/Boost-1.78.0)
if(EXISTS ${FMT_INST_PATH}/lib64)
set(fmt_DIR ${FMT_INST_PATH}/lib64/cmake/fmt)
else()
@@ -652,12 +627,6 @@ if(USE_FOLLY)
${FOLLY_INST_PATH}/lib/cmake/folly/folly-targets.cmake)
include(${FOLLY_INST_PATH}/lib/cmake/folly/folly-config.cmake)
# Fix gflags library name for debug builds
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath=${GFLAGS_INST_PATH}/lib")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GFLAGS_INST_PATH}/lib/libgflags_debug.so.2.2")
endif()
endif()
add_compile_definitions(USE_FOLLY FOLLY_NO_CONFIG HAVE_CXX11_ATOMIC)
@@ -722,7 +691,6 @@ set(SOURCES
db/db_impl/db_impl_write.cc
db/db_impl/db_impl_compaction_flush.cc
db/db_impl/db_impl_files.cc
db/db_impl/db_impl_follower.cc
db/db_impl/db_impl_open.cc
db/db_impl/db_impl_debug.cc
db/db_impl/db_impl_experimental.cc
@@ -745,12 +713,10 @@ set(SOURCES
db/log_reader.cc
db/log_writer.cc
db/malloc_stats.cc
db/manifest_ops.cc
db/memtable.cc
db/memtable_list.cc
db/merge_helper.cc
db/merge_operator.cc
db/multi_scan.cc
db/output_validator.cc
db/periodic_task_scheduler.cc
db/range_del_aggregator.cc
@@ -776,14 +742,12 @@ set(SOURCES
db/write_controller.cc
db/write_stall_stats.cc
db/write_thread.cc
db_stress_tool/db_stress_compression_manager.cc
env/composite_env.cc
env/env.cc
env/env_chroot.cc
env/env_encryption.cc
env/file_system.cc
env/file_system_tracer.cc
env/fs_on_demand.cc
env/fs_remap.cc
env/mock_env.cc
env/unique_id_gen.cc
@@ -811,7 +775,6 @@ set(SOURCES
memtable/hash_skiplist_rep.cc
memtable/skiplistrep.cc
memtable/vectorrep.cc
memtable/wbwi_memtable.cc
memtable/write_buffer_manager.cc
monitoring/histogram.cc
monitoring/histogram_windowing.cc
@@ -866,7 +829,6 @@ set(SOURCES
table/cuckoo/cuckoo_table_builder.cc
table/cuckoo/cuckoo_table_factory.cc
table/cuckoo/cuckoo_table_reader.cc
table/external_table.cc
table/format.cc
table/get_context.cc
table/iterator.cc
@@ -905,13 +867,11 @@ set(SOURCES
trace_replay/trace_record.cc
trace_replay/trace_replay.cc
util/async_file_reader.cc
util/auto_tune_compressor.cc
util/cleanable.cc
util/coding.cc
util/compaction_job_stats_impl.cc
util/comparator.cc
util/compression.cc
util/simple_mixed_compressor.cc
util/compression_context_cache.cc
util/concurrent_task_limiter_impl.cc
util/crc32c.cc
@@ -973,11 +933,8 @@ set(SOURCES
utilities/persistent_cache/block_cache_tier_metadata.cc
utilities/persistent_cache/persistent_cache_tier.cc
utilities/persistent_cache/volatile_tier_impl.cc
utilities/secondary_index/secondary_index_iterator.cc
utilities/secondary_index/simple_secondary_index.cc
utilities/simulator_cache/cache_simulator.cc
utilities/simulator_cache/sim_cache.cc
utilities/table_properties_collectors/compact_for_tiering_collector.cc
utilities/table_properties_collectors/compact_on_deletion_collector.cc
utilities/trace/file_trace_reader_writer.cc
utilities/trace/replayer_impl.cc
@@ -1080,8 +1037,10 @@ endif()
else()
list(APPEND SOURCES
db/db_impl/db_impl_follower.cc
port/port_posix.cc
env/env_posix.cc
env/fs_on_demand.cc
env/fs_posix.cc
env/io_posix.cc)
endif()
@@ -1090,7 +1049,6 @@ if(USE_FOLLY_LITE)
list(APPEND SOURCES
third-party/folly/folly/container/detail/F14Table.cpp
third-party/folly/folly/detail/Futex.cpp
third-party/folly/folly/lang/Exception.cpp
third-party/folly/folly/lang/SafeAssert.cpp
third-party/folly/folly/lang/ToAscii.cpp
third-party/folly/folly/ScopeGuard.cpp
@@ -1098,21 +1056,6 @@ if(USE_FOLLY_LITE)
third-party/folly/folly/synchronization/DistributedMutex.cpp
third-party/folly/folly/synchronization/ParkingLot.cpp)
include_directories(${PROJECT_SOURCE_DIR}/third-party/folly)
# Add boost to the include path
exec_program(python3 ${PROJECT_SOURCE_DIR}/third-party/folly ARGS
build/fbcode_builder/getdeps.py show-source-dir boost OUTPUT_VARIABLE
BOOST_SOURCE_PATH)
exec_program(ls ARGS -d ${BOOST_SOURCE_PATH}/boost* OUTPUT_VARIABLE
BOOST_INCLUDE_DIR)
include_directories(${BOOST_INCLUDE_DIR})
# Add fmt to the include path
exec_program(python3 ${PROJECT_SOURCE_DIR}/third-party/folly ARGS
build/fbcode_builder/getdeps.py show-source-dir fmt OUTPUT_VARIABLE
FMT_SOURCE_PATH)
exec_program(ls ARGS -d ${FMT_SOURCE_PATH}/fmt*/include OUTPUT_VARIABLE
FMT_INCLUDE_DIR)
include_directories(${FMT_INCLUDE_DIR})
add_definitions(-DUSE_FOLLY -DFOLLY_NO_CONFIG)
list(APPEND THIRDPARTY_LIBS glog)
endif()
@@ -1384,7 +1327,6 @@ if(WITH_TESTS)
db/db_clip_test.cc
db/db_dynamic_level_test.cc
db/db_encryption_test.cc
db/db_etc3_test.cc
db/db_flush_test.cc
db/db_inplace_update_test.cc
db/db_io_failure_test.cc
@@ -1499,7 +1441,6 @@ if(WITH_TESTS)
util/autovector_test.cc
util/bloom_test.cc
util/coding_test.cc
util/compression_test.cc
util/crc32c_test.cc
util/defer_test.cc
util/dynamic_bloom_test.cc
@@ -1538,12 +1479,10 @@ if(WITH_TESTS)
utilities/persistent_cache/persistent_cache_test.cc
utilities/simulator_cache/cache_simulator_test.cc
utilities/simulator_cache/sim_cache_test.cc
utilities/table_properties_collectors/compact_for_tiering_collector_test.cc
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
utilities/transactions/optimistic_transaction_test.cc
utilities/transactions/transaction_test.cc
utilities/transactions/lock/point/point_lock_manager_test.cc
utilities/transactions/lock/point/point_lock_manager_stress_test.cc
utilities/transactions/write_committed_transaction_ts_test.cc
utilities/transactions/write_prepared_transaction_test.cc
utilities/transactions/write_unprepared_transaction_test.cc
@@ -1619,7 +1558,6 @@ if(WITH_BENCHMARK_TOOLS)
add_executable(db_bench${ARTIFACT_SUFFIX}
tools/simulated_hybrid_file_system.cc
tools/db_bench.cc
tools/tool_hooks.cc
tools/db_bench_tool.cc)
target_link_libraries(db_bench${ARTIFACT_SUFFIX}
${ROCKSDB_LIB} ${THIRDPARTY_LIBS})
@@ -1654,12 +1592,6 @@ if(WITH_BENCHMARK_TOOLS)
utilities/persistent_cache/hash_table_bench.cc)
target_link_libraries(hash_table_bench${ARTIFACT_SUFFIX}
${ROCKSDB_LIB} ${GFLAGS_LIB} ${FOLLY_LIBS})
add_executable(point_lock_bench${ARTIFACT_SUFFIX}
utilities/transactions/lock/point/point_lock_bench.cc
utilities/transactions/lock/point/point_lock_bench_tool.cc)
target_link_libraries(point_lock_bench${ARTIFACT_SUFFIX}
${ROCKSDB_LIB} ${GFLAGS_LIB} ${FOLLY_LIBS})
endif()
option(WITH_TRACE_TOOLS "build with trace tools" ON)
-9
View File
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<CLToolExe>ccache_msvc_compiler.bat</CLToolExe>
<CLToolPath>$(MSBuildThisFileDirectory)</CLToolPath>
<UseMultiToolTask>true</UseMultiToolTask>
<EnforceProcessCountAcrossBuilds>true</EnforceProcessCountAcrossBuilds>
</PropertyGroup>
</Project>
+15 -402
View File
@@ -1,401 +1,14 @@
# Rocksdb Change Log
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 10.10.1 (02/02/2026)
## 9.3.2 (08/02/2024)
### Bug Fixes
* Fix Windows VS 2022 build errors.
## 10.10.0 (12/16/2025)
### Bug Fixes
* Fixed a bug in best-efforts recovery that causes use-after-free crashes when accessing SST files that were cached during the recovery.
* Fix resumable compaction incorrectly allowing resumption from a truncated range deletion that is not well handled currently.
* Fixed a bug in `PosixRandomFileAccess` IO uring submission queue ownership & management. Fix eliminates the false positive 'Bad cqe data' IO errors in `PosixRandomFileAccess::MultiRead` when interleaved with `PosixRandomFileAccess::ReadAsync` on the same thread.
## 10.9.0 (11/21/2025)
### New Features
* Added an auto-tuning feature for DB manifest file size that also (by default) improves the safety of existing configurations in case `max_manifest_file_size` is repeatedly exceeded. The new recommendation is to set `max_manifest_file_size` to something small like 1MB and tune `max_manifest_space_amp_pct` as needed to balance write amp and space amp in the manifest. Refer to comments on those options in `DBOptions` for details. Both options are (now) mutable.
* Added a new API to support option migration for multiple column families
* Added new option target_file_size_is_upper_bound that makes most compaction output SST files come close to the target file size without exceeding it, rather than commonly exceeding it by some fraction (current behavior). For now the new behavior is off by default, but we expect to enable it by default in the future.
* Add a new option allow_trivial_move in CompactionOptions to allow CompactFiles to perform trivial move if possible. By default the flag of allow_trivial_move is false, so it preserve the original behavior.
### Public API Changes
* To reduce risk of ODR violations or similar, `ROCKSDB_USING_THREAD_STATUS` has been removed from public headers and replaced with static `const bool ThreadStatus::kEnabled`. Some other uses of conditional compilation have been removed from public API headers to reduce risk of ODR violations or other issues.
### Behavior Changes
* PosixWritableFile now repositions the seek pointer to the new end of file after a call to Truncate.
* Updated standalone range deletion L0 file compaction behavior to avoid compacting with any newer L0 files (which is expensive and not useful).
### Bug Fixes
* Fix a bug where compaction with range deletion can persist kTypeMaxValid in MANIFEST as file metadata. kTypeMaxValid is not supposed to be persisted and can change as new value types are introduced. This can cause a forward compatibility issue where older versions of RocksDB don't recognize kTypeMaxValid from newer versions. A new placeholder value type kTypeTruncatedRangeDeletionSentinel is also introduced to replace kTypeMaxValid when reading existing SST files' metadata from MANIFEST. This allows us to strengthen some checks to avoid using kTypeMaxValid in the future.
* Fixed a bug where `DB::GetSortedWalFiles()` could hang when waiting for a purge operation that found nothing to do (potentially triggered by iterator release, flush, compaction, etc.).
* Fixed a bug in MultiScan where `max_sequential_skip_in_iterations` could cause the iterator to seek backward to already-unpinned blocks when the same user key spans multiple data blocks, leading to assertion failures or seg fault.
* Fixed a bug for `WAL_ttl_seconds > 0` use cases where the newest archived WAL files could be incorrectly deleted when the system clock moved backwards.
### Performance Improvements
* Added optimization that allowed for the asynchronous prefetching of all data outlined in a multiscan iterator. This optimization was applied to the level iterator, which prefetches all data through each of the block-based iterators.
## 10.8.0 (10/21/2025)
### New Features
* Add kFSPrefetch to FSSupportedOps enum to allow file systems to indicate prefetch support capability, avoiding unnecessary prefetch system calls on file systems that don't support them.
* Added experimental support `OpenAndCompactOptions::allow_resumption` for resumable compaction that persists progress during `OpenAndCompact()`, allowing interrupted compactions to resume from the last progress persitence. The default behavior is to not persist progress.
### Public API Changes
* Allow specifying output temperature in CompactionOptions
* Added `DB::FlushWAL(const FlushWALOptions&)` as an alternative to `DB::FlushWAL(bool sync)`, where `FlushWALOptions` includes a new `rate_limiter_priority` field (default `Env::IO_TOTAL`) that allows rate limiting and priority passing of manual WAL flush's IO operations.
* The MultiScan API contract is updated. After a multi scan range got prepared with Prepare API call, the following seeks must seek the start of each prepared scan range in order. In addition, when limit is set, upper bound must be set to the same value of limit before each seek
### Behavior Changes
* `kChangeTemperature` FIFO compaction will now honor `compaction_target_temp` to all levels regardless of `cf_options::last_level_temperature`
* Allow UDIs with a non BytewiseComparator
### Bug Fixes
* Fix incorrect MultiScan seek error status due to bugs in handling range limit falling between adjacent SST files key range.
* Fix a bug in Page unpinning in MultiScan
### Performance Improvements
* Fixed a performance regression in LZ4 compression that started in version 10.6.0
## 10.7.0 (09/19/2025)
### New Features
* Add the fail_if_no_udi_on_open flag in BlockBasedTableOption to control whether a missing user defined index block in a SST is a hard error or not.
* A new flag memtable_verify_per_key_checksum_on_seek is added to AdvancedColumnFamilyOptions. When it is enabled, it will validate key checksum along the binary search path on skiplist based memtable during seek operation.
* Introduce option MultiScanArgs::use_async_io to enable asynchronous I/O during MultiScan, instead of waiting for I/O to be done in Prepare().
* Add new option `MultiScanArgs::max_prefetch_size` that limits the memory usage of per file pinning of prefetched blocks.
* Improved `sst_dump` by allowing standalone file and directory arguments without `--file=`. Also added new options and better output for `sst_dump --command=recompress`. See `sst_dump --help`
### Public API Changes
* HyperClockCache with no `estimated_entry_charge` is now production-ready and is the preferred block cache implementation vs. LRUCache. Please consider updating your code to minimize the risk of hitting performance bottlenecks or anomalies from LRUCache. See cache.h for more detail.
* RocksDB now requires a C++20 compatible compiler (GCC >= 11, Clang >= 10, Visual Studio >= 2019), including for any code using RocksDB headers.
* MultiScanArgs used to have a default constructor with default parameter of BytewiseComparator. Now it always requires Comparator in its constructor.
### Behavior Changes
* The default provided block cache implementation is now HyperClockCache instead of LRUCache, when `block_cache` is nullptr (default) and `no_block_cache==false` (default). We recommend explicitly creating a HyperClockCache block cache based on memory budget and sharing it across all column families and even DB instances. This change could expose previously hidden memory or resource leaks.
### Bug Fixes
* Reported numbers for compaction and flush CPU usage now include time spent by parallel compression worker threads. This now means compaction/flush CPU usage could exceed the wall clock time.
* Fix a race condition in FIFO size-based compaction where concurrent threads could select the same non-L0 file, causing assertion failures in debug builds or "Cannot delete table file from LSM tree" errors in release builds.
* Fix a bug in RocksDB MultiScan with UDI when one of the scan ranges is determined to be empty by the UDI, which causes incorrect results.
### Performance Improvements
* Add a new table property "rocksdb.key.smallest.seqno" which records the smallest sequence number of all keys in file. It makes ingesting DB generated files faster by
avoiding scanning the whole file to find the smallest sequence number.
* Add a new experimental PerKeyPointLockManager to improve efficiency under high lock contention. PointLockManager was not efficient when there is high write contention on same key, as it uses a single conditional variable per lock stripe. PerKeyPointLockManager uses per thread conditional variable supporting fifo order. Although this is an experimental feature. By default, it is disabled. A new boolean flag TransactionDBOptions::use_per_key_point_lock_mgr is added to optionally enable it. Search the flag in code for more info.
Together, a new configuration TransactionOptions::deadlock_timeout_us is added, which allows the transaction to wait for a short period before perform deadlock detection. When the workload has low lock contention, the deadlock_timeout_us can be configured to be slightly higher than average transaction execution time, so that transaction would likely be able to take the lock before deadlock detection is performed when it is waiting for a lock. This allows transaction to reduce CPU cost on performing deadlock detection, which could be expensive in CPU time. When the workload has high lock contention, the deadlock_timeout_us can be configured to 0, so that transaction would perform deadlock detection immediately. By default the value is 0 to keep the behavior same as before.
* Majorly improved CPU efficiency and scalability of parallel compression (`CompressionOptions::parallel_threads` > 1), though this efficiency improvement makes parallel compression currently incompatible with UserDefinedIndex and with old setting of `decouple_partitioned_filters=false`. Parallel compression is now considered a production-ready feature. Maximum performance is available with `-DROCKSDB_USE_STD_SEMAPHORES` at compile time, but this is not currently recommended because of reported bugs in implementations of `std::counting_semaphore`/`binary_semaphore`.
## 10.6.0 (08/22/2025)
### New Features
* Introduce column family option `cf_allow_ingest_behind`. This option aims to replace `DBOptions::allow_ingest_behind` to enable ingest behind at the per-CF level. `DBOptions::allow_ingest_behind` is deprecated.
* Introduce `MultiScanArgs::io_coalesce_threshold` to allow a configurable IO coalescing threshold.
### Public API Changes
* `IngestExternalFileOptions::allow_db_generated_files` now allows files ingestion of any DB generated SST file, instead of only the ones with all keys having sequence number 0.
* `decouple_partitioned_filters = true` is now the default in BlockBasedTableOptions.
* GetTtl() API is now available in TTL DB
* Minimum supported version of LZ4 library is now 1.7.0 (r129 from 2015)
* Some changes to experimental Compressor and CompressionManager APIs
* A new Filesystem::SyncFile function is added for syncing a file that was already written, such as on file ingestion. The default implementation matches previous RocksDB behavior: re-open the file for read-write, sync it, and close it. We recommend overriding for FileSystems that do not require syncing for crash recovery or do not handle (well) re-opening for writes.
### Behavior Changes
* When `allow_ingest_behind` is enabled, compaction will no longer drop tombstones based on the absence of underlying data. Tombstones will be preserved to apply to ingested files.
### Bug Fixes
* Files in dropped column family won't be returned to the caller upon successful, offline MANIFEST iteration in `GetFileChecksumsFromCurrentManifest`.
* Fix a bug in MultiScan that causes it to fall back to a normal scan when dictionary compression is enabled.
* Fix a crash in iterator Prepare() when fill_cache=false
* Fix a bug in MultiScan where incorrect results can be returned when a Scan's range is across multiple files.
* Fixed a bug in remote compaction that may mistakenly delete live SST file(s) during the cleanup phase when no keys survive the compaction (all expired)
* Allow a user defined index to be configured from a string.
* Make the User Defined Index interface consistently use the user key format, fixing the previous mixed usage of internal and user key.
### Performance Improvements
* Small improvement to CPU efficiency of compression using built-in algorithms, and a dramatic efficiency improvement for LZ4HC, based on reusing data structures between invocations.
## 10.5.0 (07/18/2025)
### Public API Changes
* DB option skip_checking_sst_file_sizes_on_db_open is deprecated, in favor of validating file size in parallel in a thread pool, when db is opened. When DB is opened, with paranoid check enabled, a file with the wrong size would fail the DB open. With paranoid check disabled, the DB open would succeed, the column family with the corrupted file would not be read or write, while the other healthy column families could be read and write normally. When max_open_files option is not set to -1, only a subset of the files will be opened and checked. The rest of the files will be opened and checked when they are accessed.
### Behavior Changes
* PessimisticTransaction::GetWaitingTxns now returns waiting transaction information even if the current transaction has timed out. This allows the information to be surfaced to users for debugging purposes once it is known that the timeout has occurred.
* A new API GetFileSize is added to FSRandomAccessFile interface class. It uses fstat vs stat on the posix implementation which is more efficient. Caller could use it to get file size faster. This function might be required in the future for FileSystem implementation outside of the RocksDB code base.
* RocksDB now triggers eligible compactions every 12 hours when periodic compaction is configured. This solves a limitation of the compaction trigger mechanism, which would only trigger compaction after specific events like flush, compaction, or SetOptions.
### Bug Fixes
* Fix a bug in BackupEngine that can crash backup due to a null FSWritableFile passed to WritableFileWriter.
* Fix DB::NewMultiScan iterator to respect the scan upper bound specified in ScanOptions
### Performance Improvements
* Optimized MultiScan using BlockBasedTable to coalesce I/Os and prefetch all data blocks.
## 10.4.0 (06/20/2025)
### New Features
* Add a new CF option `memtable_avg_op_scan_flush_trigger` that supports triggering memtable flush when an iterator scans through an expensive range of keys, with the average number of skipped keys from the active memtable exceeding the threshold.
* Vector based memtable now supports concurrent writers (DBOptions::allow_concurrent_memtable_write) #13675.
* Add new experimental `TransactionOptions::large_txn_commit_optimize_byte_threshold` to enable optimizations for large transaction commit by transaction batch data size.
* Add a new option `CompactionOptionsUniversal::reduce_file_locking` and if it's true, auto universal compaction picking will adjust to minimize locking of input files when bottom priority compactions are waiting to run. This can increase the likelihood of existing L0s being selected for compaction, thereby improving write stall and reducing read regression.
* Add new `format_version=7` to aid experimental support of custom compression algorithms with CompressionManager and block-based table. This format version includes changing the format of `TableProperties::compression_name`.
### Public API Changes
* Change NewExternalTableFactory to return a unique_ptr instead of shared_ptr.
* Add an optional min file size requirement for deletion triggered compaction. It can be specified when creating `CompactOnDeletionCollectorFactory`.
### Behavior Changes
* `TransactionOptions::large_txn_commit_optimize_threshold` now has default value 0 for disabled. `TransactionDBOptions::txn_commit_bypass_memtable_threshold` now has no effect on transactions.
### Bug Fixes
* Fix a bug where CreateColumnFamilyWithImport() could miss the SST file for the memtable flush it triggered. The exported CF then may not contain the updates in the memtable when CreateColumnFamilyWithImport() is called.
* Fix iterator operations returning NotImplemented status if disallow_memtable_writes and paranoid_memory_checks CF options are both set.
* Fixed handling of file checksums in IngestExternalFile() to allow providing checksums using recognized but not necessarily the DB's preferred checksum function, to ease migration between checksum functions.
## 10.3.0 (05/17/2025)
### New Features
* Add new experimental `CompactionOptionsFIFO::allow_trivial_copy_when_change_temperature` along with `CompactionOptionsFIFO::trivial_copy_buffer_size` to allow optimizing FIFO compactions with tiering when kChangeTemperature to move files from source tier FileSystem to another tier FileSystem via trivial and direct copying raw sst file instead of reading thru the content of the SST file then rebuilding the table files.
* Add a new field to Compaction Stats in LOG files for the pre-compression size written to each level.
* Add new experimental `TransactionOptions::large_txn_commit_optimize_threshold` to enable optimizations for large transaction commit with per transaction threshold. `TransactionDBOptions::txn_commit_bypass_memtable_threshold` is deprecated in favor of this transaction option.
* [internal team use only] Allow an application-defined `request_id` to be passed to RocksDB and propagated to the filesystem via IODebugContext
### Bug Fixes
* Fix a bug where transaction lock upgrade can incorrectly fail with a Deadlock status. This happens when a transaction has a non-zero timeout and tries to upgrade a shared lock that is also held by another transaction.
* Pass wrapped WritableFileWriter pointer to ExternalTableBuilder so that the file checksum can be correctly calculated and returned by SstFileWriter for external table files.
* Fix an infinite-loop bug in transaction locking. This can happen if a transaction reaches lock limit and its time out expires before it attempts to wait for it.
* Fixed a potential data race with `CompressionOptions::parallel_threads > 1` and a `TablePropertiesCollector` overriding `BlockAdd()`.
## 10.2.0 (04/21/2025)
### New Features
* Provide histogram stats `COMPACTION_PREFETCH_BYTES` to measure number of bytes for RocksDB's prefetching (as opposed to file
system's prefetch) on SST file during compaction read
* A new API DB::GetNewestUserDefinedTimestamp is added to return the newest user defined timestamp seen in a column family
* Introduce API `IngestWriteBatchWithIndex()` for ingesting updates into DB while bypassing memtable writes. This improves performance when writing a large write batch to the DB.
* Add a new CF option `memtable_op_scan_flush_trigger` that triggers a flush of the memtable if an iterator's Seek()/Next() scans over a certain number of invisible entries from the memtable.
### Public API Changes
* AdvancedColumnFamilyOptions.max_write_buffer_number_to_maintain is deleted. It's deprecated since introduction of a better option max_write_buffer_size_to_maintain since RocksDB 6.5.0.
* Deprecated API `DB::MaxMemCompactionLevel()`.
* Deprecated `ReadOptions::ignore_range_deletions`.
* Deprecated API `experimental::PromoteL0()`.
* Added arbitrary string map for additional options to be overridden for remote compactions
* The fail_if_options_file_error option in DBOptions has been removed. The behavior now is to always return failure in any API that fails to persist the OPTIONS file.
### Behavior Changes
* Make stats `PREFETCH_BYTES_USEFUL`, `PREFETCH_HITS`, `PREFETCH_BYTES` only account for prefetching during user initiated scan
### Bug Fixes
* Fix a bug in Posix file system that the FSWritableFile created via `FileSystem::ReopenWritableFile` internally does not track the correct file size.
* Fix a bug where tail size of remote compaction output is not persisted in primary db's manifest
## 10.1.0 (03/24/2025)
### New Features
* Added a new `DBOptions.calculate_sst_write_lifetime_hint_set` setting that allows to customize which compaction styles SST write lifetime hint calculation is allowed on. Today RocksDB supports only two modes `kCompactionStyleLevel` and `kCompactionStyleUniversal`.
* Add a new field `num_l0_files` in `CompactionJobInfo` about the number of L0 files in the CF right before and after the compaction
* Added per-key-placement feature in Remote Compaction
* Implemented API DB::GetPropertiesOfTablesByLevel that retrieves table properties for files in each LSM tree level
### Public API Changes
* `GetAllKeyVersions()` now interprets empty slices literally, as valid keys, and uses new `OptSlice` type default value for extreme upper and lower range limits.
* `DeleteFilesInRanges()` now takes `RangeOpt` which is based on `OptSlice`. The overload taking `RangePtr` is deprecated.
* Add an unordered map of name/value pairs, ReadOptions::property_bag, to pass opaque options through to an external table when creating an Iterator.
* Introduced CompactionServiceJobStatus::kAborted to allow handling aborted scenario in Schedule(), Wait() or OnInstallation() APIs in Remote Compactions.
* format\_version < 2 in BlockBasedTableOptions is no longer supported for writing new files. Support for reading such files is deprecated and might be removed in the future. `CompressedSecondaryCacheOptions::compress_format_version == 1` is also deprecated.
### Behavior Changes
* `ldb` now returns an error if the specified `--compression_type` is not supported in the build.
* MultiGet with snapshot and ReadOptions::read_tier = kPersistedTier will now read a consistent view across CFs (instead of potentially reading some CF before and some CF after a flush).
* CreateColumnFamily() is no longer allowed on a read-only DB (OpenForReadOnly())
### Bug Fixes
* Fixed stats for Tiered Storage with preclude_last_level feature
## 10.0.0 (02/21/2025)
### New Features
* Introduced new `auto_refresh_iterator_with_snapshot` opt-in knob that (when enabled) will periodically release obsolete memory and storage resources for as long as the iterator is making progress and its supplied `read_options.snapshot` was initialized with non-nullptr value.
* Added the ability to plug-in a custom table reader implementation. See include/rocksdb/external_table_reader.h for more details.
* Experimental feature: RocksDB now supports FAISS inverted file based indices via the secondary indexing framework. Applications can use FAISS secondary indices to automatically quantize embeddings and perform K-nearest-neighbors similarity searches. See `FaissIVFIndex` and `SecondaryIndex` for more details. Note: the FAISS integration currently requires using the BUCK build.
* Add new DB property `num_running_compaction_sorted_runs` that tracks the number of sorted runs being processed by currently running compactions
* Experimental feature: added support for simple secondary indices that index the specified column as-is. See `SimpleSecondaryIndex` and `SecondaryIndex` for more details.
* Added new `TransactionDBOptions::txn_commit_bypass_memtable_threshold`, which enables optimized transaction commit (see `TransactionOptions::commit_bypass_memtable`) when the transaction size exceeds a configured threshold.
### Public API Changes
* Updated the query API of the experimental secondary indexing feature by removing the earlier `SecondaryIndex::NewIterator` virtual and adding a `SecondaryIndexIterator` class that can be utilized by applications to find the primary keys for a given search target.
* Added back the ability to leverage the primary key when building secondary index entries. This involved changes to the signatures of `SecondaryIndex::GetSecondary{KeyPrefix,Value}` as well as the addition of a new method `SecondaryIndex::FinalizeSecondaryKeyPrefix`. See the API comments for more details.
* Minimum supported version of ZSTD is now 1.4.0, for code simplification. Obsolete `CompressionType` `kZSTDNotFinalCompression` is also removed.
### Behavior Changes
* `VerifyBackup` in `verify_with_checksum`=`true` mode will now evaluate checksums in parallel. As a result, unlike in case of original implementation, the API won't bail out on a very first corruption / mismatch and instead will iterate over all the backup files logging success / _degree_of_failure_ for each.
* Reversed the order of updates to the same key in WriteBatchWithIndex. This means if there are multiple updates to the same key, the most recent update is ordered first. This affects the output of WBWIIterator. When WriteBatchWithIndex is created with `overwrite_key=true`, this affects the output only if Merge is used (#13387).
* Added support for Merge operations in transactions using option `TransactionOptions::commit_bypass_memtable`.
### Bug Fixes
* Fixed GetMergeOperands() API in ReadOnlyDB and SecondaryDB
* Fix a bug in `GetMergeOperands()` that can return incorrect status (MergeInProgress) and incorrect number of merge operands. This can happen when `GetMergeOperandsOptions::continue_cb` is set, both active and immutable memtables have merge operands and the callback stops the look up at the immutable memtable.
## 9.11.0 (01/17/2025)
### New Features
* Introduce CancelAwaitingJobs() in CompactionService interface which will allow users to implement cancellation of running remote compactions from the primary instance
* Experimental feature: RocksDB now supports defining secondary indices, which are automatically maintained by the storage engine. Secondary indices provide a new customization point: applications can provide their own by implementing the new `SecondaryIndex` interface. See the `SecondaryIndex` API comments for more details. Note: this feature is currently only available in conjunction with write-committed pessimistic transactions, and `Merge` is not yet supported.
* Provide a new option `track_and_verify_wals` to track and verify various information about WAL during WAL recovery. This is intended to be a better replacement to `track_and_verify_wals_in_manifest`.
### Public API Changes
* Add `io_buffer_size` to BackupEngineOptions to enable optimal configuration of IO size
* Clean up all the references to `random_access_max_buffer_size`, related rules and all the clients wrappers. This option has been officially deprecated in 5.4.0.
* Add `file_ingestion_nanos` and `file_ingestion_blocking_live_writes_nanos` in PerfContext to observe file ingestions
* Offer new DB::Open and variants that use `std::unique_ptr<DB>*` output parameters and deprecate the old versions that use `DB**` output parameters.
* The DB::DeleteFile API is officially deprecated.
### Behavior Changes
* For leveled compaction, manual compaction (CompactRange()) will be more strict about keeping compaction size under `max_compaction_bytes`. This prevents overly large compactions in some cases (#13306).
* Experimental tiering options `preclude_last_level_data_seconds` and `preserve_internal_time_seconds` are now mutable with `SetOptions()`. Some changes to handling of these features along with long-lived snapshots and range deletes made this possible.
### Bug Fixes
* Fix a longstanding major bug with SetOptions() in which setting changes can be quietly reverted.
## 9.10.0 (12/12/2024)
### New Features
* Introduce `TransactionOptions::commit_bypass_memtable` to enable transaction commit to bypass memtable insertions. This can be beneficial for transactions with many operations, as it reduces commit time that is mostly spent on memtable insertion.
### Public API Changes
* Deprecated Remote Compaction APIs (StartV2, WaitForCompleteV2) are completely removed from the codebase
### Behavior Changes
* DB::KeyMayExist() now follows its function comment, which means `value` parameter can be null, and it will be set only if `value_found` is passed in.
### Bug Fixes
* Fix the issue where compaction incorrectly drops a key when there is a snapshot with a sequence number of zero.
* Honor ConfigOptions.ignore_unknown_options in ParseStruct()
### Performance Improvements
* Enable reuse of file system allocated buffer for synchronous prefetching.
* In buffered IO mode, try to align writes on power of 2 if checksum handoff is not enabled for the file type being written.
## 9.9.0 (11/18/2024)
### New Features
* Multi-Column-Family-Iterator (CoalescingIterator/AttributeGroupIterator) is no longer marked as experimental
* Adds a new table property "rocksdb.newest.key.time" which records the unix timestamp of the newest key. Uses this table property for FIFO TTL and temperature change compaction.
### Public API Changes
* Added a new API `Transaction::GetAttributeGroupIterator` that can be used to create a multi-column-family attribute group iterator over the specified column families, including the data from both the transaction and the underlying database. This API is currently supported for optimistic and write-committed pessimistic transactions.
* Added a new API `Transaction::GetCoalescingIterator` that can be used to create a multi-column-family coalescing iterator over the specified column families, including the data from both the transaction and the underlying database. This API is currently supported for optimistic and write-committed pessimistic transactions.
### Behavior Changes
* `BaseDeltaIterator` now honors the read option `allow_unprepared_value`.
### Bug Fixes
* `BaseDeltaIterator` now calls `PrepareValue` on the base iterator in case it has been created with the `allow_unprepared_value` read option set. Earlier, such base iterators could lead to incorrect values being exposed from `BaseDeltaIterator`.
* Fix a leak of obsolete blob files left open until DB::Close(). This bug was introduced in version 9.4.0.
* Fix missing cases of corruption retry during DB open and read API processing.
* Fix a bug for transaction db with 2pc where an old WAL may be retained longer than needed (#13127).
* Fix leaks of some open SST files (until `DB::Close()`) that are written but never become live due to various failures. (We now have a check for such leaks with no outstanding issues.)
* Fix a bug for replaying WALs for WriteCommitted transaction DB when its user-defined timestamps setting is toggled on/off between DB sessions.
### Performance Improvements
* Fix regression in issue #12038 due to `Options::compaction_readahead_size` greater than `max_sectors_kb` (i.e, largest I/O size that the OS issues to a block device defined in linux)
## 9.8.0 (10/25/2024)
### New Features
* All non-`block_cache` options in `BlockBasedTableOptions` are now mutable with `DB::SetOptions()`. See also Bug Fixes below.
* When using iterators with BlobDB, it is now possible to load large values on an on-demand basis, i.e. only if they are actually needed by the application. This can save I/O in use cases where the values associated with certain keys are not needed. For more details, see the new read option `allow_unprepared_value` and the iterator API `PrepareValue`.
* Add a new file ingestion option `IngestExternalFileOptions::fill_cache` to support not adding blocks from ingested files into block cache during file ingestion.
* The option `allow_unprepared_value` is now also supported for multi-column-family iterators (i.e. `CoalescingIterator` and `AttributeGroupIterator`).
* When a file with just one range deletion (standalone range deletion file) is ingested via bulk loading, it will be marked for compaction. During compaction, this type of files can be used to directly filter out some input files that are not protected by any snapshots and completely deleted by the standalone range deletion file.
### Behavior Changes
* During file ingestion, overlapping files level assignment are done in multiple batches, so that they can potentially be assigned to lower levels other than always land on L0.
* OPTIONS file to be loaded by remote worker is now preserved so that it does not get purged by the primary host. A similar technique as how we are preserving new SST files from getting purged is used for this. min_options_file_numbers_ is tracked like pending_outputs_ is tracked.
* Trim readahead_size during scans so data blocks containing keys that are not in the same prefix as the seek key in `Seek()` are not prefetched when `ReadOptions::auto_readahead_size=true` (default value) and `ReadOptions::prefix_same_as_start = true`
* Assigning levels for external files are done in the same way for universal compaction and leveled compaction. The old behavior tends to assign files to L0 while the new behavior will assign the files to the lowest level possible.
### Bug Fixes
* Fix a longstanding race condition in SetOptions for `block_based_table_factory` options. The fix has some subtle behavior changes because of copying and replacing the TableFactory on a change with SetOptions, including requiring an Iterator::Refresh() for an existing Iterator to use the latest options.
* Fix under counting of allocated memory in the compressed secondary cache due to looking at the compressed block size rather than the actual memory allocated, which could be larger due to internal fragmentation.
* `GetApproximateMemTableStats()` could return disastrously bad estimates 5-25% of the time. The function has been re-engineered to return much better estimates with similar CPU cost.
* Skip insertion of compressed blocks in the secondary cache if the lowest_used_cache_tier DB option is kVolatileTier.
* Fix an issue in level compaction where a small CF with small compaction debt can cause the DB to allow parallel compactions. (#13054)
* Several DB option settings could be lost through `GetOptionsFromString()`, possibly elsewhere as well. Affected options, now fixed:`background_close_inactive_wals`, `write_dbid_to_manifest`, `write_identity_file`, `prefix_seek_opt_in_only`
## 9.7.0 (09/20/2024)
### New Features
* Make Cache a customizable class that can be instantiated by the object registry.
* Add new option `prefix_seek_opt_in_only` that makes iterators generally safer when you might set a `prefix_extractor`. When `prefix_seek_opt_in_only=true`, which is expected to be the future default, prefix seek is only used when `prefix_same_as_start` or `auto_prefix_mode` are set. Also, `prefix_same_as_start` and `auto_prefix_mode` now allow prefix filtering even with `total_order_seek=true`.
* Add a new table property "rocksdb.key.largest.seqno" which records the largest sequence number of all keys in file. It is verified to be zero during SST file ingestion.
### Behavior Changes
* Changed the semantics of the BlobDB configuration option `blob_garbage_collection_force_threshold` to define a threshold for the overall garbage ratio of all blob files currently eligible for garbage collection (according to `blob_garbage_collection_age_cutoff`). This can provide better control over space amplification at the cost of slightly higher write amplification.
* Set `write_dbid_to_manifest=true` by default. This means DB ID will now be preserved through backups, checkpoints, etc. by default. Also add `write_identity_file` option which can be set to false for anticipated future behavior.
* In FIFO compaction, compactions for changing file temperature (configured by option `file_temperature_age_thresholds`) will compact one file at a time, instead of merging multiple eligible file together (#13018).
* Support ingesting db generated files using hard link, i.e. IngestExternalFileOptions::move_files/link_files and IngestExternalFileOptions::allow_db_generated_files.
* Add a new file ingestion option `IngestExternalFileOptions::link_files` to hard link input files and preserve original files links after ingestion.
* DB::Close now untracks files in SstFileManager, making available any space used
by them. Prior to this change they would be orphaned until the DB is re-opened.
### Bug Fixes
* Fix a bug in CompactRange() where result files may not be compacted in any future compaction. This can only happen when users configure CompactRangeOptions::change_level to true and the change level step of manual compaction fails (#13009).
* Fix handling of dynamic change of `prefix_extractor` with memtable prefix filter. Previously, prefix seek could mix different prefix interpretations between memtable and SST files. Now the latest `prefix_extractor` at the time of iterator creation or refresh is respected.
* Fix a bug with manual_wal_flush and auto error recovery from WAL failure that may cause CFs to be inconsistent (#12995). The fix will set potential WAL write failure as fatal error when manual_wal_flush is true, and disables auto error recovery from these errors.
## 9.6.0 (08/19/2024)
### New Features
* Best efforts recovery supports recovering to incomplete Version with a clean seqno cut that presents a valid point in time view from the user's perspective, if versioning history doesn't include atomic flush.
* New option `BlockBasedTableOptions::decouple_partitioned_filters` should improve efficiency in serving read queries because filter and index partitions can consistently target the configured `metadata_block_size`. This option is currently opt-in.
* Introduce a new mutable CF option `paranoid_memory_checks`. It enables additional validation on data integrity during reads/scanning. Currently, skip list based memtable will validate key ordering during look up and scans.
### Public API Changes
* Add ticker stats to count file read retries due to checksum mismatch
* Adds optional installation callback function for remote compaction
### Behavior Changes
* There may be less intra-L0 compaction triggered by total L0 size being too small. We now use compensated file size (tombstones are assigned some value size) when calculating L0 size and reduce the threshold for L0 size limit. This is to avoid accumulating too much data/tombstones in L0.
### Bug Fixes
* Make DestroyDB supports slow deletion when it's configured in `SstFileManager`. The slow deletion is subject to the configured `rate_bytes_per_sec`, but not subject to the `max_trash_db_ratio`.
* Fixed a bug where we set unprep_seqs_ even when WriteImpl() fails. This was caught by stress test write fault injection in WriteImpl(). This may have incorrectly caused iteration creation failure for unvalidated writes or returned wrong result for WriteUnpreparedTxn::GetUnpreparedSequenceNumbers().
* Fixed a bug where successful write right after error recovery for last failed write finishes causes duplicate WAL entries
* Fixed a data race involving the background error status in `unordered_write` mode.
* Fix a bug where file snapshot functions like backup, checkpoint may attempt to copy a non-existing manifest file. #12882
* Fix a bug where per kv checksum corruption may be ignored in MultiGet().
* *Make DestroyDB supports slow deletion when it's configured in `SstFileManager`. The slow deletion is subject to the configured `rate_bytes_per_sec`, but not subject to the `max_trash_db_ratio`.
* Fix a race condition in pessimistic transactions that could allow multiple transactions with the same name to be registered simultaneously, resulting in a crash or other unpredictable behavior.
## 9.5.0 (07/19/2024)
### Public API Changes
* Introduced new C API function rocksdb_writebatch_iterate_cf for column family-aware iteration over the contents of a WriteBatch
* Add support to ingest SST files generated by a DB instead of SstFileWriter. This can be enabled with experimental option `IngestExternalFileOptions::allow_db_generated_files`.
### Behavior Changes
* When calculating total log size for the `log_size_for_flush` argument in `CreateCheckpoint` API, the size of the archived log will not be included to avoid unnecessary flush
## 9.3.1 (05/25/2024)
### Bug Fixes
* Fix a major bug in which an iterator using prefix filtering and SeekForPrev might miss data when the DB is using `whole_key_filtering=false` and `partition_filters=true`.
* Fixed a bug where `OnErrorRecoveryBegin()` is not called before auto recovery starts.
* Fixed a bug where event listener reads ErrorHandler's `bg_error_` member without holding db mutex(#12803).
* Fixed a bug in handling MANIFEST write error that caused the latest valid MANIFEST file to get deleted, resulting in the DB being unopenable.
* Fixed a race between error recovery due to manifest sync or write failure and external SST file ingestion. Both attempt to write a new manifest file, which causes an assertion failure.
### Performance Improvements
* Fix an issue where compactions were opening table files and reading table properties while holding db mutex_.
* Reduce unnecessary filesystem queries and DB mutex acquires in creating backups and checkpoints.
## 9.4.0 (06/23/2024)
### New Features
* Added a `CompactForTieringCollectorFactory` to auto trigger compaction for tiering use case.
* Optimistic transactions and pessimistic transactions with the WriteCommitted policy now support the `GetEntityForUpdate` API.
* Added a new "count" command to the ldb repl shell. By default, it prints a count of keys in the database from start to end. The options --from=<key> and/or --to=<key> can be specified to limit the range.
* Add `rocksdb_writebatch_update_timestamps`, `rocksdb_writebatch_wi_update_timestamps` in C API.
* Add `rocksdb_iter_refresh` in C API.
* Add `rocksdb_writebatch_create_with_params`, `rocksdb_writebatch_wi_create_with_params` to create WB and WBWI with all options in C API
### Public API Changes
* Deprecated names `LogFile` and `VectorLogPtr` in favor of new names `WalFile` and `VectorWalPtr`.
* Introduce a new universal compaction option CompactionOptionsUniversal::max_read_amp which allows user to define the limit on the number of sorted runs separately from the trigger for compaction (`level0_file_num_compaction_trigger`) #12477.
### Behavior Changes
* Inactive WALs are immediately closed upon being fully sync-ed rather than in a background thread. This is to ensure LinkFile() is not called on files still open for write, which might not be supported by some FileSystem implementations. This should not be a performance issue, but an opt-out is available with with new DB option `background_close_inactive_wals`.
### Bug Fixes
* Fix a rare case in which a hard-linked WAL in a Checkpoint is not fully synced (so might lose data on power loss).
* Fixed the output of the `ldb dump_wal` command for `PutEntity` records so it prints the key and correctly resets the hexadecimal formatting flag after printing the wide-column entity.
* Fixed an issue where `PutEntity` records were handled incorrectly while rebuilding transactions during recovery.
* Various read operations could ignore various ReadOptions that might be relevant. Fixed many such cases, which can result in behavior change but a better reflection of specified options.
### Performance Improvements
* Improved write throughput to memtable when there's a large number of concurrent writers and allow_concurrent_memtable_write=true(#12545)
* [internal only] Build script improvement
## 9.3.0 (05/17/2024)
### New Features
@@ -428,7 +41,7 @@ by them. Prior to this change they would be orphaned until the DB is re-opened.
* Added a new API `GetEntityFromBatchAndDB` to `WriteBatchWithIndex` that can be used for wide-column point lookups with read-your-own-writes consistency. Similarly to `GetFromBatchAndDB`, the API can combine data from the write batch with data from the underlying database if needed. See the API comments for more details.
* [Experimental] Introduce two new cross-column-family iterators - CoalescingIterator and AttributeGroupIterator. The CoalescingIterator enables users to iterate over multiple column families and access their values and columns. During this iteration, if the same key exists in more than one column family, the keys in the later column family will overshadow the previous ones. The AttributeGroupIterator allows users to gather wide columns per Column Family and create attribute groups while iterating over keys across all CFs.
* Added a new API `MultiGetEntityFromBatchAndDB` to `WriteBatchWithIndex` that can be used for batched wide-column point lookups with read-your-own-writes consistency. Similarly to `MultiGetFromBatchAndDB`, the API can combine data from the write batch with data from the underlying database if needed. See the API comments for more details.
* Adds a `SstFileReader::NewTableIterator` API to support programmatically read a SST file as a raw table file.
* *Adds a `SstFileReader::NewTableIterator` API to support programmatically read a SST file as a raw table file.
* Add an option to `WaitForCompactOptions` - `wait_for_purge` to make `WaitForCompact()` API wait for background purge to complete
### Public API Changes
@@ -457,24 +70,24 @@ the whole DB to be dropped right after migration if the migrated data is larger
* Fixed a bug where wrong padded bytes are used to generate file checksum and `DataVerificationInfo::checksum` upon file creation
* Correctly implemented the move semantics of `PinnableWideColumns`.
* Fixed a bug when the recycle_log_file_num in DBOptions is changed from 0 to non-zero when a DB is reopened. On a subsequent reopen, if a log file created when recycle_log_file_num==0 was reused previously, is alive and is empty, we could end up inserting stale WAL records into the memtable.
* Fix a bug where obsolete files' deletion during DB::Open are not rate limited with `SstFilemManager`'s slow deletion feature even if it's configured.
* *Fix a bug where obsolete files' deletion during DB::Open are not rate limited with `SstFilemManager`'s slow deletion feature even if it's configured.
## 9.1.0 (03/22/2024)
### New Features
* Added an option, `GetMergeOperandsOptions::continue_cb`, to give users the ability to end `GetMergeOperands()`'s lookup process before all merge operands were found.
* Add sanity checks for ingesting external files that currently checks if the user key comparator used to create the file is compatible with the column family's user key comparator.
* *Add sanity checks for ingesting external files that currently checks if the user key comparator used to create the file is compatible with the column family's user key comparator.
*Support ingesting external files for column family that has user-defined timestamps in memtable only enabled.
* On file systems that support storage level data checksum and reconstruction, retry SST block reads for point lookups, scans, and flush and compaction if there's a checksum mismatch on the initial read.
* Some enhancements and fixes to experimental Temperature handling features, including new `default_write_temperature` CF option and opening an `SstFileWriter` with a temperature.
* `WriteBatchWithIndex` now supports wide-column point lookups via the `GetEntityFromBatch` API. See the API comments for more details.
* Implement experimental features: API `Iterator::GetProperty("rocksdb.iterator.write-time")` to allow users to get data's approximate write unix time and write data with a specific write time via `WriteBatch::TimedPut` API.
* *Implement experimental features: API `Iterator::GetProperty("rocksdb.iterator.write-time")` to allow users to get data's approximate write unix time and write data with a specific write time via `WriteBatch::TimedPut` API.
### Public API Changes
* Best-effort recovery (`best_efforts_recovery == true`) may now be used together with atomic flush (`atomic_flush == true`). The all-or-nothing recovery guarantee for atomically flushed data will be upheld.
* Remove deprecated option `bottommost_temperature`, already replaced by `last_level_temperature`
* Added new PerfContext counters for block cache bytes read - block_cache_index_read_byte, block_cache_filter_read_byte, block_cache_compression_dict_read_byte, and block_cache_read_byte.
* Deprecate experimental Remote Compaction APIs - StartV2() and WaitForCompleteV2() and introduce Schedule() and Wait(). The new APIs essentially does the same thing as the old APIs. They allow taking externally generated unique id to wait for remote compaction to complete.
* For API `WriteCommittedTransaction::GetForUpdate`, if the column family enables user-defined timestamp, it was mandated that argument `do_validate` cannot be false, and UDT based validation has to be done with a user set read timestamp. It's updated to make the UDT based validation optional if user sets `do_validate` to false and does not set a read timestamp. With this, `GetForUpdate` skips UDT based validation and it's users' responsibility to enforce the UDT invariant. SO DO NOT skip this UDT-based validation if users do not have ways to enforce the UDT invariant. Ways to enforce the invariant on the users side include manage a monotonically increasing timestamp, commit transactions in a single thread etc.
* *For API `WriteCommittedTransaction::GetForUpdate`, if the column family enables user-defined timestamp, it was mandated that argument `do_validate` cannot be false, and UDT based validation has to be done with a user set read timestamp. It's updated to make the UDT based validation optional if user sets `do_validate` to false and does not set a read timestamp. With this, `GetForUpdate` skips UDT based validation and it's users' responsibility to enforce the UDT invariant. SO DO NOT skip this UDT-based validation if users do not have ways to enforce the UDT invariant. Ways to enforce the invariant on the users side include manage a monotonically increasing timestamp, commit transactions in a single thread etc.
* Defined a new PerfLevel `kEnableWait` to measure time spent by user threads blocked in RocksDB other than mutex, such as a write thread waiting to be added to a write group, a write thread delayed or stalled etc.
* `RateLimiter`'s API no longer requires the burst size to be the refill size. Users of `NewGenericRateLimiter()` can now provide burst size in `single_burst_bytes`. Implementors of `RateLimiter::SetSingleBurstBytes()` need to adapt their implementations to match the changed API doc.
* Add `write_memtable_time` to the newly introduced PerfLevel `kEnableWait`.
@@ -507,7 +120,7 @@ MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 1024 thrpt 25 76
## 9.0.0 (02/16/2024)
### New Features
* Provide support for FSBuffer for point lookups. Also added support for scans and compactions that don't go through prefetching.
* Make `SstFileWriter` create SST files without persisting user defined timestamps when the `Option.persist_user_defined_timestamps` flag is set to false.
* *Make `SstFileWriter` create SST files without persisting user defined timestamps when the `Option.persist_user_defined_timestamps` flag is set to false.
* Add support for user-defined timestamps in APIs `DeleteFilesInRanges` and `GetPropertiesOfTablesInRange`.
* Mark wal\_compression feature as production-ready. Currently only compatible with ZSTD compression.
@@ -517,9 +130,9 @@ MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 1024 thrpt 25 76
* Exposed mode option to Rate Limiter via c api.
* Removed deprecated option `access_hint_on_compaction_start`
* Removed deprecated option `ColumnFamilyOptions::check_flush_compaction_key_order`
* Remove the default `WritableFile::GetFileSize` and `FSWritableFile::GetFileSize` implementation that returns 0 and make it pure virtual, so that subclasses are enforced to explicitly provide an implementation.
* *Remove the default `WritableFile::GetFileSize` and `FSWritableFile::GetFileSize` implementation that returns 0 and make it pure virtual, so that subclasses are enforced to explicitly provide an implementation.
* Removed deprecated option `ColumnFamilyOptions::level_compaction_dynamic_file_size`
* Removed tickers with typos "rocksdb.error.handler.bg.error.count", "rocksdb.error.handler.bg.io.error.count", "rocksdb.error.handler.bg.retryable.io.error.count".
* *Removed tickers with typos "rocksdb.error.handler.bg.errro.count", "rocksdb.error.handler.bg.io.errro.count", "rocksdb.error.handler.bg.retryable.io.errro.count".
* Remove the force mode for `EnableFileDeletions` API because it is unsafe with no known legitimate use.
* Removed deprecated option `ColumnFamilyOptions::ignore_max_compaction_bytes_for_input`
* `sst_dump --command=check` now compares the number of records in a table with `num_entries` in table property, and reports corruption if there is a mismatch. API `SstFileDumper::ReadSequential()` is updated to optionally do this verification. (#12322)
@@ -546,7 +159,7 @@ MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 1024 thrpt 25 76
* 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 explicitly flushing blob file.
* `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.
@@ -611,7 +224,7 @@ want to continue to use force enabling, they need to explicitly pass a `true` to
### 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 occurred previously in its status message.
* 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.
@@ -1399,7 +1012,7 @@ Note: The next release will be major release 7.0. See https://github.com/faceboo
### Public API change
* Extend WriteBatch::AssignTimestamp and AssignTimestamps API so that both functions can accept an optional `checker` argument that performs additional checking on timestamp sizes.
* Introduce a new EventListener callback that will be called upon the end of automatic error recovery.
* Add IncreaseFullHistoryTsLow API so users can advance each column family's full_history_ts_low separately.
* Add IncreaseFullHistoryTsLow API so users can advance each column family's full_history_ts_low seperately.
* Add GetFullHistoryTsLow API so users can query current full_history_low value of specified column family.
### Performance Improvements
+5 -5
View File
@@ -6,7 +6,7 @@ than release mode.
RocksDB's library should be able to compile without any dependency installed,
although we recommend installing some compression libraries (see below).
We do depend on newer gcc/clang with C++20 support (GCC >= 11, Clang >= 10).
We do depend on newer gcc/clang with C++17 support (GCC >= 7, Clang >= 5).
There are few options when compiling RocksDB:
@@ -60,7 +60,7 @@ most processors made since roughly 2013.
## Supported platforms
* **Linux - Ubuntu**
* Upgrade your gcc to version at least 11 to get C++20 support.
* Upgrade your gcc to version at least 7 to get C++17 support.
* Install gflags. First, try: `sudo apt-get install libgflags-dev`
If this doesn't work and you're using Ubuntu, here's a nice tutorial:
(http://askubuntu.com/questions/312173/installing-gflags-12-04)
@@ -72,7 +72,7 @@ most processors made since roughly 2013.
* Install zstandard: `sudo apt-get install libzstd-dev`.
* **Linux - CentOS / RHEL**
* Upgrade your gcc to version at least 11 to get C++20 support
* Upgrade your gcc to version at least 7 to get C++17 support
* Install gflags:
git clone https://github.com/gflags/gflags.git
@@ -122,7 +122,7 @@ most processors made since roughly 2013.
make && sudo make install
* **OS X**:
* Install latest C++ compiler that supports C++20:
* Install latest C++ compiler that supports C++ 17:
* Update XCode: run `xcode-select --install` (or install it from XCode App's settting).
* Install via [homebrew](http://brew.sh/).
* If you're first time developer in MacOS, you still need to run: `xcode-select --install` in your command line.
@@ -213,7 +213,7 @@ most processors made since roughly 2013.
export PATH=/opt/freeware/bin:$PATH
* **Solaris Sparc**
* Install GCC 11 and higher.
* Install GCC 7 and higher.
* Use these environment variables:
export CC=gcc
+1 -2
View File
@@ -2,8 +2,7 @@ This is the list of all known third-party language bindings for RocksDB. If some
* Java - https://github.com/facebook/rocksdb/tree/main/java
* Python
* https://github.com/rocksdict/RocksDict
* http://python-rocksdb.readthedocs.io/en/latest/ (unmaintained)
* http://python-rocksdb.readthedocs.io/en/latest/
* http://pyrocksdb.readthedocs.org/en/latest/ (unmaintained)
* Perl - https://metacpan.org/pod/RocksDB
* Node.js - https://npmjs.org/package/rocksdb
+136 -96
View File
@@ -103,7 +103,6 @@ dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
export LIB_MODE="$(LIB_MODE)"; \
export ROCKSDB_CXX_STANDARD="$(ROCKSDB_CXX_STANDARD)"; \
export USE_FOLLY="$(USE_FOLLY)"; \
export USE_FOLLY_LITE="$(USE_FOLLY_LITE)"; \
"$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
# this file is generated by the previous line to set build flags and sources
include make_config.mk
@@ -148,8 +147,10 @@ ifeq ($(USE_COROUTINES), 1)
USE_FOLLY = 1
# glog/logging.h requires HAVE_CXX11_ATOMIC
OPT += -DUSE_COROUTINES -DHAVE_CXX11_ATOMIC
ROCKSDB_CXX_STANDARD = c++2a
USE_RTTI = 1
ifneq ($(USE_CLANG), 1)
ROCKSDB_CXX_STANDARD = c++20
PLATFORM_CXXFLAGS += -fcoroutines
endif
endif
@@ -296,28 +297,6 @@ $(info $(shell $(CC) --version))
$(info $(shell $(CXX) --version))
endif
# ccache support
# Set USE_CCACHE=1 to enable ccache, or let it auto-detect
ifndef USE_CCACHE
CCACHE := $(shell which ccache 2>/dev/null)
ifneq ($(CCACHE),)
USE_CCACHE := 1
else
USE_CCACHE := 0
endif
endif
ifeq ($(USE_CCACHE), 1)
CCACHE := $(shell which ccache 2>/dev/null)
ifneq ($(CCACHE),)
$(info Using ccache: $(CCACHE))
CC := $(CCACHE) $(CC)
CXX := $(CCACHE) $(CXX)
else
$(warning ccache requested but not found in PATH)
endif
endif
missing_make_config_paths := $(shell \
grep "\./\S*\|/\S*" -o $(CURDIR)/make_config.mk | \
while read path; \
@@ -468,7 +447,72 @@ else
PLATFORM_CXXFLAGS += -isystem $(GTEST_DIR)
endif
include folly.mk
# This provides a Makefile simulation of a Meta-internal folly integration.
# It is not validated for general use.
#
# USE_FOLLY links the build targets with libfolly.a. The latter could be
# built using 'make build_folly', or built externally and specified in
# the CXXFLAGS and EXTRA_LDFLAGS env variables. The build_detect_platform
# script tries to detect if an external folly dependency has been specified.
# If not, it exports FOLLY_PATH to the path of the installed Folly and
# dependency libraries.
#
# USE_FOLLY_LITE cherry picks source files from Folly to include in the
# RocksDB library. Its faster and has fewer dependencies on 3rd party
# libraries, but with limited functionality. For example, coroutine
# functionality is not available.
ifeq ($(USE_FOLLY),1)
ifeq ($(USE_FOLLY_LITE),1)
$(error Please specify only one of USE_FOLLY and USE_FOLLY_LITE)
endif
ifneq ($(strip $(FOLLY_PATH)),)
BOOST_PATH = $(shell (ls -d $(FOLLY_PATH)/../boost*))
DBL_CONV_PATH = $(shell (ls -d $(FOLLY_PATH)/../double-conversion*))
GFLAGS_PATH = $(shell (ls -d $(FOLLY_PATH)/../gflags*))
GLOG_PATH = $(shell (ls -d $(FOLLY_PATH)/../glog*))
LIBEVENT_PATH = $(shell (ls -d $(FOLLY_PATH)/../libevent*))
XZ_PATH = $(shell (ls -d $(FOLLY_PATH)/../xz*))
LIBSODIUM_PATH = $(shell (ls -d $(FOLLY_PATH)/../libsodium*))
FMT_PATH = $(shell (ls -d $(FOLLY_PATH)/../fmt*))
# For some reason, glog and fmt libraries are under either lib or lib64
GLOG_LIB_PATH = $(shell (ls -d $(GLOG_PATH)/lib*))
FMT_LIB_PATH = $(shell (ls -d $(FMT_PATH)/lib*))
# AIX: pre-defined system headers are surrounded by an extern "C" block
ifeq ($(PLATFORM), OS_AIX)
PLATFORM_CCFLAGS += -I$(BOOST_PATH)/include -I$(DBL_CONV_PATH)/include -I$(GLOG_PATH)/include -I$(LIBEVENT_PATH)/include -I$(XZ_PATH)/include -I$(LIBSODIUM_PATH)/include -I$(FOLLY_PATH)/include -I$(FMT_PATH)/include
PLATFORM_CXXFLAGS += -I$(BOOST_PATH)/include -I$(DBL_CONV_PATH)/include -I$(GLOG_PATH)/include -I$(LIBEVENT_PATH)/include -I$(XZ_PATH)/include -I$(LIBSODIUM_PATH)/include -I$(FOLLY_PATH)/include -I$(FMT_PATH)/include
else
PLATFORM_CCFLAGS += -isystem $(BOOST_PATH)/include -isystem $(DBL_CONV_PATH)/include -isystem $(GLOG_PATH)/include -isystem $(LIBEVENT_PATH)/include -isystem $(XZ_PATH)/include -isystem $(LIBSODIUM_PATH)/include -isystem $(FOLLY_PATH)/include -isystem $(FMT_PATH)/include
PLATFORM_CXXFLAGS += -isystem $(BOOST_PATH)/include -isystem $(DBL_CONV_PATH)/include -isystem $(GLOG_PATH)/include -isystem $(LIBEVENT_PATH)/include -isystem $(XZ_PATH)/include -isystem $(LIBSODIUM_PATH)/include -isystem $(FOLLY_PATH)/include -isystem $(FMT_PATH)/include
endif
# Add -ldl at the end as gcc resolves a symbol in a library by searching only in libraries specified later
# in the command line
PLATFORM_LDFLAGS += $(FOLLY_PATH)/lib/libfolly.a $(BOOST_PATH)/lib/libboost_context.a $(BOOST_PATH)/lib/libboost_filesystem.a $(BOOST_PATH)/lib/libboost_atomic.a $(BOOST_PATH)/lib/libboost_program_options.a $(BOOST_PATH)/lib/libboost_regex.a $(BOOST_PATH)/lib/libboost_system.a $(BOOST_PATH)/lib/libboost_thread.a $(DBL_CONV_PATH)/lib/libdouble-conversion.a $(FMT_LIB_PATH)/libfmt.a $(GLOG_LIB_PATH)/libglog.so $(GFLAGS_PATH)/lib/libgflags.so.2.2 $(LIBEVENT_PATH)/lib/libevent-2.1.so -ldl
PLATFORM_LDFLAGS += -Wl,-rpath=$(GFLAGS_PATH)/lib -Wl,-rpath=$(GLOG_LIB_PATH) -Wl,-rpath=$(LIBEVENT_PATH)/lib -Wl,-rpath=$(LIBSODIUM_PATH)/lib -Wl,-rpath=$(LIBEVENT_PATH)/lib
endif
PLATFORM_CCFLAGS += -DUSE_FOLLY -DFOLLY_NO_CONFIG
PLATFORM_CXXFLAGS += -DUSE_FOLLY -DFOLLY_NO_CONFIG
endif
ifeq ($(USE_FOLLY_LITE),1)
# Path to the Folly source code and include files
FOLLY_DIR = ./third-party/folly
# AIX: pre-defined system headers are surrounded by an extern "C" block
ifeq ($(PLATFORM), OS_AIX)
PLATFORM_CCFLAGS += -I$(FOLLY_DIR)
PLATFORM_CXXFLAGS += -I$(FOLLY_DIR)
else
PLATFORM_CCFLAGS += -isystem $(FOLLY_DIR)
PLATFORM_CXXFLAGS += -isystem $(FOLLY_DIR)
endif
PLATFORM_CCFLAGS += -DUSE_FOLLY -DFOLLY_NO_CONFIG
PLATFORM_CXXFLAGS += -DUSE_FOLLY -DFOLLY_NO_CONFIG
# TODO: fix linking with fbcode compiler config
PLATFORM_LDFLAGS += -lglog
endif
ifdef TEST_CACHE_LINE_SIZE
PLATFORM_CCFLAGS += -DTEST_CACHE_LINE_SIZE=$(TEST_CACHE_LINE_SIZE)
@@ -574,22 +618,16 @@ VALGRIND_VER := $(join $(VALGRIND_VER),valgrind)
VALGRIND_OPTS = --error-exitcode=$(VALGRIND_ERROR) --leak-check=full
# Not yet supported: --show-leak-kinds=definite,possible,reachable --errors-for-leak-kinds=definite,possible,reachable
# Work around valgrind hanging on systems with limited internet access
ifneq ($(shell which git 2>/dev/null && git config --get https.proxy),)
export DEBUGINFOD_URLS=
endif
TEST_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(TEST_LIB_SOURCES) $(MOCK_LIB_SOURCES)) $(GTEST)
BENCH_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(BENCH_LIB_SOURCES))
CACHE_BENCH_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(CACHE_BENCH_LIB_SOURCES))
POINT_LOCK_BENCH_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(POINT_LOCK_BENCH_LIB_SOURCES))
TOOL_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(TOOL_LIB_SOURCES))
ANALYZE_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(ANALYZER_LIB_SOURCES))
STRESS_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(STRESS_LIB_SOURCES))
# Exclude build_version.cc -- a generated source file -- from all sources. Not needed for dependencies
ALL_SOURCES = $(filter-out util/build_version.cc, $(LIB_SOURCES)) $(TEST_LIB_SOURCES) $(MOCK_LIB_SOURCES) $(GTEST_DIR)/gtest/gtest-all.cc
ALL_SOURCES += $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(CACHE_BENCH_LIB_SOURCES) $(POINT_LOCK_BENCH_LIB_SOURCES) $(ANALYZER_LIB_SOURCES) $(STRESS_LIB_SOURCES)
ALL_SOURCES += $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(CACHE_BENCH_LIB_SOURCES) $(ANALYZER_LIB_SOURCES) $(STRESS_LIB_SOURCES)
ALL_SOURCES += $(TEST_MAIN_SOURCES) $(TOOL_MAIN_SOURCES) $(BENCH_MAIN_SOURCES)
ALL_SOURCES += $(ROCKSDB_PLUGIN_SOURCES) $(ROCKSDB_PLUGIN_TESTS)
@@ -598,38 +636,26 @@ TESTS = $(patsubst %.cc, %, $(notdir $(TEST_MAIN_SOURCES)))
TESTS += $(patsubst %.c, %, $(notdir $(TEST_MAIN_SOURCES_C)))
TESTS += $(PLUGIN_TESTS)
# `make check-headers` to verify that each header file includes its own deps
# and that public headers do not depend on internal headers
# `make check-headers` to very that each header file includes its own
# dependencies
ifneq ($(filter check-headers, $(MAKECMDGOALS)),)
# TODO: add/support JNI headers
DEV_HEADER_DIRS := $(sort include/ $(dir $(ALL_SOURCES)))
# Some headers like in port/ are platform-specific
DEV_HEADERS_TO_CHECK := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|lua/|range_tree/|secondary_index/')
PUBLIC_HEADERS_TO_CHECK := $(shell $(FIND) include/ -type f -name '*.h' | grep -E -v 'lua/')
DEV_HEADERS := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|lua/|range_tree/')
else
DEV_HEADERS_TO_CHECK :=
PUBLIC_HEADERS_TO_CHECK :=
DEV_HEADERS :=
endif
HEADER_OK_FILES = $(patsubst %.h, %.h.ok, $(DEV_HEADERS_TO_CHECK)) \
$(patsubst %.h, %.h.pub, $(PUBLIC_HEADERS_TO_CHECK))
HEADER_OK_FILES = $(patsubst %.h, %.h.ok, $(DEV_HEADERS))
AM_V_CCH = $(am__v_CCH_$(V))
am__v_CCH_ = $(am__v_CCH_$(AM_DEFAULT_VERBOSITY))
am__v_CCH_0 = @echo " CC.h " $<;
am__v_CCH_1 =
# verify headers include their own dependencies, under dev build settings
%.h.ok: %.h # .h.ok not actually created, so re-checked on each invocation
# -DROCKSDB_NAMESPACE=42 ensures the namespace header is included
$(AM_V_CCH) echo '#include "$<"' | $(CXX) $(CXXFLAGS) \
-DROCKSDB_NAMESPACE=42 -x c++ -c - -o /dev/null
# verify public headers do not depend on internal headers, under typical
# user build settings
%.h.pub: %.h # .h.pub not actually created, so re-checked on each invocation
$(AM_V_CCH) cd include/ && echo '#include "$(patsubst include/%,%,$<)"' | \
$(CXX) -std=$(or $(ROCKSDB_CXX_STANDARD),c++20) -I. -DROCKSDB_NAMESPACE=42 -x c++ -c - -o /dev/null
build_tools/check-public-header.sh $<
$(AM_V_CCH) echo '#include "$<"' | $(CXX) $(CXXFLAGS) -DROCKSDB_NAMESPACE=42 -x c++ -c - -o /dev/null
check-headers: $(HEADER_OK_FILES)
@@ -1115,16 +1141,16 @@ ubsan_crash_test_with_best_efforts_recovery: clean
$(MAKE) clean
full_valgrind_test:
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 PORTABLE=1 $(MAKE) valgrind_check
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
full_valgrind_test_some:
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 PORTABLE=1 $(MAKE) valgrind_check_some
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check_some
valgrind_test:
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 PORTABLE=1 $(MAKE) valgrind_check
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
valgrind_test_some:
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 PORTABLE=1 $(MAKE) valgrind_check_some
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check_some
valgrind_check: $(TESTS)
$(MAKE) DRIVER="$(VALGRIND_VER) $(VALGRIND_OPTS)" gen_parallel_tests
@@ -1291,9 +1317,6 @@ block_cache_trace_analyzer: $(OBJ_DIR)/tools/block_cache_analyzer/block_cache_tr
cache_bench: $(OBJ_DIR)/cache/cache_bench.o $(CACHE_BENCH_OBJECTS) $(LIBRARY)
$(AM_LINK)
point_lock_bench: $(OBJ_DIR)/utilities/transactions/lock/point/point_lock_bench.o $(POINT_LOCK_BENCH_OBJECTS) $(LIBRARY)
$(AM_LINK)
persistent_cache_bench: $(OBJ_DIR)/utilities/persistent_cache/persistent_cache_bench.o $(LIBRARY)
$(AM_LINK)
@@ -1306,9 +1329,6 @@ filter_bench: $(OBJ_DIR)/util/filter_bench.o $(LIBRARY)
db_stress: $(OBJ_DIR)/db_stress_tool/db_stress.o $(STRESS_LIBRARY) $(TOOLS_LIBRARY) $(LIBRARY)
$(AM_LINK)
db_stress_compression_manager: $(OBJ_DIR)/db_stress_tool/db_stress_compression_manager.o $(LIBRARY)
$(AM_LINK)
write_stress: $(OBJ_DIR)/tools/write_stress.o $(LIBRARY)
$(AM_LINK)
@@ -1374,13 +1394,13 @@ agg_merge_test: $(OBJ_DIR)/utilities/agg_merge/agg_merge_test.o $(TEST_LIBRARY)
stringappend_test: $(OBJ_DIR)/utilities/merge_operators/string_append/stringappend_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
cassandra_format_test: $(OBJ_DIR)/utilities/cassandra/cassandra_format_test.o $(TEST_LIBRARY) $(LIBRARY)
cassandra_format_test: $(OBJ_DIR)/utilities/cassandra/cassandra_format_test.o $(OBJ_DIR)/utilities/cassandra/test_utils.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
cassandra_functional_test: $(OBJ_DIR)/utilities/cassandra/cassandra_functional_test.o $(TEST_LIBRARY) $(LIBRARY)
cassandra_functional_test: $(OBJ_DIR)/utilities/cassandra/cassandra_functional_test.o $(OBJ_DIR)/utilities/cassandra/test_utils.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
cassandra_row_merge_test: $(OBJ_DIR)/utilities/cassandra/cassandra_row_merge_test.o $(TEST_LIBRARY) $(LIBRARY)
cassandra_row_merge_test: $(OBJ_DIR)/utilities/cassandra/cassandra_row_merge_test.o $(OBJ_DIR)/utilities/cassandra/test_utils.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
cassandra_serialize_test: $(OBJ_DIR)/utilities/cassandra/cassandra_serialize_test.o $(TEST_LIBRARY) $(LIBRARY)
@@ -1443,12 +1463,6 @@ db_test: $(OBJ_DIR)/db/db_test.o $(TEST_LIBRARY) $(LIBRARY)
db_test2: $(OBJ_DIR)/db/db_test2.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
db_etc3_test: $(OBJ_DIR)/db/db_etc3_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
compression_test: $(OBJ_DIR)/util/compression_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
db_logical_block_size_cache_test: $(OBJ_DIR)/db/db_logical_block_size_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
@@ -1617,9 +1631,6 @@ compaction_job_stats_test: $(OBJ_DIR)/db/compaction/compaction_job_stats_test.o
compaction_service_test: $(OBJ_DIR)/db/compaction/compaction_service_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
compact_for_tiering_collector_test: $(OBJ_DIR)/utilities/table_properties_collectors/compact_for_tiering_collector_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
compact_on_deletion_collector_test: $(OBJ_DIR)/utilities/table_properties_collectors/compact_on_deletion_collector_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
@@ -1833,9 +1844,6 @@ heap_test: $(OBJ_DIR)/util/heap_test.o $(TEST_LIBRARY) $(LIBRARY)
point_lock_manager_test: utilities/transactions/lock/point/point_lock_manager_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
point_lock_manager_stress_test: utilities/transactions/lock/point/point_lock_manager_stress_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
transaction_test: $(OBJ_DIR)/utilities/transactions/transaction_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
@@ -1995,9 +2003,6 @@ wide_column_serialization_test: $(OBJ_DIR)/db/wide/wide_column_serialization_tes
wide_columns_helper_test: $(OBJ_DIR)/db/wide/wide_columns_helper_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
interval_test: $(OBJ_DIR)/util/interval_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
#-------------------------------------------------
# make install related stuff
PREFIX ?= /usr/local
@@ -2108,14 +2113,14 @@ ZLIB_DOWNLOAD_BASE ?= http://zlib.net
BZIP2_VER ?= 1.0.8
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
SNAPPY_VER ?= 1.2.2
SNAPPY_SHA256 ?= 90f74bc1fbf78a6c56b3c4a082a05103b3a56bb17bca1a27e052ea11723292dc
SNAPPY_VER ?= 1.1.8
SNAPPY_SHA256 ?= 16b677f07832a612b0836178db7f374e414f94657c138e6993cbfc5dcc58651f
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
LZ4_VER ?= 1.10.0
LZ4_SHA256 ?= 537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b
LZ4_VER ?= 1.9.4
LZ4_SHA256 ?= 0b0e3aa07c8c063ddf40b082bdf7e37a1562bda40a0ff5272957f3e987e0e54b
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
ZSTD_VER ?= 1.5.7
ZSTD_SHA256 ?= 37d7284556b20954e56e1ca85b80226768902e2edabd3b649e9e72c0c9012ee3
ZSTD_VER ?= 1.5.5
ZSTD_SHA256 ?= 98e9c3d949d1b924e28e01eccb7deed865eefebf25c2f21c702e5cd5b63b85e1
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
CURL_SSL_OPTS ?= --tlsv1
@@ -2206,7 +2211,7 @@ libsnappy.a: snappy-$(SNAPPY_VER).tar.gz
-rm -rf snappy-$(SNAPPY_VER)
tar xvzf snappy-$(SNAPPY_VER).tar.gz
mkdir snappy-$(SNAPPY_VER)/build
cd snappy-$(SNAPPY_VER)/build && CFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DSNAPPY_BUILD_BENCHMARKS=OFF -DSNAPPY_BUILD_TESTS=OFF ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
cd snappy-$(SNAPPY_VER)/build && CFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
cp snappy-$(SNAPPY_VER)/build/libsnappy.a .
lz4-$(LZ4_VER).tar.gz:
@@ -2336,27 +2341,27 @@ rocksdbjavastaticreleasedocker: rocksdbjavastaticosx rocksdbjavastaticdockerx86
rocksdbjavastaticdockerx86:
mkdir -p java/target
docker run --rm --name rocksdb_linux_x86-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_x86-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_x86-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerx86_64:
mkdir -p java/target
docker run --rm --name rocksdb_linux_x64-be --platform linux/amd64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_x64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerppc64le:
mkdir -p java/target
docker run --rm --name rocksdb_linux_ppc64le-be --platform linux/ppc64le --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_ppc64le-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerarm64v8:
mkdir -p java/target
docker run --rm --name rocksdb_linux_arm64v8-be --platform linux/aarch64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_arm64v8-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockers390x:
mkdir -p java/target
docker run --rm --name rocksdb_linux_s390x-be --platform linux/s390x --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_s390x-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerriscv64:
mkdir -p java/target
docker run --rm --name rocksdb_linux_riscv64-be --platform linux/riscv64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_riscv64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerx86musl:
mkdir -p java/target
@@ -2364,19 +2369,19 @@ rocksdbjavastaticdockerx86musl:
rocksdbjavastaticdockerx86_64musl:
mkdir -p java/target
docker run --rm --name rocksdb_linux_x64-musl-be --platform linux/amd64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_x64-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerppc64lemusl:
mkdir -p java/target
docker run --rm --name rocksdb_linux_ppc64le-musl-be --platform linux/ppc64le --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_ppc64le-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerarm64v8musl:
mkdir -p java/target
docker run --rm --name rocksdb_linux_arm64v8-musl-be --platform linux/aarch64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_arm64v8-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockers390xmusl:
mkdir -p java/target
docker run --rm --name rocksdb_linux_s390x-musl-be --platform linux/s390x --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_s390x-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentral
@@ -2431,8 +2436,8 @@ jtest_run:
jtest: rocksdbjava
cd java;$(MAKE) sample test
jpmd: rocksdbjavageneratepom
cd java;$(MAKE) java java_test pmd
jpmd: rocksdbjava rocksdbjavageneratepom
cd java;$(MAKE) pmd
jdb_bench:
cd java;$(MAKE) db_bench;
@@ -2442,6 +2447,41 @@ commit_prereq:
false # J=$(J) build_tools/precommit_checker.py unit clang_unit release clang_release tsan asan ubsan lite unit_non_shm
# $(MAKE) clean && $(MAKE) jclean && $(MAKE) rocksdbjava;
# For public CI runs, checkout folly in a way that can build with RocksDB.
# This is mostly intended as a test-only simulation of Meta-internal folly
# integration.
checkout_folly:
if [ -e third-party/folly ]; then \
cd third-party/folly && ${GIT_COMMAND} fetch origin; \
else \
cd third-party && ${GIT_COMMAND} clone https://github.com/facebook/folly.git; \
fi
@# Pin to a particular version for public CI, so that PR authors don't
@# need to worry about folly breaking our integration. Update periodically
cd third-party/folly && git reset --hard beacd86d63cd71c904632262e6c36f60874d78ba
@# A hack to remove boost dependency.
@# NOTE: this hack is only needed if building using USE_FOLLY_LITE
perl -pi -e 's/^(#include <boost)/\/\/$$1/' third-party/folly/folly/functional/Invoke.h
@# NOTE: this hack is required for clang in some cases
perl -pi -e 's/int rv = syscall/int rv = (int)syscall/' third-party/folly/folly/detail/Futex.cpp
@# NOTE: this hack is required for gcc in some cases
perl -pi -e 's/(__has_include.<experimental.memory_resource>.)/__cpp_rtti && $$1/' third-party/folly/folly/memory/MemoryResource.h
CXX_M_FLAGS = $(filter -m%, $(CXXFLAGS))
build_folly:
FOLLY_INST_PATH=`cd third-party/folly; $(PYTHON) build/fbcode_builder/getdeps.py show-inst-dir`; \
if [ "$$FOLLY_INST_PATH" ]; then \
rm -rf $${FOLLY_INST_PATH}/../../*; \
else \
echo "Please run checkout_folly first"; \
false; \
fi
# Restore the original version of Invoke.h with boost dependency
cd third-party/folly && ${GIT_COMMAND} checkout folly/functional/Invoke.h
cd third-party/folly && \
CXXFLAGS=" $(CXX_M_FLAGS) -DHAVE_CXX11_ATOMIC " $(PYTHON) build/fbcode_builder/getdeps.py build --no-tests
# ---------------------------------------------------------------------------
# Build size testing
# ---------------------------------------------------------------------------
+10 -88
View File
@@ -1,14 +1,12 @@
# This file @generated by:
#$ python3 buckifier/buckify_rocksdb.py
# --> DO NOT EDIT MANUALLY <--
# This file is a Meta-specific integration for buck builds, so can
# only be validated by Meta employees.
# This file is a Facebook-specific integration for buck builds, so can
# only be validated by Facebook employees.
load("//rocks/buckifier:defs.bzl", "cpp_library_wrapper","rocks_cpp_library_wrapper","cpp_binary_wrapper","cpp_unittest_wrapper","fancy_bench_wrapper","add_c_test_wrapper")
load("@fbcode_macros//build_defs:export_files.bzl", "export_file")
oncall("rocksdb_point_of_contact")
cpp_library_wrapper(name="rocksdb_lib", srcs=[
"cache/cache.cc",
"cache/cache_entry_roles.cc",
@@ -85,12 +83,10 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"db/log_writer.cc",
"db/logs_with_prep_tracker.cc",
"db/malloc_stats.cc",
"db/manifest_ops.cc",
"db/memtable.cc",
"db/memtable_list.cc",
"db/merge_helper.cc",
"db/merge_operator.cc",
"db/multi_scan.cc",
"db/output_validator.cc",
"db/periodic_task_scheduler.cc",
"db/range_del_aggregator.cc",
@@ -116,7 +112,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"db/write_controller.cc",
"db/write_stall_stats.cc",
"db/write_thread.cc",
"db_stress_tool/db_stress_compression_manager.cc",
"env/composite_env.cc",
"env/env.cc",
"env/env_chroot.cc",
@@ -154,7 +149,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"memtable/hash_skiplist_rep.cc",
"memtable/skiplistrep.cc",
"memtable/vectorrep.cc",
"memtable/wbwi_memtable.cc",
"memtable/write_buffer_manager.cc",
"monitoring/histogram.cc",
"monitoring/histogram_windowing.cc",
@@ -218,7 +212,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"table/cuckoo/cuckoo_table_builder.cc",
"table/cuckoo/cuckoo_table_factory.cc",
"table/cuckoo/cuckoo_table_reader.cc",
"table/external_table.cc",
"table/format.cc",
"table/get_context.cc",
"table/iterator.cc",
@@ -253,7 +246,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"trace_replay/trace_record_result.cc",
"trace_replay/trace_replay.cc",
"util/async_file_reader.cc",
"util/auto_tune_compressor.cc",
"util/build_version.cc",
"util/cleanable.cc",
"util/coding.cc",
@@ -272,7 +264,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"util/random.cc",
"util/rate_limiter.cc",
"util/ribbon_config.cc",
"util/simple_mixed_compressor.cc",
"util/slice.cc",
"util/status.cc",
"util/stderr_logger.cc",
@@ -324,11 +315,8 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"utilities/persistent_cache/block_cache_tier_metadata.cc",
"utilities/persistent_cache/persistent_cache_tier.cc",
"utilities/persistent_cache/volatile_tier_impl.cc",
"utilities/secondary_index/secondary_index_iterator.cc",
"utilities/secondary_index/simple_secondary_index.cc",
"utilities/simulator_cache/cache_simulator.cc",
"utilities/simulator_cache/sim_cache.cc",
"utilities/table_properties_collectors/compact_for_tiering_collector.cc",
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
"utilities/trace/file_trace_reader_writer.cc",
"utilities/trace/replayer_impl.cc",
@@ -373,14 +361,9 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"//folly/experimental/coro:coroutine",
"//folly/experimental/coro:task",
"//folly/synchronization:distributed_mutex",
], headers=glob(["**/*.h"]), link_whole=False, extra_test_libs=False)
], headers=None, link_whole=False, extra_test_libs=False)
cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[], deps=[":rocksdb_lib"], headers=[], link_whole=True, extra_test_libs=False)
cpp_library_wrapper(name="rocksdb_with_faiss_lib", srcs=["utilities/secondary_index/faiss_ivf_index.cc"], deps=[
"//faiss:faiss",
":rocksdb_lib",
], headers=[], link_whole=False, extra_test_libs=False)
cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[], deps=[":rocksdb_lib"], headers=None, link_whole=True, extra_test_libs=False)
cpp_library_wrapper(name="rocksdb_test_lib", srcs=[
"db/db_test_util.cc",
@@ -394,46 +377,27 @@ cpp_library_wrapper(name="rocksdb_test_lib", srcs=[
"tools/trace_analyzer_tool.cc",
"utilities/agg_merge/test_agg_merge.cc",
"utilities/cassandra/test_utils.cc",
], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=True)
cpp_library_wrapper(name="rocksdb_with_faiss_test_lib", srcs=[
"db/db_test_util.cc",
"db/db_with_timestamp_test_util.cc",
"table/mock_table.cc",
"test_util/mock_time_env.cc",
"test_util/secondary_cache_test_util.cc",
"test_util/testharness.cc",
"test_util/testutil.cc",
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
"tools/trace_analyzer_tool.cc",
"utilities/agg_merge/test_agg_merge.cc",
"utilities/cassandra/test_utils.cc",
], deps=[":rocksdb_with_faiss_lib"], headers=[], link_whole=False, extra_test_libs=True)
], deps=[":rocksdb_lib"], headers=None, link_whole=False, extra_test_libs=True)
cpp_library_wrapper(name="rocksdb_tools_lib", srcs=[
"test_util/testutil.cc",
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
"tools/db_bench_tool.cc",
"tools/simulated_hybrid_file_system.cc",
"tools/tool_hooks.cc",
"tools/trace_analyzer_tool.cc",
], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=False)
], deps=[":rocksdb_lib"], headers=None, link_whole=False, extra_test_libs=False)
cpp_library_wrapper(name="rocksdb_cache_bench_tools_lib", srcs=["cache/cache_bench_tool.cc"], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=False)
cpp_library_wrapper(name="rocksdb_point_lock_bench_tools_lib", srcs=["utilities/transactions/lock/point/point_lock_bench_tool.cc"], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=False)
cpp_library_wrapper(name="rocksdb_cache_bench_tools_lib", srcs=["cache/cache_bench_tool.cc"], deps=[":rocksdb_lib"], headers=None, link_whole=False, extra_test_libs=False)
rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
"db_stress_tool/batched_ops_stress.cc",
"db_stress_tool/cf_consistency_stress.cc",
"db_stress_tool/db_stress_common.cc",
"db_stress_tool/db_stress_compaction_service.cc",
"db_stress_tool/db_stress_compression_manager.cc",
"db_stress_tool/db_stress_driver.cc",
"db_stress_tool/db_stress_filters.cc",
"db_stress_tool/db_stress_gflags.cc",
"db_stress_tool/db_stress_listener.cc",
"db_stress_tool/db_stress_shared_state.cc",
"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",
@@ -444,19 +408,13 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
"test_util/testutil.cc",
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
"tools/trace_analyzer_tool.cc",
], headers=[])
], headers=None)
cpp_binary_wrapper(name="ldb", srcs=["tools/ldb.cc"], deps=[":rocksdb_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
cpp_binary_wrapper(name="db_stress", srcs=["db_stress_tool/db_stress.cc"], deps=[":rocksdb_stress_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
cpp_binary_wrapper(name="db_bench", srcs=["tools/db_bench.cc"], deps=[":rocksdb_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
cpp_binary_wrapper(name="cache_bench", srcs=["cache/cache_bench.cc"], deps=[":rocksdb_cache_bench_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
cpp_binary_wrapper(name="point_lock_bench", srcs=["utilities/transactions/lock/point/point_lock_bench.cc"], deps=[":rocksdb_point_lock_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)
@@ -4666,12 +4624,6 @@ cpp_unittest_wrapper(name="compact_files_test",
extra_compiler_flags=[])
cpp_unittest_wrapper(name="compact_for_tiering_collector_test",
srcs=["utilities/table_properties_collectors/compact_for_tiering_collector_test.cc"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="compact_on_deletion_collector_test",
srcs=["utilities/table_properties_collectors/compact_on_deletion_collector_test.cc"],
deps=[":rocksdb_test_lib"],
@@ -4720,12 +4672,6 @@ cpp_unittest_wrapper(name="compressed_secondary_cache_test",
extra_compiler_flags=[])
cpp_unittest_wrapper(name="compression_test",
srcs=["util/compression_test.cc"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="configurable_test",
srcs=["options/configurable_test.cc"],
deps=[":rocksdb_test_lib"],
@@ -4846,12 +4792,6 @@ cpp_unittest_wrapper(name="db_encryption_test",
extra_compiler_flags=[])
cpp_unittest_wrapper(name="db_etc3_test",
srcs=["db/db_etc3_test.cc"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="db_flush_test",
srcs=["db/db_flush_test.cc"],
deps=[":rocksdb_test_lib"],
@@ -5074,7 +5014,7 @@ cpp_unittest_wrapper(name="dynamic_bloom_test",
extra_compiler_flags=[])
cpp_library_wrapper(name="env_basic_test_lib", srcs=["env/env_basic_test.cc"], deps=[":rocksdb_test_lib"], headers=[], link_whole=False, extra_test_libs=True)
cpp_library_wrapper(name="env_basic_test_lib", srcs=["env/env_basic_test.cc"], deps=[":rocksdb_test_lib"], headers=None, link_whole=False, extra_test_libs=True)
cpp_unittest_wrapper(name="env_basic_test",
srcs=["env/env_basic_test.cc"],
@@ -5124,12 +5064,6 @@ cpp_unittest_wrapper(name="external_sst_file_test",
extra_compiler_flags=[])
cpp_unittest_wrapper(name="faiss_ivf_index_test",
srcs=["utilities/secondary_index/faiss_ivf_index_test.cc"],
deps=[":rocksdb_with_faiss_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="fault_injection_test",
srcs=["db/fault_injection_test.cc"],
deps=[":rocksdb_test_lib"],
@@ -5208,12 +5142,6 @@ cpp_unittest_wrapper(name="inlineskiplist_test",
extra_compiler_flags=[])
cpp_unittest_wrapper(name="interval_test",
srcs=["util/interval_test.cc"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="io_posix_test",
srcs=["env/io_posix_test.cc"],
deps=[":rocksdb_test_lib"],
@@ -5394,12 +5322,6 @@ cpp_unittest_wrapper(name="plain_table_db_test",
extra_compiler_flags=[])
cpp_unittest_wrapper(name="point_lock_manager_stress_test",
srcs=["utilities/transactions/lock/point/point_lock_manager_stress_test.cc"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="point_lock_manager_test",
srcs=["utilities/transactions/lock/point/point_lock_manager_test.cc"],
deps=[":rocksdb_test_lib"],
+5 -4
View File
@@ -38,11 +38,12 @@ Snowflake [uses](https://www.snowflake.com/blog/how-foundationdb-powers-snowflak
The Bing search engine from Microsoft uses RocksDB as the storage engine for its web data platform: https://blogs.bing.com/Engineering-Blog/october-2021/RocksDB-in-Microsoft-Bing
## LinkedIn
1. [Venice](https://venicedb.org/) is a derived data platform using RocksDB as its storage engine. It is LinkedIn's ML feature store, powering thousands of recommender use cases, including the Feed, Video recommendations, and People You May Know.
2. LinkedIn's follow feed for storing user's activities. Check out the blog post: https://engineering.linkedin.com/blog/2016/03/followfeed--linkedin-s-feed-made-faster-and-smarter
3. Apache Samza, open source framework for stream processing.
Two different use cases at Linkedin are using RocksDB as a storage engine:
Learn more about LinkedIn's follow feed and Apache Samza in a Tech Talk by Ankit Gupta and Naveen Somasundaram: http://www.youtube.com/watch?v=plqVp_OnSzg
1. LinkedIn's follow feed for storing user's activities. Check out the blog post: https://engineering.linkedin.com/blog/2016/03/followfeed--linkedin-s-feed-made-faster-and-smarter
2. Apache Samza, open source framework for stream processing
Learn more about those use cases in a Tech Talk by Ankit Gupta and Naveen Somasundaram: http://www.youtube.com/watch?v=plqVp_OnSzg
## Yahoo
Yahoo is using RocksDB as a storage engine for their biggest distributed data store Sherpa. Learn more about it here: http://yahooeng.tumblr.com/post/120730204806/sherpa-scales-new-heights
+45 -101
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
from __future__ import absolute_import, division, print_function, unicode_literals
try:
from builtins import str
@@ -10,14 +11,14 @@ import json
import os
import sys
from targets_builder import TARGETSBuilder, LiteralValue
from targets_builder import TARGETSBuilder
from util import ColorString
# This script generates BUCK file for Buck.
# This script generates TARGETS file for Buck.
# Buck is a build tool specifying dependencies among different build targets.
# User can pass extra dependencies as a JSON object via command line, and this
# script can include these dependencies in the generate BUCK file.
# script can include these dependencies in the generate TARGETS file.
# Usage:
# $python3 buckifier/buckify_rocksdb.py
# (This generates a TARGET file without user-specified dependency for unit
@@ -28,7 +29,7 @@ from util import ColorString
# "extra_compiler_flags": ["-DFOO_BAR", "-Os"]
# }
# }'
# (Generated BUCK file has test_dep and mock1 as dependencies for RocksDB
# (Generated TARGETS file has test_dep and mock1 as dependencies for RocksDB
# unit tests, and will use the extra_compiler_flags to compile the unit test
# source.)
@@ -114,9 +115,9 @@ def get_dependencies():
return deps_map
# Prepare BUCK file for buck
def generate_buck(repo_path, deps_map):
print(ColorString.info("Generating BUCK"))
# Prepare TARGETS file for buck
def generate_targets(repo_path, deps_map):
print(ColorString.info("Generating TARGETS"))
# parsed src.mk file
src_mk = parse_src_mk(repo_path)
# get all .cc files
@@ -131,15 +132,12 @@ def generate_buck(repo_path, deps_map):
if len(sys.argv) >= 2:
# Heuristically quote and canonicalize whitespace for inclusion
# in how the file was generated.
extra_argv = " '{}'".format(" ".join(sys.argv[1].split()))
extra_argv = " '{0}'".format(" ".join(sys.argv[1].split()))
BUCK = TARGETSBuilder("%s/BUCK" % repo_path, extra_argv)
# Add oncall("rocksdb_point_of_contact") at the top
BUCK.add_oncall("rocksdb_point_of_contact")
TARGETS = TARGETSBuilder("%s/TARGETS" % repo_path, extra_argv)
# rocksdb_lib
BUCK.add_library(
TARGETS.add_library(
"rocksdb_lib",
src_mk["LIB_SOURCES"] +
# always add range_tree, it's only excluded on ppc64, which we don't use internally
@@ -152,29 +150,20 @@ def generate_buck(repo_path, deps_map):
"//folly/experimental/coro:task",
"//folly/synchronization:distributed_mutex",
],
headers=LiteralValue("glob([\"**/*.h\"])")
)
# rocksdb_whole_archive_lib
BUCK.add_library(
TARGETS.add_library(
"rocksdb_whole_archive_lib",
[],
deps=[
":rocksdb_lib",
],
headers=None,
extra_external_deps="",
link_whole=True,
)
# rocksdb_with_faiss_lib
BUCK.add_library(
"rocksdb_with_faiss_lib",
src_mk.get("WITH_FAISS_LIB_SOURCES", []),
deps=[
"//faiss:faiss",
":rocksdb_lib",
],
)
# rocksdb_test_lib
BUCK.add_library(
TARGETS.add_library(
"rocksdb_test_lib",
src_mk.get("MOCK_LIB_SOURCES", [])
+ src_mk.get("TEST_LIB_SOURCES", [])
@@ -183,20 +172,8 @@ def generate_buck(repo_path, deps_map):
[":rocksdb_lib"],
extra_test_libs=True,
)
# rocksdb_with_faiss_test_lib
BUCK.add_library(
"rocksdb_with_faiss_test_lib",
src_mk.get("MOCK_LIB_SOURCES", [])
+ src_mk.get("TEST_LIB_SOURCES", [])
+ src_mk.get("EXP_LIB_SOURCES", [])
+ src_mk.get("ANALYZER_LIB_SOURCES", []),
deps=[
":rocksdb_with_faiss_lib",
],
extra_test_libs=True,
)
# rocksdb_tools_lib
BUCK.add_library(
TARGETS.add_library(
"rocksdb_tools_lib",
src_mk.get("BENCH_LIB_SOURCES", [])
+ src_mk.get("ANALYZER_LIB_SOURCES", [])
@@ -204,63 +181,43 @@ def generate_buck(repo_path, deps_map):
[":rocksdb_lib"],
)
# rocksdb_cache_bench_tools_lib
BUCK.add_library(
TARGETS.add_library(
"rocksdb_cache_bench_tools_lib",
src_mk.get("CACHE_BENCH_LIB_SOURCES", []),
[":rocksdb_lib"],
)
# rocksdb_point_lock_bench_tools_lib
BUCK.add_library(
"rocksdb_point_lock_bench_tools_lib",
src_mk.get("POINT_LOCK_BENCH_LIB_SOURCES", []),
[":rocksdb_lib"],
)
# rocksdb_stress_lib
BUCK.add_rocksdb_library(
TARGETS.add_rocksdb_library(
"rocksdb_stress_lib",
src_mk.get("ANALYZER_LIB_SOURCES", [])
+ src_mk.get("STRESS_LIB_SOURCES", [])
+ ["test_util/testutil.cc"],
)
# ldb binary
BUCK.add_binary(
"ldb", ["tools/ldb.cc"], [":rocksdb_tools_lib"]
)
# db_stress binary
BUCK.add_binary(
TARGETS.add_binary(
"db_stress", ["db_stress_tool/db_stress.cc"], [":rocksdb_stress_lib"]
)
# db_bench binary
BUCK.add_binary(
"db_bench", ["tools/db_bench.cc"], [":rocksdb_tools_lib"]
)
# cache_bench binary
BUCK.add_binary(
TARGETS.add_binary(
"cache_bench", ["cache/cache_bench.cc"], [":rocksdb_cache_bench_tools_lib"]
)
# point_lock_bench binary
BUCK.add_binary(
"point_lock_bench",
["utilities/transactions/lock/point/point_lock_bench.cc"],
[":rocksdb_point_lock_bench_tools_lib"]
)
# bench binaries
for src in src_mk.get("MICROBENCH_SOURCES", []):
name = src.rsplit("/", 1)[1].split(".")[0] if "/" in src else src.split(".")[0]
BUCK.add_binary(name, [src], [], extra_bench_libs=True)
print(f"Extra dependencies:\n{json.dumps(deps_map)}")
TARGETS.add_binary(name, [src], [], extra_bench_libs=True)
print("Extra dependencies:\n{0}".format(json.dumps(deps_map)))
# Dictionary test executable name -> relative source file path
test_source_map = {}
# c_test.c is added through BUCK.add_c_test(). If there
# c_test.c is added through TARGETS.add_c_test(). If there
# are more than one .c test file, we need to extend
# BUCK.add_c_test() to include other C tests too.
# TARGETS.add_c_test() to include other C tests too.
for test_src in src_mk.get("TEST_MAIN_SOURCES_C", []):
if test_src != "db/c_test.c":
print("Don't know how to deal with " + test_src)
return False
BUCK.add_c_test()
TARGETS.add_c_test()
try:
with open(f"{repo_path}/buckifier/bench.json") as json_file:
@@ -275,7 +232,7 @@ def generate_buck(repo_path, deps_map):
for metric in overloaded_metric_list:
if not isinstance(metric, dict):
clean_benchmarks[binary][benchmark].append(metric)
BUCK.add_fancy_bench_config(
TARGETS.add_fancy_bench_config(
config_dict["name"],
clean_benchmarks,
False,
@@ -297,7 +254,7 @@ def generate_buck(repo_path, deps_map):
if not isinstance(metric, dict):
clean_benchmarks[binary][benchmark].append(metric)
for config_dict in slow_fancy_bench_config_list:
BUCK.add_fancy_bench_config(
TARGETS.add_fancy_bench_config(
config_dict["name"] + "_slow",
clean_benchmarks,
True,
@@ -310,20 +267,15 @@ def generate_buck(repo_path, deps_map):
except Exception:
pass
BUCK.add_test_header()
TARGETS.add_test_header()
for test_src in src_mk.get("TEST_MAIN_SOURCES", []):
test = test_src.split(".c")[0].strip().split("/")[-1].strip()
test_source_map[test] = (test_src, False)
test_source_map[test] = test_src
print("" + test + " " + test_src)
for test_src in src_mk.get("WITH_FAISS_TEST_MAIN_SOURCES", []):
test = test_src.split(".c")[0].strip().split("/")[-1].strip()
test_source_map[test] = (test_src, True)
print("" + test + " " + test_src + " [FAISS]")
for target_alias, deps in deps_map.items():
for test, (test_src, with_faiss) in sorted(test_source_map.items()):
for test, test_src in sorted(test_source_map.items()):
if len(test) == 0:
print(ColorString.warning("Failed to get test name for %s" % test_src))
continue
@@ -332,39 +284,31 @@ def generate_buck(repo_path, deps_map):
if test in _EXPORTED_TEST_LIBS:
test_library = "%s_lib" % test_target_name
BUCK.add_library(
TARGETS.add_library(
test_library,
[test_src],
deps=[":rocksdb_test_lib"],
extra_test_libs=True,
)
BUCK.register_test(
TARGETS.register_test(
test_target_name,
test_src,
deps=json.dumps(deps["extra_deps"] + [":" + test_library]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
)
else:
if with_faiss:
BUCK.register_test(
test_target_name,
test_src,
deps=json.dumps(deps["extra_deps"] + [":rocksdb_with_faiss_test_lib"]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
)
else:
BUCK.register_test(
test_target_name,
test_src,
deps=json.dumps(deps["extra_deps"] + [":rocksdb_test_lib"]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
)
BUCK.export_file("tools/db_crashtest.py")
TARGETS.register_test(
test_target_name,
test_src,
deps=json.dumps(deps["extra_deps"] + [":rocksdb_test_lib"]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
)
TARGETS.export_file("tools/db_crashtest.py")
print(ColorString.info("Generated BUCK Summary:"))
print(ColorString.info("- %d libs" % BUCK.total_lib))
print(ColorString.info("- %d binarys" % BUCK.total_bin))
print(ColorString.info("- %d tests" % BUCK.total_test))
print(ColorString.info("Generated TARGETS Summary:"))
print(ColorString.info("- %d libs" % TARGETS.total_lib))
print(ColorString.info("- %d binarys" % TARGETS.total_bin))
print(ColorString.info("- %d tests" % TARGETS.total_test))
return True
@@ -384,10 +328,10 @@ def exit_with_error(msg):
def main():
deps_map = get_dependencies()
# Generate BUCK file for buck
ok = generate_buck(get_rocksdb_path(), deps_map)
# Generate TARGETS file for buck
ok = generate_targets(get_rocksdb_path(), deps_map)
if not ok:
exit_with_error("Failed to generate BUCK files")
exit_with_error("Failed to generate TARGETS files")
if __name__ == "__main__":
+11 -23
View File
@@ -1,44 +1,32 @@
#!/usr/bin/env bash
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
# If clang_format_diff.py command is not specfied, we assume we are able to
# access directly without any path.
if [[ ! -f "BUCK" ]]
then
echo "BUCK file is missing!"
echo "Please do not remove / rename BUCK file in your commit(s)."
exit 1
fi
TGT_DIFF=`git diff BUCK | head -n 1`
TGT_DIFF=`git diff TARGETS | head -n 1`
if [ ! -z "$TGT_DIFF" ]
then
echo "BUCK file has uncommitted changes. Skip this check."
echo "TARGETS file has uncommitted changes. Skip this check."
exit 0
fi
echo Backup original BUCK file.
echo Backup original TARGETS file.
cp BUCK BUCK.bkp
cp TARGETS TARGETS.bkp
${PYTHON:-python3} buckifier/buckify_rocksdb.py
if [[ ! -f "BUCK" ]]
then
echo "BUCK file went missing after running buckifier/buckify_rocksdb.py!"
echo "Please do not remove the BUCK file."
exit 1
fi
TGT_DIFF=`git diff BUCK | head -n 1`
TGT_DIFF=`git diff TARGETS | head -n 1`
if [ -z "$TGT_DIFF" ]
then
mv BUCK.bkp BUCK
mv TARGETS.bkp TARGETS
exit 0
else
echo "Please run '${PYTHON:-python3} buckifier/buckify_rocksdb.py' to update BUCK file."
echo "Do not manually update BUCK file."
echo "Please run '${PYTHON:-python3} buckifier/buckify_rocksdb.py' to update TARGETS file."
echo "Do not manually update TARGETS file."
${PYTHON:-python3} --version
mv BUCK.bkp BUCK
mv TARGETS.bkp TARGETS
exit 1
fi
+8 -27
View File
@@ -1,4 +1,5 @@
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
from __future__ import absolute_import, division, print_function, unicode_literals
try:
from builtins import object, str
@@ -8,28 +9,17 @@ import pprint
import targets_cfg
class LiteralValue:
def __init__(self, value):
self.value = value
def __str__(self):
return str(self.value)
def smart_quote_value(val):
if isinstance(val, LiteralValue):
return str(val)
return '"%s"' % val
def pretty_list(lst, indent=8):
if lst is None or len(lst) == 0:
return ""
if len(lst) == 1:
return smart_quote_value(lst[0])
return '"%s"' % lst[0]
separator = ',\n%s' % (" " * indent)
res = separator.join(sorted(map(smart_quote_value, lst)))
res = "\n" + (" " * indent) + res + ',\n' + (" " * (indent - 4))
separator = '",\n%s"' % (" " * indent)
res = separator.join(sorted(lst))
res = "\n" + (" " * indent) + '"' + res + '",\n' + (" " * (indent - 4))
return res
@@ -45,11 +35,6 @@ class TARGETSBuilder:
self.total_bin = 0
self.total_test = 0
self.tests_cfg = ""
def add_oncall(self, oncall):
with open(self.path, "ab") as targets_file:
targets_file.write(targets_cfg.oncall_template.format(name=oncall).encode("utf-8"))
def add_library(
self,
@@ -63,12 +48,7 @@ class TARGETSBuilder:
extra_test_libs=False,
):
if headers is not None:
if isinstance(headers, LiteralValue):
headers = str(headers)
else:
headers = "[" + pretty_list(headers) + "]"
else:
headers = "[]"
headers = "[" + pretty_list(headers) + "]"
with open(self.path, "ab") as targets_file:
targets_file.write(
targets_cfg.library_template.format(
@@ -85,7 +65,8 @@ class TARGETSBuilder:
self.total_lib = self.total_lib + 1
def add_rocksdb_library(self, name, srcs, headers=None, external_dependencies=None):
headers = "[" + pretty_list(headers) + "]"
if headers is not None:
headers = "[" + pretty_list(headers) + "]"
with open(self.path, "ab") as targets_file:
targets_file.write(
targets_cfg.rocksdb_library_template.format(
+4 -10
View File
@@ -1,12 +1,11 @@
# 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 the Apache 2.0 License (found in the LICENSE.Apache file in the root directory).
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
from __future__ import absolute_import, division, print_function, unicode_literals
rocksdb_target_header_template = """# This file \100generated by:
#$ python3 buckifier/buckify_rocksdb.py{extra_argv}
# --> DO NOT EDIT MANUALLY <--
# This file is a Meta-specific integration for buck builds, so can
# only be validated by Meta employees.
# This file is a Facebook-specific integration for buck builds, so can
# only be validated by Facebook employees.
load("//rocks/buckifier:defs.bzl", "cpp_library_wrapper","rocks_cpp_library_wrapper","cpp_binary_wrapper","cpp_unittest_wrapper","fancy_bench_wrapper","add_c_test_wrapper")
load("@fbcode_macros//build_defs:export_files.bzl", "export_file")
@@ -43,8 +42,3 @@ fancy_bench_wrapper(suite_name="{name}", binary_to_bench_to_metric_list_map={ben
export_file_template = """
export_file(name = "{name}")
"""
oncall_template = """
oncall("{name}")
"""
+1
View File
@@ -2,6 +2,7 @@
"""
This module keeps commonly used components.
"""
from __future__ import absolute_import, division, print_function, unicode_literals
try:
from builtins import object
+5 -4
View File
@@ -25,6 +25,7 @@
#
# The solution is to move the include out of the #ifdef.
from __future__ import print_function
import argparse
import re
@@ -61,7 +62,7 @@ def expand_include(
included.add(include_path)
with open(include_path) as f:
print(f'#line 1 "{include_path}"', file=source_out)
print('#line 1 "{}"'.format(include_path), file=source_out)
process_file(
f, include_path, source_out, header_out, include_paths, public_include_paths
)
@@ -117,7 +118,7 @@ def process_file(
)
if expanded:
print(f'#line {line + 1} "{abs_path}"', file=source_out)
print('#line {} "{}"'.format(line + 1, abs_path), file=source_out)
elif text != "#pragma once\n":
source_out.write(text)
@@ -156,8 +157,8 @@ def main():
with open(filename) as f, open(args.source_out, "w") as source_out, open(
args.header_out, "w"
) as header_out:
print(f'#line 1 "{filename}"', file=source_out)
print(f'#include "{header_out.name}"', file=source_out)
print('#line 1 "{}"'.format(filename), file=source_out)
print('#include "{}"'.format(header_out.name), file=source_out)
process_file(
f, abs_path, source_out, header_out, include_paths, public_include_paths
)
+2 -2
View File
@@ -102,7 +102,7 @@ class BenchmarkUtils:
class ResultParser:
def __init__(self, field=r"(\w|[+-:.%])+", intrafield=r"(\s)+", separator="\t"):
def __init__(self, field="(\w|[+-:.%])+", intrafield="(\s)+", separator="\t"):
self.field = re.compile(field)
self.intra = re.compile(intrafield)
self.sep = re.compile(separator)
@@ -159,7 +159,7 @@ class ResultParser:
def load_report_from_tsv(filename: str):
file = open(filename)
file = open(filename, "r")
contents = file.readlines()
file.close()
parser = ResultParser()
+11 -39
View File
@@ -45,21 +45,18 @@ if test -z "$OUTPUT"; then
exit 1
fi
# we depend on C++20, but should be compatible with newer standards
# we depend on C++17, but should be compatible with newer standards
if [ "$ROCKSDB_CXX_STANDARD" ]; then
PLATFORM_CXXFLAGS="-std=$ROCKSDB_CXX_STANDARD"
else
PLATFORM_CXXFLAGS="-std=c++20"
PLATFORM_CXXFLAGS="-std=c++17"
fi
# we currently depend on POSIX platform
COMMON_FLAGS="-DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX"
# Default to fbcode gcc on Meta internal machines
IS_META_HOST="$(hostname | grep -E '(facebook|meta).com|fbinfra.net')"
if [ -z "$ROCKSDB_NO_FBCODE" -a "$IS_META_HOST" ]; then
if [ -d /mnt/gvfs/third-party ]; then
echo "NOTE: Using fbcode build" >&2
# Default to fbcode gcc on internal fb machines
if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
FBCODE_BUILD="true"
# If we're compiling with TSAN or shared lib, we need pic build
PIC_BUILD=$COMPILE_WITH_TSAN
@@ -67,11 +64,6 @@ if [ -z "$ROCKSDB_NO_FBCODE" -a "$IS_META_HOST" ]; then
PIC_BUILD=1
fi
source "$PWD/build_tools/fbcode_config_platform010.sh"
else
echo "************************************************************************" >&2
echo "WARNING: -d /mnt/gvfs/third-party failed; no fbcode build" >&2
echo "************************************************************************" >&2
fi
fi
# Delete existing output, if it exists
@@ -79,9 +71,7 @@ rm -f "$OUTPUT"
touch "$OUTPUT"
if test -z "$CC"; then
if [ "$USE_CLANG" -a -x "$(command -v clang)" ]; then
CC=clang
elif [ -x "$(command -v cc)" ]; then
if [ -x "$(command -v cc)" ]; then
CC=cc
elif [ -x "$(command -v clang)" ]; then
CC=clang
@@ -91,9 +81,7 @@ if test -z "$CC"; then
fi
if test -z "$CXX"; then
if [ "$USE_CLANG" -a -x "$(command -v clang++)" ]; then
CXX=clang++
elif [ -x "$(command -v g++)" ]; then
if [ -x "$(command -v g++)" ]; then
CXX=g++
elif [ -x "$(command -v clang++)" ]; then
CXX=clang++
@@ -103,9 +91,7 @@ if test -z "$CXX"; then
fi
if test -z "$AR"; then
if [ "$USE_CLANG" -a -x "$(command -v llvm-ar)" ]; then
AR=llvm-ar
elif [ -x "$(command -v gcc-ar)" ]; then
if [ -x "$(command -v gcc-ar)" ]; then
AR=gcc-ar
elif [ -x "$(command -v llvm-ar)" ]; then
AR=llvm-ar
@@ -311,8 +297,7 @@ EOF
EOF
then
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=1"
# Hack: don't link extra gflags assuming it comes with folly
[ "$USE_FOLLY" ] || PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
# check if namespace is gflags
elif $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null << EOF
#include <gflags/gflags.h>
@@ -321,8 +306,7 @@ EOF
EOF
then
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=1 -DGFLAGS_NAMESPACE=gflags"
# Hack: don't link extra gflags assuming it comes with folly
[ "$USE_FOLLY" ] || PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
# check if namespace is google
elif $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null << EOF
#include <gflags/gflags.h>
@@ -376,13 +360,9 @@ EOF
fi
if ! test $ROCKSDB_DISABLE_ZSTD; then
# Test whether zstd library is installed with minimum version
# (Keep in sync with compression.h)
# Test whether zstd library is installed
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
#include <zstd.h>
#if ZSTD_VERSION_NUMBER < 10400
#error "ZSTD support requires version >= 1.4.0 (libzstd-devel)"
#endif // ZSTD_VERSION_NUMBER
int main() {}
EOF
if [ "$?" = 0 ]; then
@@ -605,7 +585,7 @@ EOF
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lbenchmark"
fi
fi
if test $USE_FOLLY || test $USE_FOLLY_LITE; then
if test $USE_FOLLY; then
# Test whether libfolly library is installed
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
#include <folly/synchronization/DistributedMutex.h>
@@ -771,12 +751,6 @@ if [ "$USE_FOLLY" ]; then
FOLLY_PATH=`cd $FOLLY_DIR && $PYTHON build/fbcode_builder/getdeps.py show-inst-dir folly`
fi
fi
if [ "$USE_FOLLY_LITE" ]; then
if [ "$FOLLY_DIR" ]; then
BOOST_SOURCE_PATH=`cd $FOLLY_DIR && $PYTHON build/fbcode_builder/getdeps.py show-source-dir boost`
FMT_SOURCE_PATH=`cd $FOLLY_DIR && $PYTHON build/fbcode_builder/getdeps.py show-source-dir fmt`
fi
fi
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
@@ -818,8 +792,6 @@ echo "PROFILING_FLAGS=$PROFILING_FLAGS" >> "$OUTPUT"
echo "FIND=$FIND" >> "$OUTPUT"
echo "WATCH=$WATCH" >> "$OUTPUT"
echo "FOLLY_PATH=$FOLLY_PATH" >> "$OUTPUT"
echo "BOOST_SOURCE_PATH=$BOOST_SOURCE_PATH" >> "$OUTPUT"
echo "FMT_SOURCE_PATH=$FMT_SOURCE_PATH" >> "$OUTPUT"
# This will enable some related identifiers for the preprocessor
if test -n "$JEMALLOC"; then
-31
View File
@@ -1,31 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved.
#
# Check for some simple mistakes in public headers (on the command line)
# that should prevent commit or push
BAD=""
# Look for potential for ODR violations caused by public headers depending on
# build parameters that could vary between RocksDB build and application build.
# * Cases like LUA, ROCKSDB_NAMESPACE, and ROCKSDB_ASSERT_STATUS_CHECKED are
# intentional, hard to avoid. (We expect definitions to change and the user
# should also.)
# * Cases like _WIN32, OS_WIN, and __cplusplus are essentially ODR-safe.
# * Cases like
# #ifdef BLAH // ODR-SAFE
# #undef BLAH
# #endif
# that should not cause ODR violations can be exempted with the ODR-SAFE
# marker recognized here.
grep -nHE '^#if' -- "$@" | grep -vE 'ROCKSDB_NAMESPACE|ROCKSDB_ASSERT_STATUS_CHECKED|LUA|_WIN32|OS_WIN|ODR-SAFE|__cplusplus|ROCKSDB_DLL|ROCKSDB_LIBRARY_EXPORTS'
if [ "$?" != "1" ]; then
echo "^^^^^ #if in public API could cause an ODR violation."
echo " Add // ODR-SAFE if verified safe."
BAD=1
fi
if [ "$BAD" ]; then
exit 1
fi
+11 -10
View File
@@ -9,6 +9,7 @@
- Prints those error messages to stdout
"""
from __future__ import absolute_import, division, print_function, unicode_literals
import re
import sys
@@ -42,7 +43,7 @@ class GTestErrorParser(ErrorParserBase):
return None
gtest_fail_match = self._GTEST_FAIL_PATTERN.match(line)
if gtest_fail_match:
return "{} failed: {}".format(self._last_gtest_name, gtest_fail_match.group(1))
return "%s failed: %s" % (self._last_gtest_name, gtest_fail_match.group(1))
return None
@@ -65,52 +66,52 @@ class CompilerErrorParser(MatchErrorParser):
# format (link error):
# '<filename>:<line #>: error: <error msg>'
# The below regex catches both
super().__init__(r"\S+:\d+: error:")
super(CompilerErrorParser, self).__init__(r"\S+:\d+: error:")
class ScanBuildErrorParser(MatchErrorParser):
def __init__(self):
super().__init__(r"scan-build: \d+ bugs found.$")
super(ScanBuildErrorParser, self).__init__(r"scan-build: \d+ bugs found.$")
class DbCrashErrorParser(MatchErrorParser):
def __init__(self):
super().__init__(r"\*\*\*.*\^$|TEST FAILED.")
super(DbCrashErrorParser, self).__init__(r"\*\*\*.*\^$|TEST FAILED.")
class WriteStressErrorParser(MatchErrorParser):
def __init__(self):
super().__init__(
super(WriteStressErrorParser, self).__init__(
r"ERROR: write_stress died with exitcode=\d+"
)
class AsanErrorParser(MatchErrorParser):
def __init__(self):
super().__init__(r"==\d+==ERROR: AddressSanitizer:")
super(AsanErrorParser, self).__init__(r"==\d+==ERROR: AddressSanitizer:")
class UbsanErrorParser(MatchErrorParser):
def __init__(self):
# format: '<filename>:<line #>:<column #>: runtime error: <error msg>'
super().__init__(r"\S+:\d+:\d+: runtime error:")
super(UbsanErrorParser, self).__init__(r"\S+:\d+:\d+: runtime error:")
class ValgrindErrorParser(MatchErrorParser):
def __init__(self):
# just grab the summary, valgrind doesn't clearly distinguish errors
# from other log messages.
super().__init__(r"==\d+== ERROR SUMMARY:")
super(ValgrindErrorParser, self).__init__(r"==\d+== ERROR SUMMARY:")
class CompatErrorParser(MatchErrorParser):
def __init__(self):
super().__init__(r"==== .*[Ee]rror.* ====$")
super(CompatErrorParser, self).__init__(r"==== .*[Ee]rror.* ====$")
class TsanErrorParser(MatchErrorParser):
def __init__(self):
super().__init__(r"WARNING: ThreadSanitizer:")
super(TsanErrorParser, self).__init__(r"WARNING: ThreadSanitizer:")
_TEST_NAME_TO_PARSERS = {
+1 -1
View File
@@ -59,7 +59,7 @@ fi
if ! test $ROCKSDB_DISABLE_ZSTD; then
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd${MAYBE_PIC}.a"
CFLAGS+=" -DZSTD -DZSTD_STATIC_LINKING_ONLY"
CFLAGS+=" -DZSTD"
fi
# location of gflags headers and libraries
+4 -71
View File
@@ -118,9 +118,6 @@ fi
# fi
set -e
# Exclude third-party from formatting
EXCLUDE=':!third-party/'
uncommitted_code=`git diff HEAD`
# If there's no uncommitted changes, we assume user are doing post-commit
@@ -140,78 +137,14 @@ 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" -- $EXCLUDE | $CLANG_FORMAT_DIFF -p 1) || true
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1) || true
echo "Checking format of changes not yet in $FORMAT_UPSTREAM..."
else
# Check the format of uncommitted lines,
diffs=$(git diff -U0 HEAD -- $EXCLUDE | $CLANG_FORMAT_DIFF -p 1) || true
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1) || true
echo "Checking format of uncommitted changes..."
fi
# Check for missing copyright in new files
echo "Checking for copyright headers in new files..."
# Get list of new files (added, not just modified)
if [ -z "$uncommitted_code" ]; then
# Post-commit: check files added since merge base
new_files=$(git diff --name-only --diff-filter=A "$FORMAT_UPSTREAM_MERGE_BASE" -- '*.h' '*.cc' '*.py' $EXCLUDE)
else
# Pre-commit: check staged new files
new_files=$(git diff --name-only --diff-filter=A --cached HEAD -- '*.h' '*.cc' '*.py' $EXCLUDE)
fi
if [ -n "$new_files" ]; then
files_missing_copyright=""
for file in $new_files; do
if [ -f "$file" ]; then
# Check if file is missing copyright
# For .py files, check for Python-style comment
# For .h and .cc files, check for C++-style comment
if [[ "$file" == *.py ]]; then
if ! grep -q "Copyright (c) Meta Platforms, Inc. and affiliates" "$file"; then
files_missing_copyright="$files_missing_copyright $file"
# Add copyright header to Python file
temp_file=$(mktemp)
{
echo "# Copyright (c) Meta Platforms, Inc. and affiliates."
echo "# This source code is licensed under both the GPLv2 (found in the COPYING file in the root directory)"
echo "# and the Apache 2.0 License (found in the LICENSE.Apache file in the root directory)."
echo
cat "$file"
} > "$temp_file"
mv "$temp_file" "$file"
echo "Added copyright header to $file"
fi
elif [[ "$file" == *.h ]] || [[ "$file" == *.cc ]]; then
if ! grep -q "Copyright (c) Meta Platforms, Inc. and affiliates" "$file"; then
files_missing_copyright="$files_missing_copyright $file"
# Add copyright header to C++ file
temp_file=$(mktemp)
{
echo "// Copyright (c) Meta Platforms, Inc. and affiliates. "
echo "// This source code is licensed under both the GPLv2 (found in the "
echo "// COPYING file in the root directory) and Apache 2.0 License "
echo "// (found in the LICENSE.Apache file in the root directory)."
echo
cat "$file"
} > "$temp_file"
mv "$temp_file" "$file"
echo "Added copyright header to $file"
fi
fi
fi
done
if [ -n "$files_missing_copyright" ]; then
echo "Copyright headers were added to new files."
else
echo "All new files have copyright headers."
fi
else
echo "No new files to check for copyright headers."
fi
if [ -z "$diffs" ]
then
echo "Nothing needs to be reformatted!"
@@ -254,9 +187,9 @@ fi
# Do in-place format adjustment.
if [ -z "$uncommitted_code" ]
then
git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" -- $EXCLUDE | $CLANG_FORMAT_DIFF -i -p 1
git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -i -p 1
else
git diff -U0 HEAD -- $EXCLUDE | $CLANG_FORMAT_DIFF -i -p 1
git diff -U0 HEAD | $CLANG_FORMAT_DIFF -i -p 1
fi
echo "Files reformatted!"
-88
View File
@@ -1,88 +0,0 @@
# INSTRUCTIONS:
# I was not able to build docker images on an isolated devserver because of
# issues with proxy internet access. Use a public cloud or other Linux system.
# (I used a Debian system after installing docker features, adding my user to
# the docker and docker-registry groups, and logging out and back in to pick
# those up.)
#
# Follow https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-with-a-personal-access-token-classic
# to login with your GitHub credentials, as in
#
# $ docker login ghcr.io -u pdillinger
#
# and paste the limited-purpose GitHub token into the terminal.
#
# Then in the build_tools/ubuntu22_image directory, (bump minor version for
# random docker file updates, major version tracks Ubuntu release)
#
# $ docker build -t ghcr.io/facebook/rocksdb_ubuntu:22.0
# $ docker push ghcr.io/facebook/rocksdb_ubuntu:22.0
#
# Might need to change visibility to public through
# https://github.com/orgs/facebook/packages/container/rocksdb_ubuntu/settings
# or similar.
# from official ubuntu 22.04
FROM ubuntu:22.04
# update system
RUN apt-get update
RUN apt-get upgrade -y
# install basic tools
RUN apt-get install -y vim wget curl
# install tzdata noninteractive
RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata
# install git and default compilers
RUN apt-get install -y git gcc g++ clang clang-tools
# install basic package
RUN apt-get install -y lsb-release software-properties-common gnupg
# install gflags, tbb
RUN apt-get install -y libgflags-dev libtbb-dev
# install compression libs
RUN apt-get install -y libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev libzstd-dev
# install cmake
RUN apt-get install -y cmake
RUN apt-get install -y libssl-dev
# install clang-13
WORKDIR /root
RUN wget https://apt.llvm.org/llvm.sh
RUN chmod +x llvm.sh
RUN ./llvm.sh 13 all
# There are incompatibilities between clang with -std=c++20 and libstdc++
# provided by gcc, so we have to compile with clang-13 using -stdlib=libc++
# and only one version of libc++ can be installed on the system at one time.
# So to avoid confusion we remove unusable clang-14 also.
RUN apt-get install libc++-13-dev libc++abi-13-dev
RUN apt-get purge -y clang-14 && apt-get autoremove -y
# install gcc-10 and more, default is 11
RUN apt-get install -y gcc-10 g++-10
RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test
RUN apt-get install -y gcc-13 g++-13
# install apt-get install -y valgrind
RUN apt-get install -y valgrind
# install folly depencencies
# Missing compatible libunwind: RUN apt-get install -y libgoogle-glog-dev
# So instead install from source. This currently requires compiling with
# -DGLOG_USE_GLOG_EXPORT
RUN wget https://github.com/google/glog/archive/refs/tags/v0.7.1.tar.gz && tar xzf v0.7.1.tar.gz && cd glog-0.7.1/ && cmake -S . -B build -G "Unix Makefiles" && cmake --build build && cmake --build build --target install && cd .. && rm -rf v0.7.1.tar.gz glog-0.7.1
# install openjdk 8
RUN apt-get install -y openjdk-8-jdk
ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk-amd64
# install mingw
RUN apt-get install -y mingw-w64
# install gtest-parallel package
RUN git clone --single-branch --branch master --depth 1 https://github.com/google/gtest-parallel.git ~/gtest-parallel
ENV PATH $PATH:/root/gtest-parallel
# install libprotobuf for fuzzers test
RUN apt-get install -y ninja-build binutils liblzma-dev libz-dev pkg-config autoconf libtool
RUN git clone --branch v1.0 https://github.com/google/libprotobuf-mutator.git ~/libprotobuf-mutator && cd ~/libprotobuf-mutator && git checkout ffd86a32874e5c08a143019aad1aaf0907294c9f && mkdir build && cd build && cmake .. -GNinja -DCMAKE_C_COMPILER=clang-13 -DCMAKE_CXX_COMPILER=clang++-13 -DCMAKE_BUILD_TYPE=Release -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON && ninja && ninja install
ENV PKG_CONFIG_PATH /usr/local/OFF/:/root/libprotobuf-mutator/build/external.protobuf/lib/pkgconfig/
ENV PROTOC_BIN /root/libprotobuf-mutator/build/external.protobuf/bin/protoc
# install the latest google benchmark
RUN git clone --depth 1 --branch v1.7.0 https://github.com/google/benchmark.git ~/benchmark && cd ~/benchmark && mkdir build && cd build && cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DBENCHMARK_ENABLE_GTEST_TESTS=0 && ninja && ninja install && cd ~ && rm -rf /root/benchmark
# clean up
RUN rm -rf /var/lib/apt/lists/*
-72
View File
@@ -1,72 +0,0 @@
# INSTRUCTIONS:
# I was not able to build docker images on an isolated devserver because of
# issues with proxy internet access. Use a public cloud or other Linux system.
# (I used a Debian system after installing docker features, adding my user to
# the docker and docker-registry groups, and logging out and back in to pick
# those up.)
#
# Follow https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-with-a-personal-access-token-classic
# to login with your GitHub credentials, as in
#
# $ docker login ghcr.io -u pdillinger
#
# and paste the limited-purpose GitHub token into the terminal.
#
# Then in the build_tools/ubuntu24_image directory, (bump minor version for
# random docker file updates, major version tracks Ubuntu release)
#
# $ docker build -t ghcr.io/facebook/rocksdb_ubuntu:24.0
# $ docker push ghcr.io/facebook/rocksdb_ubuntu:24.0
#
# Might need to change visibility to public through
# https://github.com/orgs/facebook/packages/container/rocksdb_ubuntu/settings
# or similar.
# from official ubuntu 24.04
FROM ubuntu:24.04
# update system
RUN apt-get update
RUN apt-get upgrade -y
# install basic tools
RUN apt-get install -y vim wget curl
# install tzdata noninteractive
RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata
# install git and default compilers
RUN apt-get install -y git gcc g++ clang clang-tools
# install basic package
RUN apt-get install -y lsb-release software-properties-common gnupg
# install gflags, tbb
RUN apt-get install -y libgflags-dev libtbb-dev
# install compression libs
RUN apt-get install -y libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev libzstd-dev
# install cmake
RUN apt-get install -y cmake
RUN apt-get install -y libssl-dev
# install gcc-12 and more, default is 13
RUN apt-get install -y gcc-12 g++-12 gcc-14 g++-14
# install apt-get install -y valgrind
RUN apt-get install -y valgrind
# install folly depencencies
RUN apt-get install -y libgoogle-glog-dev
# install openjdk 8
RUN apt-get install -y openjdk-8-jdk
ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk-amd64
# install mingw
RUN apt-get install -y mingw-w64
# install gtest-parallel package
RUN git clone --single-branch --branch master --depth 1 https://github.com/google/gtest-parallel.git ~/gtest-parallel
ENV PATH $PATH:/root/gtest-parallel
# install libprotobuf for fuzzers test
RUN apt-get install -y ninja-build binutils liblzma-dev libz-dev pkg-config autoconf libtool
RUN git clone --branch v1.0 https://github.com/google/libprotobuf-mutator.git ~/libprotobuf-mutator && cd ~/libprotobuf-mutator && git checkout ffd86a32874e5c08a143019aad1aaf0907294c9f && mkdir build && cd build && cmake .. -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON && ninja && ninja install
ENV PKG_CONFIG_PATH /usr/local/OFF/:/root/libprotobuf-mutator/build/external.protobuf/lib/pkgconfig/
ENV PROTOC_BIN /root/libprotobuf-mutator/build/external.protobuf/bin/protoc
# install the latest google benchmark
RUN git clone --depth 1 --branch v1.7.0 https://github.com/google/benchmark.git ~/benchmark && cd ~/benchmark && mkdir build && cd build && cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DBENCHMARK_ENABLE_GTEST_TESTS=0 && ninja && ninja install && cd ~ && rm -rf /root/benchmark
# clean up
RUN rm -rf /var/lib/apt/lists/*
+13 -18
View File
@@ -118,6 +118,7 @@ Status SecondaryCache::CreateFromString(
sec_cache = NewCompressedSecondaryCache(sec_cache_opts);
}
if (status.ok()) {
result->swap(sec_cache);
}
@@ -132,25 +133,19 @@ Status Cache::CreateFromString(const ConfigOptions& config_options,
std::shared_ptr<Cache>* result) {
Status status;
std::shared_ptr<Cache> cache;
if (StartsWith(value, "null")) {
cache = nullptr;
} else if (value.find("://") == std::string::npos) {
if (value.find('=') == std::string::npos) {
cache = NewLRUCache(ParseSizeT(value));
} else {
LRUCacheOptions cache_opts;
status = OptionTypeInfo::ParseStruct(config_options, "",
&lru_cache_options_type_info, "",
value, &cache_opts);
if (status.ok()) {
cache = NewLRUCache(cache_opts);
}
}
if (status.ok()) {
result->swap(cache);
}
if (value.find('=') == std::string::npos) {
cache = NewLRUCache(ParseSizeT(value));
} else {
status = LoadSharedObject<Cache>(config_options, value, result);
LRUCacheOptions cache_opts;
status = OptionTypeInfo::ParseStruct(config_options, "",
&lru_cache_options_type_info, "",
value, &cache_opts);
if (status.ok()) {
cache = NewLRUCache(cache_opts);
}
}
if (status.ok()) {
result->swap(cache);
}
return status;
}
+16 -92
View File
@@ -60,8 +60,6 @@ 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_double(compressible_to_ratio, 0.5,
"Approximate size ratio that values can be compressed to.");
DEFINE_int32(
degenerate_hash_bits, 0,
@@ -119,7 +117,7 @@ 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");
DEFINE_string(cache_type, "hyper_clock_cache", "Type of block cache.");
DEFINE_string(cache_type, "lru_cache", "Type of block cache.");
DEFINE_bool(use_jemalloc_no_dump_allocator, false,
"Whether to use JemallocNoDumpAllocator");
@@ -184,11 +182,6 @@ DEFINE_bool(sck_randomize, false,
DEFINE_bool(sck_footer_unique_id, false,
"(-stress_cache_key) Simulate using proposed footer unique id");
// ## END stress_cache_key sub-tool options ##
// ## BEGIN stress_cache_instances sub-tool options ##
DEFINE_uint32(stress_cache_instances, 0,
"If > 0, run cache instance stress test instead");
// Uses cache_size and cache_type, maybe more
// ## END stress_cache_instance sub-tool options ##
namespace ROCKSDB_NAMESPACE {
@@ -198,7 +191,8 @@ namespace {
class SharedState {
public:
explicit SharedState(CacheBench* cache_bench)
: cv_(&mu_), cache_bench_(cache_bench) {}
: cv_(&mu_),
cache_bench_(cache_bench) {}
~SharedState() = default;
@@ -298,19 +292,10 @@ struct KeyGen {
Cache::ObjectPtr createValue(Random64& rnd, MemoryAllocator* alloc) {
char* rv = AllocateBlock(FLAGS_value_bytes, alloc).release();
// Fill with some filler data, and take some CPU time, but add redundancy
// as requested for compressibility.
uint32_t random_fill_size = std::max(
uint32_t{1}, std::min(FLAGS_value_bytes,
static_cast<uint32_t>(FLAGS_compressible_to_ratio *
FLAGS_value_bytes)));
uint32_t i = 0;
for (; i < random_fill_size; i += 8) {
// 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());
}
for (; i < FLAGS_value_bytes; i++) {
rv[i] = rv[i % random_fill_size];
}
return rv;
}
@@ -325,16 +310,16 @@ Status SaveToFn(Cache::ObjectPtr from_obj, size_t /*from_offset*/,
Status CreateFn(const Slice& data, CompressionType /*type*/,
CacheTier /*source*/, Cache::CreateContext* /*context*/,
MemoryAllocator* alloc, Cache::ObjectPtr* out_obj,
MemoryAllocator* /*allocator*/, Cache::ObjectPtr* out_obj,
size_t* out_charge) {
*out_obj = AllocateBlock(data.size(), alloc).release();
*out_obj = new char[data.size()];
memcpy(*out_obj, data.data(), data.size());
*out_charge = data.size();
return Status::OK();
};
void DeleteFn(Cache::ObjectPtr value, MemoryAllocator* alloc) {
CacheAllocationDeleter{alloc}(static_cast<char*>(value));
CustomDeleter{alloc}(static_cast<char*>(value));
}
Cache::CacheItemHelper helper1_wos(CacheEntryRole::kDataBlock, DeleteFn);
@@ -392,12 +377,7 @@ class CacheBench {
fprintf(stderr, "Percentages must add to 100.\n");
exit(1);
}
cache_ = MakeCache();
}
~CacheBench() = default;
static std::shared_ptr<Cache> MakeCache() {
std::shared_ptr<MemoryAllocator> allocator;
if (FLAGS_use_jemalloc_no_dump_allocator) {
JemallocAllocatorOptions opts;
@@ -416,12 +396,12 @@ class CacheBench {
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") {
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" ||
FLAGS_cache_type == "hyper_clock_cache") {
} else if (FLAGS_cache_type == "auto_hyper_clock_cache") {
if (FLAGS_value_bytes_estimate > 0) {
opts.min_avg_entry_charge = FLAGS_value_bytes_estimate;
}
@@ -430,7 +410,7 @@ class CacheBench {
exit(1);
}
ConfigureSecondaryCache(opts);
return opts.MakeSharedCache();
cache_ = opts.MakeSharedCache();
} else if (FLAGS_cache_type == "lru_cache") {
LRUCacheOptions opts(FLAGS_cache_size, FLAGS_num_shard_bits,
false /* strict_capacity_limit */,
@@ -438,13 +418,15 @@ class CacheBench {
opts.hash_seed = BitwiseAnd(FLAGS_seed, INT32_MAX);
opts.memory_allocator = allocator;
ConfigureSecondaryCache(opts);
return NewLRUCache(opts);
cache_ = NewLRUCache(opts);
} else {
fprintf(stderr, "Cache type not supported.\n");
exit(1);
}
}
~CacheBench() = default;
void PopulateCache() {
Random64 rnd(FLAGS_seed);
KeyGen keygen;
@@ -498,7 +480,7 @@ class CacheBench {
PrintEnv();
SharedState shared(this);
std::vector<std::unique_ptr<ThreadState>> threads(FLAGS_threads);
std::vector<std::unique_ptr<ThreadState> > threads(FLAGS_threads);
for (uint32_t i = 0; i < FLAGS_threads; i++) {
threads[i].reset(new ThreadState(i, &shared));
std::thread(ThreadBody, threads[i].get()).detach();
@@ -1160,59 +1142,6 @@ class StressCacheKey {
double multiplier_ = 0.0;
};
// cache_bench -stress_cache_instances is a partially independent embedded tool
// for evaluating the time and space required to create and destroy many cache
// instances, as this is considered important for a default cache implementation
// which could see many throw-away instances in handling of Options, or created
// in large numbers for many very small DBs with many CFs. Prefix command line
// with /usr/bin/time to see max RSS memory.
class StressCacheInstances {
public:
void Run() {
const int kNumIterations = 10;
const auto clock = SystemClock::Default().get();
caches_.reserve(FLAGS_stress_cache_instances);
uint64_t total_create_time_us = 0;
uint64_t total_destroy_time_us = 0;
for (int iter = 0; iter < kNumIterations; ++iter) {
// Create many cache instances
uint64_t start_create = clock->NowMicros();
for (uint32_t i = 0; i < FLAGS_stress_cache_instances; ++i) {
caches_.emplace_back(CacheBench::MakeCache());
}
uint64_t end_create = clock->NowMicros();
uint64_t create_time = end_create - start_create;
total_create_time_us += create_time;
// Destroy them
uint64_t start_destroy = clock->NowMicros();
caches_.clear();
uint64_t end_destroy = clock->NowMicros();
uint64_t destroy_time = end_destroy - start_destroy;
total_destroy_time_us += destroy_time;
printf(
"Iteration %d: Created %u caches in %.3f ms, destroyed in %.3f ms\n",
iter + 1, FLAGS_stress_cache_instances, create_time / 1000.0,
destroy_time / 1000.0);
}
printf("Average creation time: %.3f ms (%.1f us per cache)\n",
static_cast<double>(total_create_time_us) / kNumIterations / 1000.0,
static_cast<double>(total_create_time_us) / kNumIterations /
FLAGS_stress_cache_instances);
printf("Average destruction time: %.3f ms (%.1f us per cache)\n",
static_cast<double>(total_destroy_time_us) / kNumIterations / 1000.0,
static_cast<double>(total_destroy_time_us) / kNumIterations /
FLAGS_stress_cache_instances);
}
private:
std::vector<std::shared_ptr<Cache>> caches_;
};
int cache_bench_tool(int argc, char** argv) {
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
ParseCommandLineFlags(&argc, &argv, true);
@@ -1223,11 +1152,6 @@ int cache_bench_tool(int argc, char** argv) {
return 0;
}
if (FLAGS_stress_cache_instances > 0) {
StressCacheInstances().Run();
return 0;
}
if (FLAGS_threads <= 0) {
fprintf(stderr, "threads number <= 0\n");
exit(1);
-84
View File
@@ -18,7 +18,6 @@
#include "cache/lru_cache.h"
#include "cache/typed_cache.h"
#include "port/stack_trace.h"
#include "table/block_based/block_cache.h"
#include "test_util/secondary_cache_test_util.h"
#include "test_util/testharness.h"
#include "util/coding.h"
@@ -886,32 +885,6 @@ TEST_P(CacheTest, ApplyToAllEntriesDuringResize) {
ASSERT_EQ(special_count, kSpecialCount);
}
TEST_P(CacheTest, ApplyToHandleTest) {
std::string callback_state;
const auto callback = [&](const Slice& key, Cache::ObjectPtr value,
size_t charge,
const Cache::CacheItemHelper* helper) {
callback_state = std::to_string(DecodeKey(key)) + "," +
std::to_string(DecodeValue(value)) + "," +
std::to_string(charge);
assert(helper == &CacheTest::kHelper);
};
std::vector<std::string> inserted;
for (int i = 0; i < 10; ++i) {
Insert(i, i * 2, i + 1);
inserted.push_back(std::to_string(i) + "," + std::to_string(i * 2) + "," +
std::to_string(i + 1));
}
for (int i = 0; i < 10; ++i) {
Cache::Handle* handle = cache_->Lookup(EncodeKey(i));
cache_->ApplyToHandle(cache_.get(), handle, callback);
EXPECT_EQ(inserted[i], callback_state);
cache_->Release(handle);
}
}
TEST_P(CacheTest, DefaultShardBits) {
// Prevent excessive allocation (to save time & space)
estimated_value_size_ = 100000;
@@ -1044,63 +1017,6 @@ INSTANTIATE_TEST_CASE_P(CacheTestInstance, CacheTest,
INSTANTIATE_TEST_CASE_P(CacheTestInstance, LRUCacheTest,
testing::Values(secondary_cache_test_util::kLRU));
TEST(MiscBlockCacheTest, UncacheAggressivenessAdvisor) {
// Aggressiveness to a sequence of Report() calls (as string of 0s and 1s)
// exactly until the first ShouldContinue() == false.
const std::vector<std::pair<uint32_t, Slice>> expectedTraces{
// Aggressiveness 1 aborts on first unsuccessful erasure.
{1, "0"},
{1, "11111111111111111111110"},
// For sufficient evidence, aggressiveness 2 requires a minimum of two
// unsuccessful erasures.
{2, "00"},
{2, "0110"},
{2, "1100"},
{2, "011111111111111111111111111111111111111111111111111111111111111100"},
{2, "0111111111111111111111111111111111110"},
// For sufficient evidence, aggressiveness 3 and higher require a minimum
// of three unsuccessful erasures.
{3, "000"},
{3, "01010"},
{3, "111000"},
{3, "00111111111111111111111111111111111100"},
{3, "00111111111111111111110"},
{4, "000"},
{4, "01010"},
{4, "111000"},
{4, "001111111111111111111100"},
{4, "0011111111111110"},
{6, "000"},
{6, "01010"},
{6, "111000"},
{6, "00111111111111100"},
{6, "0011111110"},
// 69 -> 50% threshold, now up to minimum of 4
{69, "0000"},
{69, "010000"},
{69, "01010000"},
{69, "101010100010101000"},
// 230 -> 10% threshold, appropriately higher minimum
{230, "000000000000"},
{230, "0000000000010000000000"},
{230, "00000000000100000000010000000000"}};
for (const auto& [aggressiveness, t] : expectedTraces) {
SCOPED_TRACE("aggressiveness=" + std::to_string(aggressiveness) + " with " +
t.ToString());
UncacheAggressivenessAdvisor uaa(aggressiveness);
for (size_t i = 0; i < t.size(); ++i) {
SCOPED_TRACE("i=" + std::to_string(i));
ASSERT_TRUE(uaa.ShouldContinue());
uaa.Report(t[i] & 1);
}
ASSERT_FALSE(uaa.ShouldContinue());
}
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+199 -205
View File
@@ -10,12 +10,12 @@
#include "cache/clock_cache.h"
#include <algorithm>
#include <atomic>
#include <bitset>
#include <cassert>
#include <cinttypes>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <exception>
#include <functional>
#include <numeric>
@@ -26,9 +26,10 @@
#include "cache/cache_key.h"
#include "cache/secondary_cache_adapter.h"
#include "logging/logging.h"
#include "port/likely.h"
#include "monitoring/perf_context_imp.h"
#include "monitoring/statistics_impl.h"
#include "port/lang.h"
#include "rocksdb/env.h"
#include "util/autovector.h"
#include "util/hash.h"
#include "util/math.h"
#include "util/random.h"
@@ -360,9 +361,16 @@ void ConstApplyToEntriesRange(const Func& func, const HandleImpl* begin,
}
}
uint32_t SanitizeEvictionEffortCap(int eviction_effort_cap) {
constexpr uint32_t kStrictCapacityLimitBit = 1u << 31;
uint32_t SanitizeEncodeEecAndScl(int eviction_effort_cap,
bool strict_capacit_limit) {
eviction_effort_cap = std::max(int{1}, eviction_effort_cap);
return static_cast<uint32_t>(eviction_effort_cap);
eviction_effort_cap =
std::min(static_cast<int>(~kStrictCapacityLimitBit), eviction_effort_cap);
uint32_t eec_and_scl = static_cast<uint32_t>(eviction_effort_cap);
eec_and_scl |= strict_capacit_limit ? kStrictCapacityLimitBit : 0;
return eec_and_scl;
}
} // namespace
@@ -373,22 +381,6 @@ void ClockHandleBasicData::FreeData(MemoryAllocator* allocator) const {
}
}
BaseClockTable::BaseClockTable(size_t capacity, bool strict_capacity_limit,
int eviction_effort_cap,
CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback,
const uint32_t* hash_seed)
: capacity_(capacity),
eec_and_scl_(EecAndScl{}
.With<EvictionEffortCap>(
SanitizeEvictionEffortCap(eviction_effort_cap))
.With<StrictCapacityLimit>(strict_capacity_limit)),
metadata_charge_policy_(metadata_charge_policy),
allocator_(allocator),
eviction_callback_(*eviction_callback),
hash_seed_(*hash_seed) {}
template <class HandleImpl>
HandleImpl* BaseClockTable::StandaloneInsert(
const ClockHandleBasicData& proto) {
@@ -410,7 +402,8 @@ HandleImpl* BaseClockTable::StandaloneInsert(
template <class Table>
typename Table::HandleImpl* BaseClockTable::CreateStandalone(
ClockHandleBasicData& proto, bool allow_uncharged) {
ClockHandleBasicData& proto, size_t capacity, uint32_t eec_and_scl,
bool allow_uncharged) {
Table& derived = static_cast<Table&>(*this);
typename Table::InsertState state;
derived.StartInsert(state);
@@ -419,10 +412,10 @@ typename Table::HandleImpl* BaseClockTable::CreateStandalone(
// NOTE: we can use eec_and_scl as eviction_effort_cap below because
// strict_capacity_limit=true is supposed to disable the limit on eviction
// effort, and a large value effectively does that.
if (eec_and_scl_.LoadRelaxed().Get<StrictCapacityLimit>()) {
if (eec_and_scl & kStrictCapacityLimitBit) {
Status s = ChargeUsageMaybeEvictStrict<Table>(
total_charge,
/*need_evict_for_occupancy=*/false, state);
total_charge, capacity,
/*need_evict_for_occupancy=*/false, eec_and_scl, state);
if (!s.ok()) {
if (allow_uncharged) {
proto.total_charge = 0;
@@ -433,8 +426,8 @@ typename Table::HandleImpl* BaseClockTable::CreateStandalone(
} else {
// Case strict_capacity_limit == false
bool success = ChargeUsageMaybeEvictNonStrict<Table>(
total_charge,
/*need_evict_for_occupancy=*/false, state);
total_charge, capacity,
/*need_evict_for_occupancy=*/false, eec_and_scl, state);
if (!success) {
// Force the issue
usage_.FetchAddRelaxed(total_charge);
@@ -446,9 +439,8 @@ typename Table::HandleImpl* BaseClockTable::CreateStandalone(
template <class Table>
Status BaseClockTable::ChargeUsageMaybeEvictStrict(
size_t total_charge, bool need_evict_for_occupancy,
typename Table::InsertState& state) {
const size_t capacity = capacity_.LoadRelaxed();
size_t total_charge, size_t capacity, bool need_evict_for_occupancy,
uint32_t eviction_effort_cap, typename Table::InsertState& state) {
if (total_charge > capacity) {
return Status::MemoryLimit(
"Cache entry too large for a single cache shard: " +
@@ -473,7 +465,8 @@ Status BaseClockTable::ChargeUsageMaybeEvictStrict(
}
if (request_evict_charge > 0) {
EvictionData data;
static_cast<Table*>(this)->Evict(request_evict_charge, state, &data);
static_cast<Table*>(this)->Evict(request_evict_charge, state, &data,
eviction_effort_cap);
occupancy_.FetchSub(data.freed_count);
if (LIKELY(data.freed_charge > need_evict_charge)) {
assert(data.freed_count > 0);
@@ -502,8 +495,8 @@ Status BaseClockTable::ChargeUsageMaybeEvictStrict(
template <class Table>
inline bool BaseClockTable::ChargeUsageMaybeEvictNonStrict(
size_t total_charge, bool need_evict_for_occupancy,
typename Table::InsertState& state) {
size_t total_charge, size_t capacity, bool need_evict_for_occupancy,
uint32_t eviction_effort_cap, typename Table::InsertState& state) {
// For simplicity, we consider that either the cache can accept the insert
// with no evictions, or we must evict enough to make (at least) enough
// space. It could lead to unnecessary failures or excessive evictions in
@@ -513,8 +506,7 @@ inline bool BaseClockTable::ChargeUsageMaybeEvictNonStrict(
// charge. Thus, we should evict some extra if it's not a signifcant
// portion of the shard capacity. This can have the side benefit of
// involving fewer threads in eviction.
const size_t old_usage = usage_.LoadRelaxed();
const size_t capacity = capacity_.LoadRelaxed();
size_t old_usage = usage_.LoadRelaxed();
size_t need_evict_charge;
// NOTE: if total_charge > old_usage, there isn't yet enough to evict
// `total_charge` amount. Even if we only try to evict `old_usage` amount,
@@ -540,7 +532,8 @@ inline bool BaseClockTable::ChargeUsageMaybeEvictNonStrict(
}
EvictionData data;
if (need_evict_charge > 0) {
static_cast<Table*>(this)->Evict(need_evict_charge, state, &data);
static_cast<Table*>(this)->Evict(need_evict_charge, state, &data,
eviction_effort_cap);
// Deal with potential occupancy deficit
if (UNLIKELY(need_evict_for_occupancy) && data.freed_count == 0) {
assert(data.freed_charge == 0);
@@ -576,10 +569,8 @@ void BaseClockTable::TrackAndReleaseEvictedEntry(ClockHandle* h) {
MarkEmpty(*h);
}
bool BaseClockTable::IsEvictionEffortExceeded(
const BaseClockTable::EvictionData& data) const {
auto eviction_effort_cap =
eec_and_scl_.LoadRelaxed().GetEffectiveEvictionEffortCap();
bool IsEvictionEffortExceeded(const BaseClockTable::EvictionData& data,
uint32_t eviction_effort_cap) {
// Basically checks whether the ratio of useful effort to wasted effort is
// too low, with a start-up allowance for wasted effort before any useful
// effort.
@@ -590,7 +581,8 @@ bool BaseClockTable::IsEvictionEffortExceeded(
template <class Table>
Status BaseClockTable::Insert(const ClockHandleBasicData& proto,
typename Table::HandleImpl** handle,
Cache::Priority priority) {
Cache::Priority priority, size_t capacity,
uint32_t eec_and_scl) {
using HandleImpl = typename Table::HandleImpl;
Table& derived = static_cast<Table&>(*this);
@@ -611,9 +603,9 @@ Status BaseClockTable::Insert(const ClockHandleBasicData& proto,
// NOTE: we can use eec_and_scl as eviction_effort_cap below because
// strict_capacity_limit=true is supposed to disable the limit on eviction
// effort, and a large value effectively does that.
if (eec_and_scl_.LoadRelaxed().Get<StrictCapacityLimit>()) {
if (eec_and_scl & kStrictCapacityLimitBit) {
Status s = ChargeUsageMaybeEvictStrict<Table>(
total_charge, need_evict_for_occupancy, state);
total_charge, capacity, need_evict_for_occupancy, eec_and_scl, state);
if (!s.ok()) {
// Revert occupancy
occupancy_.FetchSubRelaxed(1);
@@ -622,7 +614,7 @@ Status BaseClockTable::Insert(const ClockHandleBasicData& proto,
} else {
// Case strict_capacity_limit == false
bool success = ChargeUsageMaybeEvictNonStrict<Table>(
total_charge, need_evict_for_occupancy, state);
total_charge, capacity, need_evict_for_occupancy, eec_and_scl, state);
if (!success) {
// Revert occupancy
occupancy_.FetchSubRelaxed(1);
@@ -726,13 +718,11 @@ void BaseClockTable::TEST_ReleaseNMinus1(ClockHandle* h, size_t n) {
#endif
FixedHyperClockTable::FixedHyperClockTable(
size_t capacity, bool strict_capacity_limit,
CacheMetadataChargePolicy metadata_charge_policy,
size_t capacity, CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback, const uint32_t* hash_seed,
const Opts& opts)
: BaseClockTable(capacity, strict_capacity_limit, opts.eviction_effort_cap,
metadata_charge_policy, allocator, eviction_callback,
: BaseClockTable(metadata_charge_policy, allocator, eviction_callback,
hash_seed),
length_bits_(CalcHashBits(capacity, opts.estimated_value_size,
metadata_charge_policy)),
@@ -1123,7 +1113,8 @@ inline void FixedHyperClockTable::ReclaimEntryUsage(size_t total_charge) {
}
inline void FixedHyperClockTable::Evict(size_t requested_charge, InsertState&,
EvictionData* data) {
EvictionData* data,
uint32_t eviction_effort_cap) {
// precondition
assert(requested_charge > 0);
@@ -1158,7 +1149,7 @@ inline void FixedHyperClockTable::Evict(size_t requested_charge, InsertState&,
if (old_clock_pointer >= max_clock_pointer) {
return;
}
if (IsEvictionEffortExceeded(*data)) {
if (IsEvictionEffortExceeded(*data, eviction_effort_cap)) {
eviction_effort_exceeded_count_.FetchAddRelaxed(1);
return;
}
@@ -1176,11 +1167,14 @@ ClockCacheShard<Table>::ClockCacheShard(
const Cache::EvictionCallback* eviction_callback, const uint32_t* hash_seed,
const typename Table::Opts& opts)
: CacheShardBase(metadata_charge_policy),
table_(capacity, strict_capacity_limit, metadata_charge_policy, allocator,
eviction_callback, hash_seed, opts) {
table_(capacity, metadata_charge_policy, allocator, eviction_callback,
hash_seed, opts),
capacity_(capacity),
eec_and_scl_(SanitizeEncodeEecAndScl(opts.eviction_effort_cap,
strict_capacity_limit)) {
// Initial charge metadata should not exceed capacity
assert(table_.GetUsage() <= table_.GetCapacity() ||
table_.GetCapacity() < sizeof(HandleImpl));
assert(table_.GetUsage() <= capacity_.LoadRelaxed() ||
capacity_.LoadRelaxed() < sizeof(HandleImpl));
}
template <class Table>
@@ -1246,14 +1240,18 @@ int FixedHyperClockTable::CalcHashBits(
template <class Table>
void ClockCacheShard<Table>::SetCapacity(size_t capacity) {
table_.SetCapacity(capacity);
capacity_.StoreRelaxed(capacity);
// next Insert will take care of any necessary evictions
}
template <class Table>
void ClockCacheShard<Table>::SetStrictCapacityLimit(
bool strict_capacity_limit) {
table_.SetStrictCapacityLimit(strict_capacity_limit);
if (strict_capacity_limit) {
eec_and_scl_.FetchOrRelaxed(kStrictCapacityLimitBit);
} else {
eec_and_scl_.FetchAndRelaxed(~kStrictCapacityLimitBit);
}
// next Insert will take care of any necessary evictions
}
@@ -1273,7 +1271,9 @@ Status ClockCacheShard<Table>::Insert(const Slice& key,
proto.value = value;
proto.helper = helper;
proto.total_charge = charge;
return table_.template Insert<Table>(proto, handle, priority);
return table_.template Insert<Table>(proto, handle, priority,
capacity_.LoadRelaxed(),
eec_and_scl_.LoadRelaxed());
}
template <class Table>
@@ -1288,7 +1288,9 @@ typename Table::HandleImpl* ClockCacheShard<Table>::CreateStandalone(
proto.value = obj;
proto.helper = helper;
proto.total_charge = charge;
return table_.template CreateStandalone<Table>(proto, allow_uncharged);
return table_.template CreateStandalone<Table>(proto, capacity_.LoadRelaxed(),
eec_and_scl_.LoadRelaxed(),
allow_uncharged);
}
template <class Table>
@@ -1357,7 +1359,7 @@ size_t ClockCacheShard<Table>::GetStandaloneUsage() const {
template <class Table>
size_t ClockCacheShard<Table>::GetCapacity() const {
return table_.GetCapacity();
return capacity_.LoadRelaxed();
}
template <class Table>
@@ -1442,22 +1444,6 @@ const Cache::CacheItemHelper* BaseHyperClockCache<Table>::GetCacheItemHelper(
return h->helper;
}
template <class Table>
void BaseHyperClockCache<Table>::ApplyToHandle(
Cache* cache, Handle* handle,
const std::function<void(const Slice& key, Cache::ObjectPtr value,
size_t charge, const CacheItemHelper* helper)>&
callback) {
BaseHyperClockCache<Table>* cache_ptr =
static_cast<BaseHyperClockCache<Table>*>(cache);
auto h = static_cast<const typename Table::HandleImpl*>(handle);
UniqueId64x2 unhashed;
auto hash_seed = cache_ptr->GetShard(h->GetHash()).GetTable().GetHashSeed();
callback(
ClockCacheShard<Table>::ReverseHash(h->hashed_key, &unhashed, hash_seed),
h->value, h->GetTotalCharge(), h->helper);
}
namespace {
// For each cache shard, estimate what the table load factor would be if
@@ -1725,13 +1711,10 @@ inline uint64_t UsedLengthToLengthInfo(size_t used_length) {
return length_info;
}
// Avoid potential initialization order race with port::kPageSize
constexpr size_t kPresumedPageSize = 4096;
inline size_t GetStartingLength(size_t capacity) {
if (capacity > kPresumedPageSize) {
if (capacity > port::kPageSize) {
// Start with one memory page
return kPresumedPageSize / sizeof(AutoHyperClockTable::HandleImpl);
return port::kPageSize / sizeof(AutoHyperClockTable::HandleImpl);
} else {
// Mostly to make unit tests happy
return 4;
@@ -1752,6 +1735,26 @@ inline void GetHomeIndexAndShift(uint64_t length_info, uint64_t hash,
assert(*home < LengthInfoToUsedLength(length_info));
}
inline int GetShiftFromNextWithShift(uint64_t next_with_shift) {
return BitwiseAnd(next_with_shift,
AutoHyperClockTable::HandleImpl::kShiftMask);
}
inline size_t GetNextFromNextWithShift(uint64_t next_with_shift) {
return static_cast<size_t>(next_with_shift >>
AutoHyperClockTable::HandleImpl::kNextShift);
}
inline uint64_t MakeNextWithShift(size_t next, int shift) {
return (uint64_t{next} << AutoHyperClockTable::HandleImpl::kNextShift) |
static_cast<uint64_t>(shift);
}
inline uint64_t MakeNextWithShiftEnd(size_t head, int shift) {
return AutoHyperClockTable::HandleImpl::kNextEndFlags |
MakeNextWithShift(head, shift);
}
// Helper function for Lookup
inline bool MatchAndRef(const UniqueId64x2* hashed_key, const ClockHandle& h,
int shift = 0, size_t home = 0,
@@ -1801,39 +1804,36 @@ inline bool MatchAndRef(const UniqueId64x2* hashed_key, const ClockHandle& h,
}
}
using NextWithShift = AutoHyperClockTable::HandleImpl::NextWithShift;
// Assumes a chain rewrite lock prevents concurrent modification of
// these chain pointers
void UpgradeShiftsOnRange(AutoHyperClockTable::HandleImpl* arr,
size_t& frontier,
NextWithShift stop_before_or_new_tail, int old_shift,
int new_shift) {
size_t& frontier, uint64_t stop_before_or_new_tail,
int old_shift, int new_shift) {
assert(frontier != SIZE_MAX);
assert(new_shift == old_shift + 1);
(void)old_shift;
(void)new_shift;
using HandleImpl = AutoHyperClockTable::HandleImpl;
for (;;) {
NextWithShift next_with_shift = arr[frontier].chain_next_with_shift.Load();
assert(next_with_shift.GetShift() == old_shift);
uint64_t next_with_shift = arr[frontier].chain_next_with_shift.Load();
assert(GetShiftFromNextWithShift(next_with_shift) == old_shift);
if (next_with_shift == stop_before_or_new_tail) {
// Stopping at entry with pointer matching "stop before"
assert(!next_with_shift.IsEnd());
assert(!HandleImpl::IsEnd(next_with_shift));
return;
}
if (next_with_shift.IsEnd()) {
if (HandleImpl::IsEnd(next_with_shift)) {
// Also update tail to new tail
assert(stop_before_or_new_tail.IsEnd());
assert(HandleImpl::IsEnd(stop_before_or_new_tail));
arr[frontier].chain_next_with_shift.Store(stop_before_or_new_tail);
// Mark nothing left to upgrade
frontier = SIZE_MAX;
return;
}
// Next is another entry to process, so upgrade and advance frontier
arr[frontier].chain_next_with_shift.Apply(
NextWithShift::Shift::PlusTransformPromiseNoOverflow(1U));
assert(next_with_shift.GetShift() + 1 == new_shift);
frontier = next_with_shift.GetNext();
arr[frontier].chain_next_with_shift.FetchAdd(1U);
assert(GetShiftFromNextWithShift(next_with_shift + 1) == new_shift);
frontier = GetNextFromNextWithShift(next_with_shift);
}
}
@@ -1871,19 +1871,19 @@ class AutoHyperClockTable::ChainRewriteLock {
// RAII wrap existing lock held (or end)
explicit ChainRewriteLock(HandleImpl* h,
RelaxedAtomic<uint64_t>& /*yield_count*/,
NextWithShift already_locked_or_end)
uint64_t already_locked_or_end)
: head_ptr_(&h->head_next_with_shift) {
saved_head_ = already_locked_or_end;
// already locked or end
assert(saved_head_.IsLocked());
assert(saved_head_ & HandleImpl::kHeadLocked);
}
~ChainRewriteLock() {
if (!IsEnd()) {
// Release lock
NextWithShift old;
head_ptr_->Apply(NextWithShift::LockedFlag::ClearTransform(), &old);
assert(old.IsLockedNotEnd());
uint64_t old = head_ptr_->FetchAnd(~HandleImpl::kHeadLocked);
(void)old;
assert((old & HandleImpl::kNextEndFlags) == HandleImpl::kHeadLocked);
}
}
@@ -1893,13 +1893,12 @@ class AutoHyperClockTable::ChainRewriteLock {
}
// Expected current state, assuming no parallel updates.
NextWithShift GetSavedHead() const { return saved_head_; }
uint64_t GetSavedHead() const { return saved_head_; }
bool CasUpdate(NextWithShift next_with_shift,
bool CasUpdate(uint64_t next_with_shift,
RelaxedAtomic<uint64_t>& yield_count) {
NextWithShift new_head =
next_with_shift.With<NextWithShift::LockedFlag>(true);
NextWithShift expected = GetSavedHead();
uint64_t new_head = next_with_shift | HandleImpl::kHeadLocked;
uint64_t expected = GetSavedHead();
bool success = head_ptr_->CasStrong(expected, new_head);
if (success) {
// Ensure IsEnd() is kept up-to-date, including for dtor
@@ -1908,7 +1907,7 @@ class AutoHyperClockTable::ChainRewriteLock {
// Parallel update to head, such as Insert()
if (IsEnd()) {
// Didn't previously hold a lock
if (expected.IsEnd()) {
if (HandleImpl::IsEnd(expected)) {
// Still don't need to
saved_head_ = expected;
} else {
@@ -1917,25 +1916,28 @@ class AutoHyperClockTable::ChainRewriteLock {
}
} else {
// Parallel update must preserve our lock
assert(expected.IsLockedNotEnd());
assert((expected & HandleImpl::kNextEndFlags) ==
HandleImpl::kHeadLocked);
saved_head_ = expected;
}
}
return success;
}
bool IsEnd() const { return saved_head_.IsEnd(); }
bool IsEnd() const { return HandleImpl::IsEnd(saved_head_); }
private:
void Acquire(RelaxedAtomic<uint64_t>& yield_count) {
for (;;) {
// Acquire removal lock on the chain
NextWithShift old_head;
head_ptr_->Apply(NextWithShift::LockedFlag::SetTransform(), &old_head,
&saved_head_);
if (!old_head.IsLockedNotEnd()) {
uint64_t old_head = head_ptr_->FetchOr(HandleImpl::kHeadLocked);
if ((old_head & HandleImpl::kNextEndFlags) != HandleImpl::kHeadLocked) {
// Either acquired the lock or lock not needed (end)
assert(old_head.IsEnd() == old_head.IsLocked());
assert((old_head & HandleImpl::kNextEndFlags) == 0 ||
(old_head & HandleImpl::kNextEndFlags) ==
HandleImpl::kNextEndFlags);
saved_head_ = old_head | HandleImpl::kHeadLocked;
break;
}
// NOTE: one of the few yield-wait loops, which is rare enough in practice
@@ -1946,18 +1948,16 @@ class AutoHyperClockTable::ChainRewriteLock {
}
}
AcqRelBitFieldsAtomic<NextWithShift>* head_ptr_;
NextWithShift saved_head_;
AcqRelAtomic<uint64_t>* head_ptr_;
uint64_t saved_head_;
};
AutoHyperClockTable::AutoHyperClockTable(
size_t capacity, bool strict_capacity_limit,
CacheMetadataChargePolicy metadata_charge_policy,
size_t capacity, CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback, const uint32_t* hash_seed,
const Opts& opts)
: BaseClockTable(capacity, strict_capacity_limit, opts.eviction_effort_cap,
metadata_charge_policy, allocator, eviction_callback,
: BaseClockTable(metadata_charge_policy, allocator, eviction_callback,
hash_seed),
array_(MemMapping::AllocateLazyZeroed(
sizeof(HandleImpl) * CalcMaxUsableLength(capacity,
@@ -1969,11 +1969,6 @@ AutoHyperClockTable::AutoHyperClockTable(
grow_frontier_(GetTableSize()),
clock_pointer_mask_(
BottomNBits(UINT64_MAX, LengthInfoToMinShift(length_info_.Load()))) {
if (array_.Get() == nullptr) {
fprintf(stderr,
"Anonymous mmap for RocksDB HyperClockCache failed. Aborting.\n");
std::terminate();
}
if (metadata_charge_policy ==
CacheMetadataChargePolicy::kFullChargeCacheMetadata) {
// NOTE: ignoring page boundaries for simplicity
@@ -2002,9 +1997,9 @@ AutoHyperClockTable::AutoHyperClockTable(
#endif
if (major + i < used_length) {
array_[i].head_next_with_shift.StoreRelaxed(
NextWithShift::MakeEnd(i, max_shift));
MakeNextWithShiftEnd(i, max_shift));
array_[major + i].head_next_with_shift.StoreRelaxed(
NextWithShift::MakeEnd(major + i, max_shift));
MakeNextWithShiftEnd(major + i, max_shift));
#ifndef NDEBUG // Extra invariant checking
GetHomeIndexAndShift(length_info, i, &home, &shift);
assert(home == i);
@@ -2015,7 +2010,7 @@ AutoHyperClockTable::AutoHyperClockTable(
#endif
} else {
array_[i].head_next_with_shift.StoreRelaxed(
NextWithShift::MakeEnd(i, min_shift));
MakeNextWithShiftEnd(i, min_shift));
#ifndef NDEBUG // Extra invariant checking
GetHomeIndexAndShift(length_info, i, &home, &shift);
assert(home == i);
@@ -2041,23 +2036,15 @@ AutoHyperClockTable::~AutoHyperClockTable() {
HandleImpl::kUnusedMarker) {
used_end++;
}
// This check can be extra expensive for a cache that is just created,
// maybe used for a small number of entries, as in a unit test, and then
// destroyed. Only do this in rare modes. REVISED: Don't scan the whole mmap,
// just a reasonable frontier past what we expect to have written.
#ifdef MUST_FREE_HEAP_ALLOCATIONS
for (size_t i = used_end; i < array_.Count() && i < used_end + 64U; i++) {
assert(array_[i].head_next_with_shift.LoadRelaxed() ==
HandleImpl::kUnusedMarker);
assert(array_[i].chain_next_with_shift.LoadRelaxed() ==
HandleImpl::kUnusedMarker);
#ifndef NDEBUG
for (size_t i = used_end; i < array_.Count(); i++) {
assert(array_[i].head_next_with_shift.LoadRelaxed() == 0);
assert(array_[i].chain_next_with_shift.LoadRelaxed() == 0);
assert(array_[i].meta.LoadRelaxed() == 0);
}
#endif // MUST_FREE_HEAP_ALLOCATIONS
#ifndef NDEBUG // Extra invariant checking
std::vector<bool> was_populated(used_end);
std::vector<bool> was_pointed_to(used_end);
#endif // !NDEBUG
#endif
for (size_t i = 0; i < used_end; i++) {
HandleImpl& h = array_[i];
switch (h.meta.LoadRelaxed() >> ClockHandle::kStateShift) {
@@ -2072,13 +2059,15 @@ AutoHyperClockTable::~AutoHyperClockTable() {
usage_.FetchSubRelaxed(h.total_charge);
occupancy_.FetchSubRelaxed(1U);
was_populated[i] = true;
if (!h.chain_next_with_shift.LoadRelaxed().IsEnd()) {
assert(!h.chain_next_with_shift.LoadRelaxed().IsLocked());
size_t next = h.chain_next_with_shift.LoadRelaxed().GetNext();
if (!HandleImpl::IsEnd(h.chain_next_with_shift.LoadRelaxed())) {
assert((h.chain_next_with_shift.LoadRelaxed() &
HandleImpl::kHeadLocked) == 0);
size_t next =
GetNextFromNextWithShift(h.chain_next_with_shift.LoadRelaxed());
assert(!was_pointed_to[next]);
was_pointed_to[next] = true;
}
#endif // !NDEBUG
#endif
break;
// otherwise
default:
@@ -2086,12 +2075,13 @@ AutoHyperClockTable::~AutoHyperClockTable() {
break;
}
#ifndef NDEBUG // Extra invariant checking
if (!h.head_next_with_shift.LoadRelaxed().IsEnd()) {
size_t next = h.head_next_with_shift.LoadRelaxed().GetNext();
if (!HandleImpl::IsEnd(h.head_next_with_shift.LoadRelaxed())) {
size_t next =
GetNextFromNextWithShift(h.head_next_with_shift.LoadRelaxed());
assert(!was_pointed_to[next]);
was_pointed_to[next] = true;
}
#endif // !NDEBUG
#endif
}
#ifndef NDEBUG // Extra invariant checking
// This check is not perfect, but should detect most reasonable cases
@@ -2104,7 +2094,7 @@ AutoHyperClockTable::~AutoHyperClockTable() {
assert(!was_pointed_to[i]);
}
}
#endif // !NDEBUG
#endif
// Metadata charging only follows the published table size
assert(usage_.LoadRelaxed() == 0 ||
@@ -2202,10 +2192,10 @@ bool AutoHyperClockTable::Grow(InsertState& state) {
// chain rewrite lock has been released.
size_t old_old_home = BottomNBits(grow_home, old_shift - 1);
for (;;) {
NextWithShift old_old_head =
array_[old_old_home].head_next_with_shift.Load();
if (old_old_head.GetShift() >= old_shift) {
if (!old_old_head.IsLockedNotEnd()) {
uint64_t old_old_head = array_[old_old_home].head_next_with_shift.Load();
if (GetShiftFromNextWithShift(old_old_head) >= old_shift) {
if ((old_old_head & HandleImpl::kNextEndFlags) !=
HandleImpl::kHeadLocked) {
break;
}
}
@@ -2265,7 +2255,8 @@ void AutoHyperClockTable::CatchUpLengthInfoNoWait(
if (published_usable_size < known_usable_grow_home) {
int old_shift = FloorLog2(next_usable_size - 1);
size_t old_home = BottomNBits(published_usable_size, old_shift);
int shift = array_[old_home].head_next_with_shift.Load().GetShift();
int shift = GetShiftFromNextWithShift(
array_[old_home].head_next_with_shift.Load());
if (shift <= old_shift) {
// Not ready
break;
@@ -2416,10 +2407,9 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
ChainRewriteLock zero_head_lock(&arr[old_home], yield_count_);
// Used for locking the one chain below
NextWithShift saved_one_head;
uint64_t saved_one_head;
// One head has not been written to
assert(arr[grow_home].head_next_with_shift.Load() ==
HandleImpl::kUnusedMarker);
assert(arr[grow_home].head_next_with_shift.Load() == 0);
// old_home will also the head of the new "zero chain" -- all entries in the
// "from" chain whose next hash bit is 0. grow_home will be head of the new
@@ -2441,7 +2431,7 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
assert(cur == SIZE_MAX);
assert(chain_frontier_first == -1);
NextWithShift next_with_shift = zero_head_lock.GetSavedHead();
uint64_t next_with_shift = zero_head_lock.GetSavedHead();
// Find a single representative for each target chain, or scan the whole
// chain if some target chain has no representative.
@@ -2454,16 +2444,16 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
assert((cur == SIZE_MAX) == (zero_chain_frontier == SIZE_MAX &&
one_chain_frontier == SIZE_MAX));
assert(next_with_shift.GetShift() == old_shift);
assert(GetShiftFromNextWithShift(next_with_shift) == old_shift);
// Check for end of original chain
if (next_with_shift.IsEnd()) {
if (HandleImpl::IsEnd(next_with_shift)) {
cur = SIZE_MAX;
break;
}
// next_with_shift is not End
cur = next_with_shift.GetNext();
cur = GetNextFromNextWithShift(next_with_shift);
if (BottomNBits(arr[cur].hashed_key[1], new_shift) == old_home) {
// Entry for zero chain
@@ -2502,10 +2492,10 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
(zero_chain_frontier == SIZE_MAX && one_chain_frontier == SIZE_MAX));
// Always update one chain's head first (safe), and mark it as locked
saved_one_head = one_chain_frontier != SIZE_MAX
? NextWithShift::Make(one_chain_frontier, new_shift)
: NextWithShift::MakeEnd(grow_home, new_shift);
saved_one_head.Set<NextWithShift::LockedFlag>(true);
saved_one_head = HandleImpl::kHeadLocked |
(one_chain_frontier != SIZE_MAX
? MakeNextWithShift(one_chain_frontier, new_shift)
: MakeNextWithShiftEnd(grow_home, new_shift));
arr[grow_home].head_next_with_shift.Store(saved_one_head);
// Make sure length_info_ hasn't been updated too early, as we're about
@@ -2515,8 +2505,8 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
// Try to set zero's head.
if (zero_head_lock.CasUpdate(
zero_chain_frontier != SIZE_MAX
? NextWithShift::Make(zero_chain_frontier, new_shift)
: NextWithShift::MakeEnd(old_home, new_shift),
? MakeNextWithShift(zero_chain_frontier, new_shift)
: MakeNextWithShiftEnd(old_home, new_shift),
yield_count_)) {
// Both heads successfully updated to new shift
break;
@@ -2550,10 +2540,10 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
size_t& other_frontier = chain_frontier_first != 0
? /*&*/ zero_chain_frontier
: /*&*/ one_chain_frontier;
NextWithShift stop_before_or_new_tail =
uint64_t stop_before_or_new_tail =
other_frontier != SIZE_MAX
? /*stop before*/ NextWithShift::Make(other_frontier, old_shift)
: /*new tail*/ NextWithShift::MakeEnd(
? /*stop before*/ MakeNextWithShift(other_frontier, old_shift)
: /*new tail*/ MakeNextWithShiftEnd(
chain_frontier_first == 0 ? old_home : grow_home, new_shift);
UpgradeShiftsOnRange(arr, first_frontier, stop_before_or_new_tail,
old_shift, new_shift);
@@ -2579,19 +2569,20 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
? /*&*/ zero_chain_frontier
: /*&*/ one_chain_frontier;
assert(cur != first_frontier);
assert(arr[first_frontier].chain_next_with_shift.Load().GetNext() ==
assert(GetNextFromNextWithShift(
arr[first_frontier].chain_next_with_shift.Load()) ==
other_frontier);
NextWithShift next_with_shift = arr[cur].chain_next_with_shift.Load();
uint64_t next_with_shift = arr[cur].chain_next_with_shift.Load();
// Check for end of original chain
if (next_with_shift.IsEnd()) {
if (HandleImpl::IsEnd(next_with_shift)) {
// Can set upgraded tail on first chain
NextWithShift first_new_tail = NextWithShift::MakeEnd(
uint64_t first_new_tail = MakeNextWithShiftEnd(
chain_frontier_first == 0 ? old_home : grow_home, new_shift);
arr[first_frontier].chain_next_with_shift.Store(first_new_tail);
// And upgrade remainder of other chain
NextWithShift other_new_tail = NextWithShift::MakeEnd(
uint64_t other_new_tail = MakeNextWithShiftEnd(
chain_frontier_first != 0 ? old_home : grow_home, new_shift);
UpgradeShiftsOnRange(arr, other_frontier, other_new_tail, old_shift,
new_shift);
@@ -2600,7 +2591,7 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
}
// next_with_shift is not End
cur = next_with_shift.GetNext();
cur = GetNextFromNextWithShift(next_with_shift);
int target_chain;
if (BottomNBits(arr[cur].hashed_key[1], new_shift) == old_home) {
@@ -2613,7 +2604,7 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
}
if (target_chain == chain_frontier_first) {
// Found next entry to skip to on the first chain
NextWithShift skip_to = NextWithShift::Make(cur, new_shift);
uint64_t skip_to = MakeNextWithShift(cur, new_shift);
arr[first_frontier].chain_next_with_shift.Store(skip_to);
first_frontier = cur;
// Upgrade other chain up to entry before that one
@@ -2654,17 +2645,17 @@ void AutoHyperClockTable::PurgeImplLocked(OpData* op_data,
HandleImpl* const arr = array_.Get();
NextWithShift next_with_shift = rewrite_lock.GetSavedHead();
assert(!next_with_shift.IsEnd());
int home_shift = next_with_shift.GetShift();
uint64_t next_with_shift = rewrite_lock.GetSavedHead();
assert(!HandleImpl::IsEnd(next_with_shift));
int home_shift = GetShiftFromNextWithShift(next_with_shift);
(void)home;
(void)home_shift;
size_t next = next_with_shift.GetNext();
size_t next = GetNextFromNextWithShift(next_with_shift);
assert(next < array_.Count());
HandleImpl* h = &arr[next];
HandleImpl* prev_to_keep = nullptr;
#ifndef NDEBUG
NextWithShift prev_to_keep_next_with_shift{};
uint64_t prev_to_keep_next_with_shift = 0;
#endif
// Whether there are entries between h and prev_to_keep that should be
// purged from the chain.
@@ -2722,13 +2713,13 @@ void AutoHyperClockTable::PurgeImplLocked(OpData* op_data,
// update any new entries just inserted in parallel.
// Can simply restart (GetSavedHead() already updated from CAS failure).
next_with_shift = rewrite_lock.GetSavedHead();
assert(!next_with_shift.IsEnd());
next = next_with_shift.GetNext();
assert(!HandleImpl::IsEnd(next_with_shift));
next = GetNextFromNextWithShift(next_with_shift);
assert(next < array_.Count());
h = &arr[next];
pending_purge = false;
assert(prev_to_keep == nullptr);
assert(next_with_shift.GetShift() == home_shift);
assert(GetShiftFromNextWithShift(next_with_shift) == home_shift);
continue;
}
pending_purge = false;
@@ -2750,13 +2741,13 @@ void AutoHyperClockTable::PurgeImplLocked(OpData* op_data,
}
#endif
assert(next_with_shift.GetShift() == home_shift);
assert(GetShiftFromNextWithShift(next_with_shift) == home_shift);
// Check for end marker
if (next_with_shift.IsEnd()) {
if (HandleImpl::IsEnd(next_with_shift)) {
h = nullptr;
} else {
next = next_with_shift.GetNext();
next = GetNextFromNextWithShift(next_with_shift);
assert(next < array_.Count());
h = &arr[next];
assert(h != prev_to_keep);
@@ -2828,7 +2819,7 @@ void AutoHyperClockTable::PurgeImpl(OpData* op_data, size_t home,
// Ensure we are at the correct home for the shift in effect for the
// chain head.
for (;;) {
int shift = rewrite_lock.GetSavedHead().GetShift();
int shift = GetShiftFromNextWithShift(rewrite_lock.GetSavedHead());
if (shift > home_shift) {
// Found a newer shift at candidate head, which must apply to us.
@@ -3024,14 +3015,14 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::DoInsert(
}
// Now insert into chain using head pointer
NextWithShift next_with_shift;
uint64_t next_with_shift;
int home_shift = orig_home_shift;
// Might need to retry
for (int i = 0;; ++i) {
CHECK_TOO_MANY_ITERATIONS(i);
next_with_shift = arr[home].head_next_with_shift.Load();
int shift = next_with_shift.GetShift();
int shift = GetShiftFromNextWithShift(next_with_shift);
if (UNLIKELY(shift != home_shift)) {
// NOTE: shift increases with table growth
@@ -3058,14 +3049,15 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::DoInsert(
}
// Values to update to
NextWithShift head_next_with_shift = NextWithShift::Make(idx, home_shift);
NextWithShift chain_next_with_shift = next_with_shift;
uint64_t head_next_with_shift = MakeNextWithShift(idx, home_shift);
uint64_t chain_next_with_shift = next_with_shift;
// Preserve the locked state in head, without propagating to chain next
// where it is meaningless (and not allowed)
if (UNLIKELY(next_with_shift.IsLockedNotEnd())) {
head_next_with_shift.Set<NextWithShift::LockedFlag>(true);
chain_next_with_shift.Set<NextWithShift::LockedFlag>(false);
if (UNLIKELY((next_with_shift & HandleImpl::kNextEndFlags) ==
HandleImpl::kHeadLocked)) {
head_next_with_shift |= HandleImpl::kHeadLocked;
chain_next_with_shift &= ~HandleImpl::kHeadLocked;
}
arr[idx].chain_next_with_shift.Store(chain_next_with_shift);
@@ -3134,9 +3126,9 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::Lookup(
// of a loop as possible.
HandleImpl* const arr = array_.Get();
NextWithShift next_with_shift = arr[home].head_next_with_shift.LoadRelaxed();
for (size_t i = 0; !next_with_shift.IsEnd() && i < 10; ++i) {
HandleImpl* h = &arr[next_with_shift.IsEnd()];
uint64_t next_with_shift = arr[home].head_next_with_shift.LoadRelaxed();
for (size_t i = 0; !HandleImpl::IsEnd(next_with_shift) && i < 10; ++i) {
HandleImpl* h = &arr[GetNextFromNextWithShift(next_with_shift)];
// Attempt cheap key match without acquiring a read ref. This could give a
// false positive, which is re-checked after acquiring read ref, or false
// negative, which is re-checked in the full Lookup. Also, this is a
@@ -3181,7 +3173,7 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::Lookup(
// Read head or chain pointer
next_with_shift = h ? h->chain_next_with_shift.Load()
: arr[home].head_next_with_shift.Load();
int shift = next_with_shift.GetShift();
int shift = GetShiftFromNextWithShift(next_with_shift);
// Make sure it's usable
size_t effective_home = home;
@@ -3235,10 +3227,10 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::Lookup(
}
// Check for end marker
if (next_with_shift.IsEnd()) {
if (HandleImpl::IsEnd(next_with_shift)) {
// To ensure we didn't miss anything in the chain, the end marker must
// point back to the correct home.
if (LIKELY(next_with_shift.GetNext() == effective_home)) {
if (LIKELY(GetNextFromNextWithShift(next_with_shift) == effective_home)) {
// Complete, clean iteration of the chain, not found.
// Clean up.
if (read_ref_on_chain) {
@@ -3254,7 +3246,7 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::Lookup(
}
// Follow the next and check for full key match, home match, or neither
h = &arr[next_with_shift.GetNext()];
h = &arr[GetNextFromNextWithShift(next_with_shift)];
bool full_match_or_unknown = false;
if (MatchAndRef(&hashed_key, *h, shift, effective_home,
&full_match_or_unknown)) {
@@ -3460,7 +3452,8 @@ void AutoHyperClockTable::EraseUnRefEntries() {
}
void AutoHyperClockTable::Evict(size_t requested_charge, InsertState& state,
EvictionData* data) {
EvictionData* data,
uint32_t eviction_effort_cap) {
// precondition
assert(requested_charge > 0);
@@ -3552,7 +3545,7 @@ void AutoHyperClockTable::Evict(size_t requested_charge, InsertState& state,
return;
}
if (IsEvictionEffortExceeded(*data)) {
if (IsEvictionEffortExceeded(*data, eviction_effort_cap)) {
eviction_effort_exceeded_count_.FetchAddRelaxed(1);
return;
}
@@ -3570,7 +3563,7 @@ size_t AutoHyperClockTable::CalcMaxUsableLength(
size_t num_slots =
static_cast<size_t>(capacity / min_avg_slot_charge + 0.999999);
const size_t slots_per_page = kPresumedPageSize / sizeof(HandleImpl);
const size_t slots_per_page = port::kPageSize / sizeof(HandleImpl);
// Round up to page size
return ((num_slots + slots_per_page - 1) / slots_per_page) * slots_per_page;
@@ -3578,7 +3571,8 @@ size_t AutoHyperClockTable::CalcMaxUsableLength(
namespace {
bool IsHeadNonempty(const AutoHyperClockTable::HandleImpl& h) {
return !h.head_next_with_shift.LoadRelaxed().IsEnd();
return !AutoHyperClockTable::HandleImpl::IsEnd(
h.head_next_with_shift.LoadRelaxed());
}
bool IsEntryAtHome(const AutoHyperClockTable::HandleImpl& h, int shift,
size_t home) {
+63 -94
View File
@@ -9,6 +9,8 @@
#pragma once
#include <array>
#include <atomic>
#include <climits>
#include <cstddef>
#include <cstdint>
@@ -17,10 +19,14 @@
#include "cache/cache_key.h"
#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/bit_fields.h"
#include "util/autovector.h"
#include "util/math.h"
namespace ROCKSDB_NAMESPACE {
@@ -377,20 +383,25 @@ class BaseClockTable {
int eviction_effort_cap;
};
BaseClockTable(size_t capacity, bool strict_capacity_limit,
int eviction_effort_cap,
CacheMetadataChargePolicy metadata_charge_policy,
BaseClockTable(CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback,
const uint32_t* hash_seed);
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);
typename Table::HandleImpl** handle, Cache::Priority priority,
size_t capacity, uint32_t eec_and_scl);
void Ref(ClockHandle& handle);
@@ -400,18 +411,6 @@ class BaseClockTable {
size_t GetStandaloneUsage() const { return standalone_usage_.LoadRelaxed(); }
size_t GetCapacity() const { return capacity_.LoadRelaxed(); }
void SetCapacity(size_t capacity) { capacity_.StoreRelaxed(capacity); }
void SetStrictCapacityLimit(bool strict_capacity_limit) {
if (strict_capacity_limit) {
eec_and_scl_.ApplyRelaxed(StrictCapacityLimit::SetTransform());
} else {
eec_and_scl_.ApplyRelaxed(StrictCapacityLimit::ClearTransform());
}
}
uint32_t GetHashSeed() const { return hash_seed_; }
uint64_t GetYieldCount() const { return yield_count_.LoadRelaxed(); }
@@ -428,7 +427,6 @@ class BaseClockTable {
void TrackAndReleaseEvictedEntry(ClockHandle* h);
bool IsEvictionEffortExceeded(const BaseClockTable::EvictionData& data) const;
#ifndef NDEBUG
// Acquire N references
void TEST_RefN(ClockHandle& handle, size_t n);
@@ -450,8 +448,9 @@ class BaseClockTable {
// 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,
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`
@@ -463,8 +462,9 @@ class BaseClockTable {
// true, indicating success.
// NOTE: occupancy_ is not managed in this function
template <class Table>
bool ChargeUsageMaybeEvictNonStrict(size_t total_charge,
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
@@ -497,25 +497,6 @@ class BaseClockTable {
// Part of usage by standalone entries (not in table)
AcqRelAtomic<size_t> standalone_usage_{};
// Maximum total charge of all elements stored in the table.
// (Relaxed: eventual consistency/update is OK)
RelaxedAtomic<size_t> capacity_;
// Encodes eviction_effort_cap (bottom 31 bits) and strict_capacity_limit
// (top bit). See HyperClockCacheOptions::eviction_effort_cap etc.
struct EecAndScl : public BitFields<uint32_t, EecAndScl> {
uint32_t GetEffectiveEvictionEffortCap() const {
// Because setting strict_capacity_limit is supposed to imply infinite
// cap on eviction effort, we can let the bit for strict_capacity_limit
// in the upper-most bit position to used as part of the effective cap.
return underlying;
}
};
using EvictionEffortCap = UnsignedBitField<EecAndScl, 31, NoPrevBitField>;
using StrictCapacityLimit = BoolBitField<EecAndScl, EvictionEffortCap>;
// (Relaxed: eventual consistency/update is OK)
RelaxedBitFieldsAtomic<EecAndScl> eec_and_scl_;
ALIGN_AS(CACHE_LINE_SIZE)
const CacheMetadataChargePolicy metadata_charge_policy_;
@@ -570,7 +551,7 @@ class FixedHyperClockTable : public BaseClockTable {
size_t estimated_value_size;
};
FixedHyperClockTable(size_t capacity, bool strict_capacity_limit,
FixedHyperClockTable(size_t capacity,
CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback,
@@ -592,7 +573,8 @@ class FixedHyperClockTable : public BaseClockTable {
// 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);
void Evict(size_t requested_charge, InsertState& state, EvictionData* data,
uint32_t eviction_effort_cap);
HandleImpl* Lookup(const UniqueId64x2& hashed_key);
@@ -775,7 +757,6 @@ class AutoHyperClockTable : public BaseClockTable {
// 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.
// These decorated pointers use the NextWithShift bit field struct below.
//
// 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
@@ -795,63 +776,47 @@ class AutoHyperClockTable : public BaseClockTable {
// 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.
struct NextWithShift : public BitFields<uint64_t, NextWithShift> {
// The "shift" associated with this decorated pointer (see description
// above).
using Shift = UnsignedBitField<NextWithShift, 6, NoPrevBitField>;
// Marker for the end of a chain. Must also (a) point back to the head of
// the chain (with end marker removed), and (b) set the LockedFlag
// (below), so that attempting to lock an empty chain has no effect (not
// needed, as the lock is only needed for removals).
using EndFlag = BoolBitField<NextWithShift, Shift>;
// Marker that some thread owning writes to the chain structure (except
// for inserts), but only if not an "end" pointer. Also called the
// "rewrite lock."
using LockedFlag = BoolBitField<NextWithShift, EndFlag>;
// The "next" associated with this decorated pointer, which is an index
// into the table's array_ (see description above).
using Next = UnsignedBitField<NextWithShift, 56, LockedFlag>;
bool IsLocked() const { return Get<LockedFlag>(); }
bool IsEnd() const {
// End flag should imply locked flag
assert(!Get<EndFlag>() || Get<LockedFlag>());
return Get<EndFlag>();
}
bool IsLockedNotEnd() const {
// NOTE: helping GCC to optimize this simpler code:
// return IsLocked() && !IsEnd();
constexpr U kEndFlag = U{1} << EndFlag::kBitOffset;
constexpr U kLockedFlag = U{1} << LockedFlag::kBitOffset;
return (underlying & (kEndFlag | kLockedFlag)) == kLockedFlag;
}
auto GetNext() const { return Get<Next>(); }
auto GetShift() const { return Get<Shift>(); }
// 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;
static NextWithShift Make(size_t next, int shift) {
return NextWithShift{}.With<Next>(next).With<Shift>(
static_cast<uint8_t>(shift));
}
// 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;
static NextWithShift MakeEnd(size_t next, int shift) {
return Make(next, shift).With<EndFlag>(true).With<LockedFlag>(true);
}
};
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;
}
// Bottom bits to right shift away to get an array index from a
// "with_shift" pointer.
static constexpr int kNextShift = 8;
// A bit mask for the "shift" associated with each "with_shift" pointer.
// Always bottommost bits.
static constexpr int kShiftMask = 63;
// A marker for head_next_with_shift that indicates this HandleImpl is
// heap allocated (standalone) rather than in the table.
static constexpr NextWithShift kStandaloneMarker{UINT64_MAX};
static constexpr uint64_t kStandaloneMarker = UINT64_MAX;
// 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 NextWithShift kUnusedMarker{0};
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.
AcqRelBitFieldsAtomic<NextWithShift> head_next_with_shift{kUnusedMarker};
AcqRelBitFieldsAtomic<NextWithShift> chain_next_with_shift{kUnusedMarker};
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 {
@@ -876,7 +841,7 @@ class AutoHyperClockTable : public BaseClockTable {
size_t min_avg_value_size;
};
AutoHyperClockTable(size_t capacity, bool strict_capacity_limit,
AutoHyperClockTable(size_t capacity,
CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback,
@@ -903,7 +868,8 @@ class AutoHyperClockTable : public BaseClockTable {
// 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);
void Evict(size_t requested_charge, InsertState& state, EvictionData* data,
uint32_t eviction_effort_cap);
HandleImpl* Lookup(const UniqueId64x2& hashed_key);
@@ -1136,6 +1102,15 @@ class ALIGN_AS(CACHE_LINE_SIZE) ClockCacheShard final : public CacheShardBase {
private: // data
Table table_;
// Maximum total charge of all elements stored in the table.
// (Relaxed: eventual consistency/update is OK)
RelaxedAtomic<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_;
}; // class ClockCacheShard
template <class Table>
@@ -1153,12 +1128,6 @@ class BaseHyperClockCache : public ShardedCache<ClockCacheShard<Table>> {
const CacheItemHelper* GetCacheItemHelper(Handle* handle) const override;
void ApplyToHandle(
Cache* cache, Handle* handle,
const std::function<void(const Slice& key, Cache::ObjectPtr obj,
size_t charge, const CacheItemHelper* helper)>&
callback) override;
void ReportProblems(
const std::shared_ptr<Logger>& /*info_log*/) const override;
};
+135 -205
View File
@@ -16,31 +16,6 @@
#include "util/string_util.h"
namespace ROCKSDB_NAMESPACE {
namespace {
// Format of values in CompressedSecondaryCache:
// If enable_custom_split_merge:
// * A chain of CacheValueChunk representing the sequence of bytes for a tagged
// value. The overall length of the tagged value is determined by the chain
// of CacheValueChunks.
// If !enable_custom_split_merge:
// * A LengthPrefixedSlice (starts with varint64 size) of a tagged value.
//
// A tagged value has a 2-byte header before the "saved" or compressed block
// data:
// * 1 byte for "source" CacheTier indicating which tier is responsible for
// compression/decompression.
// * 1 byte for compression type which is generated/used by
// CompressedSecondaryCache iff source == CacheTier::kVolatileCompressedTier
// (original entry passed in was uncompressed). Otherwise, the compression
// type is preserved from the entry passed in.
constexpr uint32_t kTagSize = 2;
// Size of tag + varint size prefix when applicable
uint32_t GetHeaderSize(size_t data_size, bool enable_split_merge) {
return (enable_split_merge ? 0 : VarintLength(kTagSize + data_size)) +
kTagSize;
}
} // namespace
CompressedSecondaryCache::CompressedSecondaryCache(
const CompressedSecondaryCacheOptions& opts)
@@ -49,14 +24,7 @@ CompressedSecondaryCache::CompressedSecondaryCache(
cache_res_mgr_(std::make_shared<ConcurrentCacheReservationManager>(
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
cache_))),
disable_cache_(opts.capacity == 0) {
auto mgr =
GetBuiltinCompressionManager(cache_options_.compress_format_version);
compressor_ = mgr->GetCompressor(cache_options_.compression_opts,
cache_options_.compression_type);
decompressor_ =
mgr->GetDecompressorOptimizeFor(cache_options_.compression_type);
}
disable_cache_(opts.capacity == 0) {}
CompressedSecondaryCache::~CompressedSecondaryCache() = default;
@@ -65,9 +33,13 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
Cache::CreateContext* create_context, bool /*wait*/, bool advise_erase,
Statistics* stats, bool& kept_in_sec_cache) {
assert(helper);
if (disable_cache_.LoadRelaxed()) {
// 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;
@@ -83,58 +55,71 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
return nullptr;
}
std::string merged_value;
Slice tagged_data;
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 =
static_cast<CacheValueChunk*>(handle_value);
merged_value = MergeChunksIntoValue(value_chunk_ptr);
tagged_data = Slice(merged_value);
reinterpret_cast<CacheValueChunk*>(handle_value);
merged_value = MergeChunksIntoValue(value_chunk_ptr, handle_value_charge);
ptr = &merged_value;
data_ptr = ptr->get();
} else {
tagged_data = GetLengthPrefixedSlice(static_cast<char*>(handle_value));
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();
auto source = lossless_cast<CacheTier>(tagged_data[0]);
auto type = lossless_cast<CompressionType>(tagged_data[1]);
std::unique_ptr<char[]> uncompressed;
Slice saved(tagged_data.data() + kTagSize, tagged_data.size() - kTagSize);
Status s;
Cache::ObjectPtr value{nullptr};
size_t charge{0};
if (source == CacheTier::kVolatileCompressedTier) {
if (type != kNoCompression) {
// TODO: can we do something to avoid yet another allocation?
Decompressor::Args args;
args.compressed_data = saved;
args.compression_type = type;
Status s = decompressor_->ExtractUncompressedSize(args);
assert(s.ok()); // in-memory data
if (s.ok()) {
uncompressed = std::make_unique<char[]>(args.uncompressed_size);
s = decompressor_->DecompressBlock(args, uncompressed.get());
assert(s.ok()); // in-memory data
}
if (!s.ok()) {
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;
}
saved = Slice(uncompressed.get(), args.uncompressed_size);
type = kNoCompression;
// Free temporary compressed data as early as we can. This could matter
// for unusually large blocks because we also have
// * Another compressed copy above (from lru_cache).
// * The uncompressed copy in `uncompressed`.
// * Another uncompressed copy in `result_value` below.
// Let's try to max out at 3 copies instead of 4.
merged_value = std::string();
s = helper->create_cb(Slice(uncompressed.get(), uncompressed_size),
kNoCompression, CacheTier::kVolatileTier,
create_context, allocator, &value, &charge);
}
// Reduced as if it came from primary cache
source = CacheTier::kVolatileTier;
} 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,
create_context, allocator, &value, &charge);
}
Cache::ObjectPtr result_value = nullptr;
size_t result_charge = 0;
Status s = helper->create_cb(saved, type, source, create_context,
cache_options_.memory_allocator.get(),
&result_value, &result_charge);
if (!s.ok()) {
cache_->Release(lru_handle, /*erase_if_last_ref=*/true);
return nullptr;
@@ -152,8 +137,7 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
kept_in_sec_cache = true;
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
}
handle.reset(
new CompressedSecondaryCacheResultHandle(result_value, result_charge));
handle.reset(new CompressedSecondaryCacheResultHandle(value, charge));
RecordTick(stats, COMPRESSED_SECONDARY_CACHE_HITS);
return handle;
}
@@ -176,111 +160,77 @@ bool CompressedSecondaryCache::MaybeInsertDummy(const Slice& key) {
Status CompressedSecondaryCache::InsertInternal(
const Slice& key, Cache::ObjectPtr value,
const Cache::CacheItemHelper* helper, CompressionType from_type,
const Cache::CacheItemHelper* helper, CompressionType type,
CacheTier source) {
bool enable_split_merge = cache_options_.enable_custom_split_merge;
const Cache::CacheItemHelper* internal_helper = GetHelper(enable_split_merge);
if (source != CacheTier::kVolatileCompressedTier &&
cache_options_.enable_custom_split_merge) {
// We don't support custom split/merge for the tiered case
return Status::OK();
}
// TODO: variant of size_cb that also returns a pointer to the data if
// already available. Saves an allocation if we keep the compressed version.
const size_t data_size_original = (*helper->size_cb)(value);
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));
// Allocate enough memory for header/tag + original data because (a) we might
// not be attempting compression at all, and (b) we might keep the original if
// compression is insufficient. But we don't need the length prefix with
// enable_split_merge. TODO: be smarter with CacheValueChunk to save an
// allocation in the enable_split_merge case.
size_t header_size = GetHeaderSize(data_size_original, enable_split_merge);
CacheAllocationPtr allocation = AllocateBlock(
header_size + data_size_original, cache_options_.memory_allocator.get());
char* data_ptr = allocation.get() + header_size;
Slice tagged_data(data_ptr - kTagSize, data_size_original + kTagSize);
assert(tagged_data.data() >= allocation.get());
size_t header_size = payload - header;
size_t data_size = (*helper->size_cb)(value);
size_t total_size = data_size + header_size;
CacheAllocationPtr ptr =
AllocateBlock(total_size, cache_options_.memory_allocator.get());
char* data_ptr = ptr.get() + header_size;
Status s = (*helper->saveto_cb)(value, 0, data_size_original, data_ptr);
Status s = (*helper->saveto_cb)(value, 0, data_size, data_ptr);
if (!s.ok()) {
return s;
}
Slice val(data_ptr, data_size);
std::unique_ptr<char[]> tagged_compressed_data;
CompressionType to_type = kNoCompression;
if (compressor_ && from_type == kNoCompression &&
std::string compressed_val;
if (cache_options_.compression_type != kNoCompression &&
type == kNoCompression &&
!cache_options_.do_not_compress_roles.Contains(helper->role)) {
assert(source == CacheTier::kVolatileCompressedTier);
PERF_COUNTER_ADD(compressed_sec_cache_uncompressed_bytes, data_size);
CompressionContext compression_context(cache_options_.compression_type,
cache_options_.compression_opts);
uint64_t sample_for_compression{0};
CompressionInfo compression_info(
cache_options_.compression_opts, compression_context,
CompressionDict::GetEmptyDict(), cache_options_.compression_type,
sample_for_compression);
// TODO: consider malloc sizes for max acceptable compressed size
// Or maybe max_compressed_bytes_per_kb
size_t data_size_compressed = data_size_original - 1;
tagged_compressed_data =
std::make_unique<char[]>(data_size_compressed + kTagSize);
s = compressor_->CompressBlock(Slice(data_ptr, data_size_original),
tagged_compressed_data.get() + kTagSize,
&data_size_compressed, &to_type,
nullptr /*working_area*/);
if (!s.ok()) {
return s;
bool success =
CompressData(val, compression_info,
cache_options_.compress_format_version, &compressed_val);
if (!success) {
return Status::Corruption("Error compressing value.");
}
PERF_COUNTER_ADD(compressed_sec_cache_uncompressed_bytes,
data_size_original);
if (to_type == kNoCompression) {
// Compression rejected or otherwise aborted/failed
to_type = kNoCompression;
tagged_compressed_data.reset();
// TODO: consider separate counters for rejected compressions
PERF_COUNTER_ADD(compressed_sec_cache_compressed_bytes,
data_size_original);
} else {
PERF_COUNTER_ADD(compressed_sec_cache_compressed_bytes,
data_size_compressed);
if (enable_split_merge) {
// Only need tagged_data for copying into CacheValueChunks.
tagged_data = Slice(tagged_compressed_data.get(),
data_size_compressed + kTagSize);
allocation.reset();
} else {
// Replace allocation with compressed version, copied from string
header_size = GetHeaderSize(data_size_compressed, enable_split_merge);
allocation = AllocateBlock(header_size + data_size_compressed,
cache_options_.memory_allocator.get());
data_ptr = allocation.get() + header_size;
// Ignore unpopulated tag on tagged_compressed_data; will only be
// populated on the new allocation.
std::memcpy(data_ptr, tagged_compressed_data.get() + kTagSize,
data_size_compressed);
tagged_data =
Slice(data_ptr - kTagSize, data_size_compressed + kTagSize);
assert(tagged_data.data() >= allocation.get());
}
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);
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);
}
}
PERF_COUNTER_ADD(compressed_sec_cache_insert_real_count, 1);
// Save the tag fields
const_cast<char*>(tagged_data.data())[0] = lossless_cast<char>(source);
const_cast<char*>(tagged_data.data())[1] = lossless_cast<char>(
source == CacheTier::kVolatileCompressedTier ? to_type : from_type);
if (enable_split_merge) {
size_t split_charge{0};
if (cache_options_.enable_custom_split_merge) {
size_t charge{0};
CacheValueChunk* value_chunks_head =
SplitValueIntoChunks(tagged_data, split_charge);
s = cache_->Insert(key, value_chunks_head, internal_helper, split_charge);
assert(s.ok()); // LRUCache::Insert() with handle==nullptr always OK
SplitValueIntoChunks(val, cache_options_.compression_type, charge);
return cache_->Insert(key, value_chunks_head, internal_helper, charge);
} else {
// Save the size prefix
char* ptr = allocation.get();
ptr = EncodeVarint64(ptr, tagged_data.size());
assert(ptr == tagged_data.data());
#ifdef ROCKSDB_MALLOC_USABLE_SIZE
size_t charge = malloc_usable_size(allocation.get());
#else
size_t charge = tagged_data.size();
#endif
s = cache_->Insert(key, allocation.release(), internal_helper, charge);
assert(s.ok()); // LRUCache::Insert() with handle==nullptr always OK
std::memcpy(ptr.get(), header, header_size);
CacheAllocationPtr* buf = new CacheAllocationPtr(std::move(ptr));
return cache_->Insert(key, buf, internal_helper, total_size);
}
return Status::OK();
}
Status CompressedSecondaryCache::Insert(const Slice& key,
@@ -302,17 +252,7 @@ Status CompressedSecondaryCache::Insert(const Slice& key,
Status CompressedSecondaryCache::InsertSaved(
const Slice& key, const Slice& saved, CompressionType type = kNoCompression,
CacheTier source = CacheTier::kVolatileTier) {
if (source == CacheTier::kVolatileCompressedTier) {
// Unexpected, would violate InsertInternal preconditions
assert(source != CacheTier::kVolatileCompressedTier);
return Status::OK();
}
if (type == kNoCompression) {
// Not currently supported (why?)
return Status::OK();
}
if (cache_options_.enable_custom_split_merge) {
// We don't support custom split/merge for the tiered case (why?)
return Status::OK();
}
@@ -332,7 +272,7 @@ Status CompressedSecondaryCache::SetCapacity(size_t capacity) {
MutexLock l(&capacity_mutex_);
cache_options_.capacity = capacity;
cache_->SetCapacity(capacity);
disable_cache_.StoreRelaxed(capacity == 0);
disable_cache_ = capacity == 0;
return Status::OK();
}
@@ -362,14 +302,9 @@ std::string CompressedSecondaryCache::GetPrintableOptions() const {
return ret;
}
// FIXME: this could use a lot of attention, including:
// * Use allocator
// * We shouldn't be worse than non-split; be more pro-actively aware of
// internal fragmentation
// * Consider a unified object/chunk structure that may or may not split
// * Optimize size overhead of chunks
CompressedSecondaryCache::CacheValueChunk*
CompressedSecondaryCache::SplitValueIntoChunks(const Slice& value,
CompressionType compression_type,
size_t& charge) {
assert(!value.empty());
const char* src_ptr = value.data();
@@ -390,14 +325,15 @@ CompressedSecondaryCache::SplitValueIntoChunks(const Slice& value,
// size, or there is no compression.
if (upper == malloc_bin_sizes_.begin() ||
upper == malloc_bin_sizes_.end() ||
*upper - predicted_chunk_size < malloc_bin_sizes_.front()) {
*upper - predicted_chunk_size < malloc_bin_sizes_.front() ||
compression_type == kNoCompression) {
tmp_size = predicted_chunk_size;
} else {
tmp_size = *(--upper);
}
CacheValueChunk* new_chunk =
static_cast<CacheValueChunk*>(static_cast<void*>(new char[tmp_size]));
reinterpret_cast<CacheValueChunk*>(new char[tmp_size]);
current_chunk->next = new_chunk;
current_chunk = current_chunk->next;
actual_chunk_size = tmp_size - sizeof(CacheValueChunk) + 1;
@@ -412,24 +348,28 @@ CompressedSecondaryCache::SplitValueIntoChunks(const Slice& value,
return dummy_head.next;
}
std::string CompressedSecondaryCache::MergeChunksIntoValue(
const CacheValueChunk* head) {
CacheAllocationPtr CompressedSecondaryCache::MergeChunksIntoValue(
const void* chunks_head, size_t& charge) {
const CacheValueChunk* head =
reinterpret_cast<const CacheValueChunk*>(chunks_head);
const CacheValueChunk* current_chunk = head;
size_t total_size = 0;
charge = 0;
while (current_chunk != nullptr) {
total_size += current_chunk->size;
charge += current_chunk->size;
current_chunk = current_chunk->next;
}
std::string result;
result.reserve(total_size);
CacheAllocationPtr ptr =
AllocateBlock(charge, cache_options_.memory_allocator.get());
current_chunk = head;
size_t pos{0};
while (current_chunk != nullptr) {
result.append(current_chunk->data, current_chunk->size);
memcpy(ptr.get() + pos, current_chunk->data, current_chunk->size);
pos += current_chunk->size;
current_chunk = current_chunk->next;
}
assert(result.size() == total_size);
return result;
return ptr;
}
const Cache::CacheItemHelper* CompressedSecondaryCache::GetHelper(
@@ -443,31 +383,21 @@ const Cache::CacheItemHelper* CompressedSecondaryCache::GetHelper(
CacheValueChunk* tmp_chunk = chunks_head;
chunks_head = chunks_head->next;
tmp_chunk->Free();
obj = nullptr;
}
}};
return &kHelper;
} else {
static const Cache::CacheItemHelper kHelper{
CacheEntryRole::kMisc,
[](Cache::ObjectPtr obj, MemoryAllocator* alloc) {
if (obj != nullptr) {
CacheAllocationDeleter{alloc}(static_cast<char*>(obj));
}
[](Cache::ObjectPtr obj, MemoryAllocator* /*alloc*/) {
delete static_cast<CacheAllocationPtr*>(obj);
obj = nullptr;
}};
return &kHelper;
}
}
size_t CompressedSecondaryCache::TEST_GetCharge(const Slice& key) {
Cache::Handle* lru_handle = cache_->Lookup(key);
if (lru_handle == nullptr) {
return 0;
}
size_t charge = cache_->GetCharge(lru_handle);
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
return charge;
}
std::shared_ptr<SecondaryCache>
CompressedSecondaryCacheOptions::MakeSharedSecondaryCache() const {
return std::make_shared<CompressedSecondaryCache>(*this);
+11 -9
View File
@@ -10,12 +10,13 @@
#include <memory>
#include "cache/cache_reservation_manager.h"
#include "cache/lru_cache.h"
#include "memory/memory_allocator_impl.h"
#include "rocksdb/advanced_compression.h"
#include "rocksdb/secondary_cache.h"
#include "rocksdb/slice.h"
#include "rocksdb/status.h"
#include "util/atomic.h"
#include "util/compression.h"
#include "util/mutexlock.h"
namespace ROCKSDB_NAMESPACE {
@@ -123,9 +124,14 @@ class CompressedSecondaryCache : public SecondaryCache {
// Split value into chunks to better fit into jemalloc bins. The chunks
// are stored in CacheValueChunk and extra charge is needed for each chunk,
// so the cache charge is recalculated here.
CacheValueChunk* SplitValueIntoChunks(const Slice& value, size_t& charge);
CacheValueChunk* SplitValueIntoChunks(const Slice& value,
CompressionType compression_type,
size_t& charge);
std::string MergeChunksIntoValue(const CacheValueChunk* head);
// After merging chunks, the extra charge for each chunk is removed, so
// the charge is recalculated.
CacheAllocationPtr MergeChunksIntoValue(const void* chunks_head,
size_t& charge);
bool MaybeInsertDummy(const Slice& key);
@@ -133,17 +139,13 @@ class CompressedSecondaryCache : public SecondaryCache {
const Cache::CacheItemHelper* helper,
CompressionType type, CacheTier source);
size_t TEST_GetCharge(const Slice& key);
// TODO: clean up to use cleaner interfaces in typed_cache.h
const Cache::CacheItemHelper* GetHelper(bool enable_custom_split_merge) const;
std::shared_ptr<Cache> cache_;
CompressedSecondaryCacheOptions cache_options_;
std::unique_ptr<Compressor> compressor_;
std::shared_ptr<Decompressor> decompressor_;
mutable port::Mutex capacity_mutex_;
std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr_;
RelaxedAtomic<bool> disable_cache_;
bool disable_cache_;
};
} // namespace ROCKSDB_NAMESPACE
+47 -119
View File
@@ -24,14 +24,6 @@ namespace ROCKSDB_NAMESPACE {
using secondary_cache_test_util::GetTestingCacheTypes;
using secondary_cache_test_util::WithCacheType;
// Read and reset a statistic
template <typename T>
T Pop(T& var) {
T ret = var;
var = T();
return ret;
}
// 16 bytes for HCC compatibility
const std::string key0 = "____ ____key0";
const std::string key1 = "____ ____key1";
@@ -47,8 +39,6 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
protected:
void BasicTestHelper(std::shared_ptr<SecondaryCache> sec_cache,
bool sec_cache_is_compressed) {
CompressedSecondaryCache* comp_sec_cache =
static_cast<CompressedSecondaryCache*>(sec_cache.get());
get_perf_context()->Reset();
bool kept_in_sec_cache{true};
// Lookup an non-existent key.
@@ -59,7 +49,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
Random rnd(301);
// Insert and Lookup the item k1 for the first time.
std::string str1 = test::CompressibleString(&rnd, 0.5, 1000);
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));
@@ -76,28 +66,16 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 1);
if (sec_cache_is_compressed) {
ASSERT_GT(comp_sec_cache->TEST_GetCharge(key1), str1.length() / 4);
ASSERT_LT(comp_sec_cache->TEST_GetCharge(key1), str1.length() * 3 / 4);
} else {
ASSERT_GE(comp_sec_cache->TEST_GetCharge(key1), str1.length());
// NOTE: split-merge is worse (1048 vs. 1024)
ASSERT_LE(comp_sec_cache->TEST_GetCharge(key1), 1048U);
}
std::unique_ptr<SecondaryCacheResultHandle> handle1_2 =
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/true,
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_NE(handle1_2, nullptr);
ASSERT_FALSE(kept_in_sec_cache);
if (sec_cache_is_compressed) {
ASSERT_EQ(
Pop(get_perf_context()->compressed_sec_cache_uncompressed_bytes),
str1.length());
ASSERT_LT(get_perf_context()->compressed_sec_cache_compressed_bytes,
str1.length() * 3 / 4);
ASSERT_GT(Pop(get_perf_context()->compressed_sec_cache_compressed_bytes),
str1.length() / 4);
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes,
1000);
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes,
1007);
} else {
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
@@ -115,7 +93,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
ASSERT_EQ(handle1_3, nullptr);
// Insert and Lookup the item k2.
std::string str2 = test::CompressibleString(&rnd, 0.5, 1017);
std::string str2(rnd.RandomString(1000));
TestItem item2(str2.data(), str2.length());
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 2);
@@ -127,13 +105,10 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 2);
if (sec_cache_is_compressed) {
ASSERT_EQ(
Pop(get_perf_context()->compressed_sec_cache_uncompressed_bytes),
str2.length());
ASSERT_LT(get_perf_context()->compressed_sec_cache_compressed_bytes,
str2.length() * 3 / 4);
ASSERT_GT(Pop(get_perf_context()->compressed_sec_cache_compressed_bytes),
str2.length() / 4);
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes,
2000);
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes,
2014);
} else {
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
@@ -147,48 +122,9 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
ASSERT_NE(val2, nullptr);
ASSERT_EQ(memcmp(val2->Buf(), item2.Buf(), item2.Size()), 0);
// Release handles
std::vector<SecondaryCacheResultHandle*> handles = {handle1_2.get(),
handle2_2.get()};
sec_cache->WaitAll(handles);
handle1_2.reset();
handle2_2.reset();
// Insert and Lookup a non-compressible item k3.
std::string str3 = rnd.RandomBinaryString(480);
TestItem item3(str3.data(), str3.length());
ASSERT_OK(sec_cache->Insert(key3, &item3, GetHelper(), false));
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 3);
std::unique_ptr<SecondaryCacheResultHandle> handle3_1 =
sec_cache->Lookup(key3, GetHelper(), this, true, /*advise_erase=*/false,
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_EQ(handle3_1, nullptr);
ASSERT_OK(sec_cache->Insert(key3, &item3, GetHelper(), false));
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 3);
if (sec_cache_is_compressed) {
// TODO: consider a compression rejected stat?
ASSERT_EQ(
Pop(get_perf_context()->compressed_sec_cache_uncompressed_bytes),
str3.length());
ASSERT_EQ(Pop(get_perf_context()->compressed_sec_cache_compressed_bytes),
str3.length());
} else {
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> handle3_2 =
sec_cache->Lookup(key3, GetHelper(), this, true, /*advise_erase=*/false,
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_NE(handle3_2, nullptr);
std::unique_ptr<TestItem> val3 =
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle3_2->Value()));
ASSERT_NE(val3, nullptr);
ASSERT_EQ(memcmp(val3->Buf(), item3.Buf(), item3.Size()), 0);
EXPECT_GE(comp_sec_cache->TEST_GetCharge(key3), str3.length());
EXPECT_LE(comp_sec_cache->TEST_GetCharge(key3), 512);
sec_cache.reset();
}
@@ -238,9 +174,8 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
}
secondary_cache_opts.capacity = 1400;
secondary_cache_opts.capacity = 1100;
secondary_cache_opts.num_shard_bits = 0;
secondary_cache_opts.strict_capacity_limit = true;
std::shared_ptr<SecondaryCache> sec_cache =
NewCompressedSecondaryCache(secondary_cache_opts);
@@ -254,7 +189,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
// Insert and Lookup the second item.
std::string str2(rnd.RandomString(500));
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));
@@ -262,23 +197,16 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
std::unique_ptr<SecondaryCacheResultHandle> handle1 =
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_NE(handle1, nullptr);
std::unique_ptr<TestItem> val1{static_cast<TestItem*>(handle1->Value())};
ASSERT_NE(val1, nullptr);
ASSERT_EQ(val1->ToString(), str1);
handle1.reset();
ASSERT_EQ(handle1, nullptr);
// Insert k2 and k1 is evicted.
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
handle1 =
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_EQ(handle1, nullptr);
std::unique_ptr<SecondaryCacheResultHandle> handle2 =
sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/false,
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_NE(handle2, nullptr);
std::unique_ptr<TestItem> val2{static_cast<TestItem*>(handle2->Value())};
std::unique_ptr<TestItem> val2 =
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle2->Value()));
ASSERT_NE(val2, nullptr);
ASSERT_EQ(memcmp(val2->Buf(), item2.Buf(), item2.Size()), 0);
@@ -300,7 +228,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
// Save Fails.
std::string str3 = rnd.RandomString(10);
TestItem item3(str3.data(), str3.length());
// The first Status is OK because a dummy handle is inserted.
// 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));
@@ -333,11 +261,11 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
get_perf_context()->Reset();
Random rnd(301);
std::string str1 = test::CompressibleString(&rnd, 0.5, 1001);
std::string str1 = rnd.RandomString(1001);
auto item1_1 = new TestItem(str1.data(), str1.length());
ASSERT_OK(cache->Insert(key1, item1_1, GetHelper(), str1.length()));
std::string str2 = test::CompressibleString(&rnd, 0.5, 1012);
std::string str2 = rnd.RandomString(1012);
auto item2_1 = new TestItem(str2.data(), str2.length());
// After this Insert, primary cache contains k2 and secondary cache contains
// k1's dummy item.
@@ -346,7 +274,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
std::string str3 = test::CompressibleString(&rnd, 0.5, 1024);
std::string str3 = rnd.RandomString(1024);
auto item3_1 = new TestItem(str3.data(), str3.length());
// After this Insert, primary cache contains k3 and secondary cache contains
// k1's dummy item and k2's dummy item.
@@ -365,13 +293,10 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
ASSERT_OK(cache->Insert(key2, item2_2, GetHelper(), str2.length()));
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 1);
if (sec_cache_is_compressed) {
ASSERT_EQ(
Pop(get_perf_context()->compressed_sec_cache_uncompressed_bytes),
str1.length());
ASSERT_LT(get_perf_context()->compressed_sec_cache_compressed_bytes,
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes,
str1.length());
ASSERT_GT(Pop(get_perf_context()->compressed_sec_cache_compressed_bytes),
str1.length() / 10);
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes,
1008);
} else {
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
@@ -383,13 +308,10 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
ASSERT_OK(cache->Insert(key3, item3_2, GetHelper(), str3.length()));
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 2);
if (sec_cache_is_compressed) {
ASSERT_EQ(
Pop(get_perf_context()->compressed_sec_cache_uncompressed_bytes),
str2.length());
ASSERT_LT(get_perf_context()->compressed_sec_cache_compressed_bytes,
str2.length());
ASSERT_GT(Pop(get_perf_context()->compressed_sec_cache_compressed_bytes),
str2.length() / 10);
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes,
str1.length() + str2.length());
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes,
2027);
} else {
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
@@ -715,7 +637,8 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
size_t str_size{8500};
std::string str = rnd.RandomString(static_cast<int>(str_size));
size_t charge{0};
CacheValueChunk* chunks_head = sec_cache->SplitValueIntoChunks(str, charge);
CacheValueChunk* chunks_head =
sec_cache->SplitValueIntoChunks(str, kLZ4Compression, charge);
ASSERT_EQ(charge, str_size + 3 * (sizeof(CacheValueChunk) - 1));
CacheValueChunk* current_chunk = chunks_head;
@@ -761,9 +684,12 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
std::unique_ptr<CompressedSecondaryCache> sec_cache =
std::make_unique<CompressedSecondaryCache>(
CompressedSecondaryCacheOptions(1000, 0, true, 0.5, 0.0));
std::string value_str = sec_cache->MergeChunksIntoValue(chunks_head);
ASSERT_EQ(value_str.size(), size1 + size2 + size3);
ASSERT_EQ(value_str, str);
size_t charge{0};
CacheAllocationPtr value =
sec_cache->MergeChunksIntoValue(chunks_head, charge);
ASSERT_EQ(charge, size1 + size2 + size3);
std::string value_str{value.get(), charge};
ASSERT_EQ(strcmp(value_str.data(), str.data()), 0);
while (chunks_head != nullptr) {
CacheValueChunk* tmp_chunk = chunks_head;
@@ -795,12 +721,15 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
size_t str_size{8500};
std::string str = rnd.RandomString(static_cast<int>(str_size));
size_t charge{0};
CacheValueChunk* chunks_head = sec_cache->SplitValueIntoChunks(str, charge);
CacheValueChunk* chunks_head =
sec_cache->SplitValueIntoChunks(str, kLZ4Compression, charge);
ASSERT_EQ(charge, str_size + 3 * (sizeof(CacheValueChunk) - 1));
std::string value_str = sec_cache->MergeChunksIntoValue(chunks_head);
ASSERT_EQ(value_str.size(), str_size);
ASSERT_EQ(value_str, str);
CacheAllocationPtr value =
sec_cache->MergeChunksIntoValue(chunks_head, charge);
ASSERT_EQ(charge, str_size);
std::string value_str{value.get(), charge};
ASSERT_EQ(strcmp(value_str.data(), str.data()), 0);
sec_cache->GetHelper(true)->del_cb(chunks_head, /*alloc*/ nullptr);
}
@@ -912,6 +841,7 @@ TEST_P(CompressedSecondaryCacheTestWithCompressionParam,
BasicTestHelper(sec_cache, sec_cache_is_compressed_);
}
TEST_P(CompressedSecondaryCacheTestWithCompressionParam, FailsTest) {
FailsTest(sec_cache_is_compressed_);
}
@@ -963,8 +893,8 @@ TEST_P(CompressedSecondaryCacheTestWithCompressionParam, EntryRoles) {
std::shared_ptr<SecondaryCache> sec_cache = NewCompressedSecondaryCache(opts);
Random rnd(301);
std::string junk = test::CompressibleString(&rnd, 0.5, 1000);
// Fixed seed to ensure consistent compressibility (doesn't compress)
std::string junk(Random(301).RandomString(1000));
for (uint32_t i = 0; i < kNumCacheEntryRoles; ++i) {
CacheEntryRole role = static_cast<CacheEntryRole>(i);
@@ -997,11 +927,9 @@ TEST_P(CompressedSecondaryCacheTestWithCompressionParam, EntryRoles) {
sec_cache_is_compressed_ && !do_not_compress.Contains(role);
if (compressed) {
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes,
junk.length());
ASSERT_LT(get_perf_context()->compressed_sec_cache_compressed_bytes,
junk.length() * 3 / 4);
ASSERT_GT(get_perf_context()->compressed_sec_cache_compressed_bytes,
junk.length() / 4);
1000);
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes,
1007);
} else {
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
+3 -13
View File
@@ -93,8 +93,9 @@ void LRUHandleTable::Resize() {
uint32_t old_length = uint32_t{1} << length_bits_;
int new_length_bits = length_bits_ + 1;
std::unique_ptr<LRUHandle*[]> new_list{
new LRUHandle* [size_t{1} << new_length_bits] {}};
std::unique_ptr<LRUHandle* []> new_list {
new LRUHandle* [size_t{1} << new_length_bits] {}
};
[[maybe_unused]] uint32_t count = 0;
for (uint32_t i = 0; i < old_length; i++) {
LRUHandle* h = list_[i];
@@ -676,17 +677,6 @@ const Cache::CacheItemHelper* LRUCache::GetCacheItemHelper(
return h->helper;
}
void LRUCache::ApplyToHandle(
Cache* cache, Handle* handle,
const std::function<void(const Slice& key, ObjectPtr value, size_t charge,
const CacheItemHelper* helper)>& callback) {
auto cache_ptr = static_cast<LRUCache*>(cache);
auto h = static_cast<const LRUHandle*>(handle);
callback(h->key(), h->value,
h->GetCharge(cache_ptr->GetShard(0).metadata_charge_policy_),
h->helper);
}
size_t LRUCache::TEST_GetLRUSize() {
return SumOverShards([](LRUCacheShard& cs) { return cs.TEST_GetLRUSize(); });
}
-6
View File
@@ -452,12 +452,6 @@ class LRUCache
size_t GetCharge(Handle* handle) const override;
const CacheItemHelper* GetCacheItemHelper(Handle* handle) const override;
void ApplyToHandle(
Cache* cache, Handle* handle,
const std::function<void(const Slice& key, ObjectPtr obj, size_t charge,
const CacheItemHelper* helper)>& callback)
override;
// Retrieves number of elements in LRU, for unit test purpose only.
size_t TEST_GetLRUSize();
// Retrieves high pri pool ratio.
+3 -3
View File
@@ -546,7 +546,7 @@ TYPED_TEST(ClockCacheTest, Limits) {
// verify usage tracking on detached entries.)
{
size_t n = kCapacity * 5 + 1;
std::unique_ptr<HandleImpl*[]> ha{new HandleImpl* [n] {}};
std::unique_ptr<HandleImpl* []> ha { new HandleImpl* [n] {} };
Status s;
for (size_t i = 0; i < n && s.ok(); ++i) {
hkey[1] = i;
@@ -713,7 +713,7 @@ TYPED_TEST(ClockCacheTest, ClockEvictionEffortCapTest) {
// evictable entries are present beyond the cache capacity, despite
// being evictable.
constexpr size_t kCount = kCapacity - 1;
std::unique_ptr<HandleImpl*[]> ha{new HandleImpl* [kCount] {}};
std::unique_ptr<HandleImpl* []> ha { new HandleImpl* [kCount] {} };
for (size_t i = 0; i < 2 * kCount; ++i) {
UniqueId64x2 hkey = this->CheapHash(i);
ASSERT_OK(shard.Insert(
@@ -1405,9 +1405,9 @@ TEST_P(BasicSecondaryCacheTest, SaveFailTest) {
TestItem* item1 = new TestItem(str1.data(), str1.length());
ASSERT_OK(cache->Insert(k1.AsSlice(), item1, GetHelperFail(), str1.length()));
std::string str2 = rnd.RandomString(1020);
ASSERT_EQ(secondary_cache->num_inserts(), 0u);
TestItem* item2 = new TestItem(str2.data(), str2.length());
// k1 should be demoted to NVM
ASSERT_EQ(secondary_cache->num_inserts(), 0u);
ASSERT_OK(cache->Insert(k2.AsSlice(), item2, GetHelperFail(), str2.length()));
ASSERT_EQ(secondary_cache->num_inserts(), 1u);
+3 -1
View File
@@ -7,4 +7,6 @@
#include "cache/cache_entry_roles.h"
namespace ROCKSDB_NAMESPACE {} // namespace ROCKSDB_NAMESPACE
namespace ROCKSDB_NAMESPACE {
} // namespace ROCKSDB_NAMESPACE
+11 -18
View File
@@ -33,7 +33,7 @@ const char* kTieredCacheName = "TieredCache";
// proportionally across the primary/secondary caches.
//
// The primary block cache is initially sized to the sum of the primary cache
// budget + the secondary cache budget, as follows -
// budget + teh secondary cache budget, as follows -
// |--------- Primary Cache Configured Capacity -----------|
// |---Secondary Cache Budget----|----Primary Cache Budget-----|
//
@@ -51,7 +51,7 @@ const char* kTieredCacheName = "TieredCache";
// placeholder is counted against the primary cache. To compensate and count
// a portion of it against the secondary cache, the secondary cache Deflate()
// method is called to shrink it. Since the Deflate() causes the secondary
// actual usage to shrink, it is reflected here by releasing an equal amount
// actual usage to shrink, it is refelcted here by releasing an equal amount
// from the pri_cache_res_ reservation. The Deflate() in the secondary cache
// can be, but is not required to be, implemented using its own cache
// reservation manager.
@@ -72,7 +72,7 @@ const char* kTieredCacheName = "TieredCache";
// reservation is increased by an equal amount.
//
// Another way of implementing this would have been to simply split the user
// reservation into primary and secondary components. However, this would
// reservation into primary and seconary components. However, this would
// require allocating a structure to track the associated secondary cache
// reservation, which adds some complexity and overhead.
//
@@ -121,14 +121,7 @@ CacheWithSecondaryAdapter::~CacheWithSecondaryAdapter() {
assert(s.ok());
assert(placeholder_usage_ == 0);
assert(reserved_usage_ == 0);
if (pri_cache_res_->GetTotalMemoryUsed() != sec_capacity) {
fprintf(stdout,
"~CacheWithSecondaryAdapter: Primary cache reservation: "
"%zu, Secondary cache capacity: %zu, "
"Secondary cache reserved: %zu\n",
pri_cache_res_->GetTotalMemoryUsed(), sec_capacity,
sec_reserved_);
}
assert(pri_cache_res_->GetTotalMemoryUsed() == sec_capacity);
}
#endif // NDEBUG
}
@@ -278,8 +271,7 @@ Status CacheWithSecondaryAdapter::Insert(const Slice& key, ObjectPtr value,
// Warm up the secondary cache with the compressed block. The secondary
// cache may choose to ignore it based on the admission policy.
if (value != nullptr && !compressed_value.empty() &&
adm_policy_ == TieredAdmissionPolicy::kAdmPolicyThreeQueue &&
helper->IsSecondaryCacheCompatible()) {
adm_policy_ == TieredAdmissionPolicy::kAdmPolicyThreeQueue) {
Status status = secondary_cache_->InsertSaved(key, compressed_value, type);
assert(status.ok() || status.IsNotSupported());
}
@@ -486,10 +478,12 @@ const char* CacheWithSecondaryAdapter::Name() const {
// as well. At the moment, we don't have a good way of handling the case
// where the new capacity < total cache reservations.
void CacheWithSecondaryAdapter::SetCapacity(size_t capacity) {
size_t sec_capacity = static_cast<size_t>(
capacity * (distribute_cache_res_ ? sec_cache_res_ratio_ : 0.0));
size_t old_sec_capacity = 0;
if (distribute_cache_res_) {
MutexLock m(&cache_res_mutex_);
size_t sec_capacity = static_cast<size_t>(capacity * sec_cache_res_ratio_);
size_t old_sec_capacity = 0;
Status s = secondary_cache_->GetCapacity(old_sec_capacity);
if (!s.ok()) {
@@ -584,7 +578,7 @@ Status CacheWithSecondaryAdapter::UpdateCacheReservationRatio(
size_t pri_capacity = target_->GetCapacity();
size_t sec_capacity =
static_cast<size_t>(pri_capacity * compressed_secondary_ratio);
size_t old_sec_capacity = 0;
size_t old_sec_capacity;
Status s = secondary_cache_->GetCapacity(old_sec_capacity);
if (!s.ok()) {
return s;
@@ -608,7 +602,6 @@ Status CacheWithSecondaryAdapter::UpdateCacheReservationRatio(
// cache utilization (increase in capacity - increase in share of cache
// reservation)
// 3. Increase secondary cache capacity
assert(new_sec_reserved >= sec_reserved_);
s = secondary_cache_->Deflate(new_sec_reserved - sec_reserved_);
assert(s.ok());
s = pri_cache_res_->UpdateCacheReservation(
@@ -621,7 +614,7 @@ Status CacheWithSecondaryAdapter::UpdateCacheReservationRatio(
} else {
// We're shrinking the ratio. Try to avoid unnecessary evictions -
// 1. Lower the secondary cache capacity
// 2. Decrease pri_cache_res_ reservation to reflect lower secondary
// 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
+5 -69
View File
@@ -253,7 +253,6 @@ TEST_F(DBTieredSecondaryCacheTest, BasicTest) {
table_options.cache_index_and_filter_blocks = false;
Options options = GetDefaultOptions();
options.create_if_missing = true;
options.compression = kLZ4Compression;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
// Disable paranoid_file_checks so that flush will not read back the newly
@@ -365,7 +364,6 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
table_options.cache_index_and_filter_blocks = false;
Options options = GetDefaultOptions();
options.create_if_missing = true;
options.compression = kLZ4Compression;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
options.paranoid_file_checks = false;
@@ -508,7 +506,6 @@ TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
table_options.cache_index_and_filter_blocks = false;
Options options = GetDefaultOptions();
options.create_if_missing = true;
options.compression = kLZ4Compression;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
options.paranoid_file_checks = false;
@@ -609,7 +606,6 @@ TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
table_options.cache_index_and_filter_blocks = false;
Options options = GetDefaultOptions();
options.create_if_missing = true;
options.compression = kLZ4Compression;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
options.statistics = CreateDBStatistics();
@@ -721,7 +717,6 @@ TEST_F(DBTieredSecondaryCacheTest, IterateTest) {
table_options.cache_index_and_filter_blocks = false;
Options options = GetDefaultOptions();
options.create_if_missing = true;
options.compression = kLZ4Compression;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
options.paranoid_file_checks = false;
@@ -765,54 +760,6 @@ TEST_F(DBTieredSecondaryCacheTest, IterateTest) {
Destroy(options);
}
TEST_F(DBTieredSecondaryCacheTest, VolatileTierTest) {
if (!LZ4_Supported()) {
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
return;
}
BlockBasedTableOptions table_options;
// We want a block cache of size 5KB, and a compressed secondary cache of
// size 5KB. However, we specify a block cache size of 256KB here in order
// to take into account the cache reservation in the block cache on
// behalf of the compressed cache. The unit of cache reservation is 256KB.
// The effective block cache capacity will be calculated as 256 + 5 = 261KB,
// and 256KB will be reserved for the compressed cache, leaving 5KB for
// the primary block cache. We only have to worry about this here because
// the cache size is so small.
table_options.block_cache = NewCache(256 * 1024, 5 * 1024, 256 * 1024);
table_options.block_size = 4 * 1024;
table_options.cache_index_and_filter_blocks = false;
Options options = GetDefaultOptions();
options.create_if_missing = true;
options.compression = kLZ4Compression;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
// Disable paranoid_file_checks so that flush will not read back the newly
// written file
options.paranoid_file_checks = false;
options.lowest_used_cache_tier = CacheTier::kVolatileTier;
DestroyAndReopen(options);
Random rnd(301);
const int N = 256;
for (int i = 0; i < N; i++) {
std::string p_v;
test::CompressibleString(&rnd, 0.5, 1007, &p_v);
ASSERT_OK(Put(Key(i), p_v));
}
ASSERT_OK(Flush());
// Since lowest_used_cache_tier is the volatile tier, nothing should be
// inserted in the secondary cache.
std::string v = Get(Key(0));
ASSERT_EQ(1007, v.size());
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 0u);
ASSERT_EQ(nvm_sec_cache()->num_misses(), 0u);
Destroy(options);
}
class DBTieredAdmPolicyTest
: public DBTieredSecondaryCacheTest,
public testing::WithParamInterface<TieredAdmissionPolicy> {};
@@ -837,7 +784,6 @@ TEST_P(DBTieredAdmPolicyTest, CompressedOnlyTest) {
table_options.cache_index_and_filter_blocks = false;
Options options = GetDefaultOptions();
options.create_if_missing = true;
options.compression = kLZ4Compression;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
size_t comp_cache_usage = compressed_secondary_cache()->TEST_GetUsage();
@@ -890,7 +836,6 @@ TEST_P(DBTieredAdmPolicyTest, CompressedCacheAdmission) {
table_options.cache_index_and_filter_blocks = false;
Options options = GetDefaultOptions();
options.create_if_missing = true;
options.compression = kLZ4Compression;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
size_t comp_cache_usage = compressed_secondary_cache()->TEST_GetUsage();
@@ -951,20 +896,12 @@ TEST_F(DBTieredSecondaryCacheTest, FSBufferTest) {
IODebugContext* dbg) override {
for (size_t i = 0; i < num_reqs; ++i) {
FSReadRequest& req = reqs[i];
// See https://github.com/facebook/rocksdb/pull/13195 for why we
// want to set up our test implementation for FSAllocationPtr this
// way.
char* internalData = new char[req.len];
req.status = Read(req.offset, req.len, options, &req.result,
internalData, dbg);
Slice* internalSlice = new Slice(internalData, req.len);
FSAllocationPtr internalPtr(internalSlice, [](void* ptr) {
delete[] static_cast<const char*>(
static_cast<Slice*>(ptr)->data_);
delete static_cast<Slice*>(ptr);
FSAllocationPtr buffer(new char[req.len], [](void* ptr) {
delete[] static_cast<char*>(ptr);
});
req.fs_scratch = std::move(internalPtr);
req.fs_scratch = std::move(buffer);
req.status = Read(req.offset, req.len, options, &req.result,
static_cast<char*>(req.fs_scratch.get()), dbg);
}
return IOStatus::OK();
}
@@ -1000,7 +937,6 @@ TEST_F(DBTieredSecondaryCacheTest, FSBufferTest) {
table_options.cache_index_and_filter_blocks = false;
Options options = GetDefaultOptions();
options.create_if_missing = true;
options.compression = kLZ4Compression;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
options.statistics = CreateDBStatistics();
options.env = wrap_env.get();
-1
View File
@@ -1 +0,0 @@
ccache.exe cl.exe %*
+4 -3
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
from __future__ import print_function
import optparse
import re
@@ -108,11 +109,11 @@ def report_coverage():
# Check if we need to display coverage info for interested files.
if len(interested_files):
per_file_coverage = {
fname: per_file_coverage[fname]
per_file_coverage = dict(
(fname, per_file_coverage[fname])
for fname in interested_files
if fname in per_file_coverage
}
)
# If we only interested in several files, it makes no sense to report
# the total_coverage
total_coverage = None
+15 -61
View File
@@ -11,29 +11,18 @@ CRASHTEST_MAKE=$(MAKE) -f crash_test.mk
CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD) --cleanup_cmd='$(DB_CLEANUP_CMD)'
.PHONY: crash_test crash_test_with_atomic_flush crash_test_with_txn \
crash_test_with_wc_txn crash_test_with_wp_txn crash_test_with_wup_txn \
crash_test_with_best_efforts_recovery crash_test_with_ts \
crash_test_with_multiops_wc_txn \
crash_test_with_multiops_wp_txn \
crash_test_with_multiops_wup_txn \
crash_test_with_optimistic_txn \
crash_test_with_tiered_storage \
blackbox_crash_test blackbox_crash_test_with_atomic_flush \
blackbox_crash_test_with_wc_txn blackbox_crash_test_with_wp_txn \
blackbox_crash_test_with_wup_txn \
blackbox_crash_test_with_txn blackbox_crash_test_with_ts \
blackbox_crash_test_with_best_efforts_recovery \
whitebox_crash_test whitebox_crash_test_with_atomic_flush \
whitebox_crash_test_with_txn whitebox_crash_test_with_ts \
blackbox_crash_test_with_multiops_wc_txn \
blackbox_crash_test_with_multiops_wp_txn \
blackbox_crash_test_with_multiops_wup_txn \
blackbox_crash_test_with_optimistic_txn \
blackbox_crash_test_with_tiered_storage \
whitebox_crash_test whitebox_crash_test_with_atomic_flush \
whitebox_crash_test_with_wc_txn whitebox_crash_test_with_wp_txn \
whitebox_crash_test_with_wup_txn \
whitebox_crash_test_with_txn whitebox_crash_test_with_ts \
whitebox_crash_test_with_optimistic_txn \
crash_test_with_tiered_storage blackbox_crash_test_with_tiered_storage \
whitebox_crash_test_with_tiered_storage \
whitebox_crash_test_with_optimistic_txn \
blackbox_crash_test_with_optimistic_txn \
crash_test: $(DB_STRESS_CMD)
# Do not parallelize
@@ -45,20 +34,10 @@ crash_test_with_atomic_flush: $(DB_STRESS_CMD)
$(CRASHTEST_MAKE) whitebox_crash_test_with_atomic_flush
$(CRASHTEST_MAKE) blackbox_crash_test_with_atomic_flush
crash_test_with_wc_txn: $(DB_STRESS_CMD)
crash_test_with_txn: $(DB_STRESS_CMD)
# Do not parallelize
$(CRASHTEST_MAKE) whitebox_crash_test_with_wc_txn
$(CRASHTEST_MAKE) blackbox_crash_test_with_wc_txn
crash_test_with_wp_txn: $(DB_STRESS_CMD)
# Do not parallelize
$(CRASHTEST_MAKE) whitebox_crash_test_with_wp_txn
$(CRASHTEST_MAKE) blackbox_crash_test_with_wp_txn
crash_test_with_wup_txn: $(DB_STRESS_CMD)
# Do not parallelize
$(CRASHTEST_MAKE) whitebox_crash_test_with_wup_txn
$(CRASHTEST_MAKE) blackbox_crash_test_with_wup_txn
$(CRASHTEST_MAKE) whitebox_crash_test_with_txn
$(CRASHTEST_MAKE) blackbox_crash_test_with_txn
crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
# Do not parallelize
@@ -83,9 +62,6 @@ crash_test_with_multiops_wc_txn: $(DB_STRESS_CMD)
crash_test_with_multiops_wp_txn: $(DB_STRESS_CMD)
$(CRASHTEST_MAKE) blackbox_crash_test_with_multiops_wp_txn
crash_test_with_multiops_wup_txn: $(DB_STRESS_CMD)
$(CRASHTEST_MAKE) blackbox_crash_test_with_multiops_wup_txn
blackbox_crash_test: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --simple blackbox $(CRASH_TEST_EXT_ARGS)
$(CRASHTEST_PY) blackbox $(CRASH_TEST_EXT_ARGS)
@@ -93,14 +69,8 @@ blackbox_crash_test: $(DB_STRESS_CMD)
blackbox_crash_test_with_atomic_flush: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --cf_consistency blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_wc_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn blackbox --txn_write_policy 0 $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_wp_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn blackbox --txn_write_policy 1 $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_wup_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn blackbox --txn_write_policy 2 $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_best_efforts_recovery: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --test_best_efforts_recovery blackbox $(CRASH_TEST_EXT_ARGS)
@@ -109,13 +79,10 @@ blackbox_crash_test_with_ts: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --enable_ts blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_multiops_wc_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --test_multiops_txn --txn_write_policy 0 blackbox $(CRASH_TEST_EXT_ARGS)
$(CRASHTEST_PY) --test_multiops_txn --write_policy write_committed blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_multiops_wp_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --test_multiops_txn --txn_write_policy 1 blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_multiops_wup_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --test_multiops_txn --txn_write_policy 2 blackbox $(CRASH_TEST_EXT_ARGS)
$(CRASHTEST_PY) --test_multiops_txn --write_policy write_prepared blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_tiered_storage: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --test_tiered_storage blackbox $(CRASH_TEST_EXT_ARGS)
@@ -137,17 +104,9 @@ whitebox_crash_test_with_atomic_flush: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --cf_consistency whitebox --random_kill_odd \
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
whitebox_crash_test_with_wc_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn whitebox --txn_write_policy 0 \
--random_kill_odd $(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
whitebox_crash_test_with_wp_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn whitebox --txn_write_policy 1 \
--random_kill_odd $(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
whitebox_crash_test_with_wup_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn whitebox --txn_write_policy 2 \
--random_kill_odd $(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
whitebox_crash_test_with_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn whitebox --random_kill_odd \
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
whitebox_crash_test_with_ts: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --enable_ts whitebox --random_kill_odd \
@@ -160,8 +119,3 @@ whitebox_crash_test_with_tiered_storage: $(DB_STRESS_CMD)
whitebox_crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --optimistic_txn whitebox --random_kill_odd \
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
# Old names DEPRECATED
crash_test_with_txn: crash_test_with_wc_txn
whitebox_crash_test_with_txn: whitebox_crash_test_with_wc_txn
blackbox_crash_test_with_txn: blackbox_crash_test_with_wc_txn
+52 -146
View File
@@ -42,138 +42,27 @@ Status ArenaWrappedDBIter::GetProperty(std::string prop_name,
void ArenaWrappedDBIter::Init(
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options, const Version* version,
const SequenceNumber& sequence, uint64_t version_number,
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
bool expose_blob_index, bool allow_refresh, ReadOnlyMemTable* active_mem) {
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iteration,
uint64_t version_number, ReadCallback* read_callback,
ColumnFamilyHandleImpl* cfh, bool expose_blob_index, bool allow_refresh) {
auto mem = arena_.AllocateAligned(sizeof(DBIter));
db_iter_ = new (mem) DBIter(
env, read_options, ioptions, mutable_cf_options, ioptions.user_comparator,
/* iter */ nullptr, version, sequence, true,
max_sequential_skip_in_iteration, read_callback, cfh, expose_blob_index);
sv_number_ = version_number;
read_options_ = read_options;
allow_refresh_ = allow_refresh;
memtable_range_tombstone_iter_ = nullptr;
if (!CheckFSFeatureSupport(env->GetFileSystem().get(),
FSSupportedOps::kAsyncIO)) {
read_options_.async_io = false;
}
read_options_.total_order_seek |= ioptions.prefix_seek_opt_in_only;
db_iter_ = DBIter::NewIter(
env, read_options_, ioptions, mutable_cf_options,
ioptions.user_comparator, /*internal_iter=*/nullptr, version, sequence,
read_callback, active_mem, cfh, expose_blob_index, &arena_);
sv_number_ = version_number;
allow_refresh_ = allow_refresh;
allow_mark_memtable_for_flush_ = active_mem;
memtable_range_tombstone_iter_ = nullptr;
}
void ArenaWrappedDBIter::MaybeAutoRefresh(bool is_seek,
DBIter::Direction direction) {
if (cfh_ != nullptr && read_options_.snapshot != nullptr && allow_refresh_ &&
read_options_.auto_refresh_iterator_with_snapshot) {
// The intent here is to capture the superversion number change
// reasonably soon from the time it actually happened. As such,
// we're fine with weaker synchronization / ordering guarantees
// provided by relaxed atomic (in favor of less CPU / mem overhead).
uint64_t cur_sv_number = cfh_->cfd()->GetSuperVersionNumberRelaxed();
if ((sv_number_ != cur_sv_number) && status().ok()) {
// Changing iterators' direction is pretty heavy-weight operation and
// could have unintended consequences when it comes to prefix seek.
// Therefore, we need an efficient implementation that does not duplicate
// the effort by doing things like double seek(forprev).
//
// Auto refresh can be triggered on the following groups of operations:
//
// 1. [Seek]: Seek(), SeekForPrev()
// 2. [Non-Seek]: Next(), Prev()
//
// In case of 'Seek' group, procedure is fairly straightforward as we'll
// simply call refresh and then invoke the operation on intended target.
//
// In case of 'Non-Seek' group, we'll first advance the cursor by invoking
// intended user operation (Next() or Prev()), capture the target key T,
// refresh the iterator and then reconcile the refreshed iterator by
// explicitly calling [Seek(T) or SeekForPrev(T)]. Below is an example
// flow for Next(), but same principle applies to Prev():
//
//
// T0: Before the operation T1: Execute Next()
// | |
// | -------------
// | | * capture the key (T)
// DBIter(SV#A) | |
// --------------\ /------\ /---------
// SV #A | ... -> [ X ] -> [ T ] -> ... |
// -----------------------------------
// / |
// / |
// / T2: Refresh iterator
// /
// DBIter(SV#A') /
// ----------------------------------
// SV #A' | ... -> [ T ] -> ... |
// ----------------/ \---------------
// |
// ---- T3: Seek(T)
//
bool valid = false;
std::string key;
if (!is_seek && db_iter_->Valid()) {
// The key() Slice is valid until the iterator state changes.
// Given that refresh is heavy-weight operation it itself,
// we should copy the target key upfront to avoid reading bad value.
valid = true;
key = db_iter_->key().ToString();
}
// It's perfectly fine to unref the corresponding superversion
// as we rely on pinning behavior of snapshot for consistency.
DoRefresh(read_options_.snapshot, cur_sv_number);
if (!is_seek && valid) { // Reconcile new iterator after Next() / Prev()
if (direction == DBIter::kForward) {
db_iter_->Seek(key);
} else {
assert(direction == DBIter::kReverse);
db_iter_->SeekForPrev(key);
}
}
}
}
}
Status ArenaWrappedDBIter::Refresh() { return Refresh(nullptr); }
void ArenaWrappedDBIter::DoRefresh(const Snapshot* snapshot,
[[maybe_unused]] uint64_t sv_number) {
Env* env = db_iter_->env();
// NOTE:
//
// Errors like file deletion (as a part of SV cleanup in ~DBIter) will be
// present in the error log, but won't be reflected in the iterator status.
// This is by design as we expect compaction to clean up those obsolete files
// eventually.
db_iter_->~DBIter();
arena_.~Arena();
new (&arena_) Arena();
auto cfd = cfh_->cfd();
auto db_impl = cfh_->db();
SuperVersion* sv = cfd->GetReferencedSuperVersion(db_impl);
assert(sv->version_number >= sv_number);
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
if (read_callback_) {
read_callback_->Refresh(read_seq);
}
Init(env, read_options_, cfd->ioptions(), sv->mutable_cf_options, sv->current,
read_seq, sv->version_number, read_callback_, cfh_, expose_blob_index_,
allow_refresh_, allow_mark_memtable_for_flush_ ? sv->mem : nullptr);
InternalIterator* internal_iter = db_impl->NewInternalIterator(
read_options_, cfd, sv, &arena_, read_seq,
/* allow_unprepared_value */ true, /* db_iter */ this);
SetIterUnderDBIter(internal_iter);
}
Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
if (cfh_ == nullptr || !allow_refresh_) {
return Status::NotSupported("Creating renew iterator is not allowed.");
@@ -193,9 +82,32 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:1");
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:2");
auto reinit_internal_iter = [&]() {
Env* env = db_iter_->env();
db_iter_->~DBIter();
arena_.~Arena();
new (&arena_) Arena();
SuperVersion* sv = cfd->GetReferencedSuperVersion(db_impl);
assert(sv->version_number >= cur_sv_number);
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
if (read_callback_) {
read_callback_->Refresh(read_seq);
}
Init(env, read_options_, *(cfd->ioptions()), sv->mutable_cf_options,
sv->current, read_seq,
sv->mutable_cf_options.max_sequential_skip_in_iterations,
sv->version_number, read_callback_, cfh_, expose_blob_index_,
allow_refresh_);
InternalIterator* internal_iter = db_impl->NewInternalIterator(
read_options_, cfd, sv, &arena_, read_seq,
/* allow_unprepared_value */ true, /* db_iter */ this);
SetIterUnderDBIter(internal_iter);
};
while (true) {
if (sv_number_ != cur_sv_number) {
DoRefresh(snapshot, cur_sv_number);
reinit_internal_iter();
break;
} else {
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
@@ -223,13 +135,13 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
// The memtable under DBIter did not have range tombstone before
// refresh.
DoRefresh(snapshot, cur_sv_number);
reinit_internal_iter();
break;
} else {
*memtable_range_tombstone_iter_ =
std::make_unique<TruncatedRangeDelIterator>(
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
&cfd->internal_comparator(), nullptr, nullptr);
delete *memtable_range_tombstone_iter_;
*memtable_range_tombstone_iter_ = new TruncatedRangeDelIterator(
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
&cfd->internal_comparator(), nullptr, nullptr);
}
}
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
@@ -251,26 +163,20 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
}
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
Env* env, const ReadOptions& read_options, ColumnFamilyHandleImpl* cfh,
SuperVersion* sv, const SequenceNumber& sequence,
ReadCallback* read_callback, DBImpl* db_impl, bool expose_blob_index,
bool allow_refresh, bool allow_mark_memtable_for_flush) {
ArenaWrappedDBIter* db_iter = new ArenaWrappedDBIter();
db_iter->Init(env, read_options, cfh->cfd()->ioptions(),
sv->mutable_cf_options, sv->current, sequence,
sv->version_number, read_callback, cfh, expose_blob_index,
allow_refresh,
allow_mark_memtable_for_flush ? sv->mem : nullptr);
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,
uint64_t version_number, ReadCallback* read_callback,
ColumnFamilyHandleImpl* cfh, bool expose_blob_index, bool allow_refresh) {
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
iter->Init(env, read_options, ioptions, mutable_cf_options, version, sequence,
max_sequential_skip_in_iterations, version_number, read_callback,
cfh, expose_blob_index, allow_refresh);
if (cfh != nullptr && allow_refresh) {
db_iter->StoreRefreshInfo(cfh, read_callback, expose_blob_index);
iter->StoreRefreshInfo(cfh, read_callback, expose_blob_index);
}
InternalIterator* internal_iter = db_impl->NewInternalIterator(
db_iter->GetReadOptions(), cfh->cfd(), sv, db_iter->GetArena(), sequence,
/*allow_unprepared_value=*/true, db_iter);
db_iter->SetIterUnderDBIter(internal_iter);
return db_iter;
return iter;
}
} // namespace ROCKSDB_NAMESPACE
+18 -40
View File
@@ -19,6 +19,7 @@
#include "options/cf_options.h"
#include "rocksdb/db.h"
#include "rocksdb/iterator.h"
#include "util/autovector.h"
namespace ROCKSDB_NAMESPACE {
@@ -54,8 +55,7 @@ class ArenaWrappedDBIter : public Iterator {
db_iter_->SetIter(iter);
}
void SetMemtableRangetombstoneIter(
std::unique_ptr<TruncatedRangeDelIterator>* iter) {
void SetMemtableRangetombstoneIter(TruncatedRangeDelIterator** iter) {
memtable_range_tombstone_iter_ = iter;
}
@@ -64,26 +64,12 @@ class ArenaWrappedDBIter : public Iterator {
void SeekToLast() override { db_iter_->SeekToLast(); }
// 'target' does not contain timestamp, even if user timestamp feature is
// enabled.
void Seek(const Slice& target) override {
MaybeAutoRefresh(true /* is_seek */, DBIter::kForward);
db_iter_->Seek(target);
}
void Seek(const Slice& target) override { db_iter_->Seek(target); }
void SeekForPrev(const Slice& target) override {
MaybeAutoRefresh(true /* is_seek */, DBIter::kReverse);
db_iter_->SeekForPrev(target);
}
void Next() override {
db_iter_->Next();
MaybeAutoRefresh(false /* is_seek */, DBIter::kForward);
}
void Prev() override {
db_iter_->Prev();
MaybeAutoRefresh(false /* is_seek */, DBIter::kReverse);
}
void Next() override { db_iter_->Next(); }
void Prev() override { db_iter_->Prev(); }
Slice key() const override { return db_iter_->key(); }
Slice value() const override { return db_iter_->value(); }
const WideColumns& columns() const override { return db_iter_->columns(); }
@@ -96,21 +82,13 @@ class ArenaWrappedDBIter : public Iterator {
Status Refresh() override;
Status Refresh(const Snapshot*) override;
bool PrepareValue() override { return db_iter_->PrepareValue(); }
void Prepare(const MultiScanArgs& scan_opts) override {
db_iter_->Prepare(scan_opts);
}
// FIXME: we could just pass SV in for mutable cf option, version and version
// number, but this is used by SstFileReader which does not have a SV.
void Init(Env* env, const ReadOptions& read_options,
const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options, const Version* version,
const SequenceNumber& sequence, uint64_t version_number,
const SequenceNumber& sequence,
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
bool expose_blob_index, bool allow_refresh,
ReadOnlyMemTable* active_mem);
bool expose_blob_index, bool allow_refresh);
// Store some parameters so we can refresh the iterator at a later point
// with these same params
@@ -122,9 +100,6 @@ class ArenaWrappedDBIter : public Iterator {
}
private:
void DoRefresh(const Snapshot* snapshot, uint64_t sv_number);
void MaybeAutoRefresh(bool is_seek, DBIter::Direction direction);
DBIter* db_iter_ = nullptr;
Arena arena_;
uint64_t sv_number_;
@@ -133,16 +108,19 @@ class ArenaWrappedDBIter : public Iterator {
ReadCallback* read_callback_;
bool expose_blob_index_ = false;
bool allow_refresh_ = true;
bool allow_mark_memtable_for_flush_ = true;
// If this is nullptr, it means the mutable memtable does not contain range
// tombstone when added under this DBIter.
std::unique_ptr<TruncatedRangeDelIterator>* memtable_range_tombstone_iter_ =
nullptr;
TruncatedRangeDelIterator** memtable_range_tombstone_iter_ = nullptr;
};
// Generate the arena wrapped iterator class.
// `cfh` is used for reneweal. If left null, renewal will not
// be supported.
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
Env* env, const ReadOptions& read_options, ColumnFamilyHandleImpl* cfh,
SuperVersion* sv, const SequenceNumber& sequence,
ReadCallback* read_callback, DBImpl* db_impl, bool expose_blob_index,
bool allow_refresh, bool allow_mark_memtable_for_flush);
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,
uint64_t version_number, ReadCallback* read_callback,
ColumnFamilyHandleImpl* cfh = nullptr, bool expose_blob_index = false,
bool allow_refresh = true);
} // namespace ROCKSDB_NAMESPACE
+9 -34
View File
@@ -13,11 +13,14 @@ namespace ROCKSDB_NAMESPACE {
class AttributeGroupIteratorImpl : public AttributeGroupIterator {
public:
AttributeGroupIteratorImpl(
const ReadOptions& read_options, const Comparator* comparator,
std::vector<std::pair<ColumnFamilyHandle*, std::unique_ptr<Iterator>>>&&
cfh_iter_pairs)
: impl_(read_options, comparator, std::move(cfh_iter_pairs),
ResetFunc(this), PopulateFunc(this)) {}
const Comparator* comparator,
const std::vector<ColumnFamilyHandle*>& column_families,
const std::vector<Iterator*>& child_iterators)
: impl_(
comparator, column_families, child_iterators, [this]() { Reset(); },
[this](const autovector<MultiCfIteratorInfo>& items) {
AddToAttributeGroups(items);
}) {}
~AttributeGroupIteratorImpl() override {}
// No copy allowed
@@ -42,36 +45,8 @@ class AttributeGroupIteratorImpl : public AttributeGroupIterator {
void Reset() { attribute_groups_.clear(); }
bool PrepareValue() override { return impl_.PrepareValue(); }
private:
class ResetFunc {
public:
explicit ResetFunc(AttributeGroupIteratorImpl* iter) : iter_(iter) {}
void operator()() const {
assert(iter_);
iter_->Reset();
}
private:
AttributeGroupIteratorImpl* iter_;
};
class PopulateFunc {
public:
explicit PopulateFunc(AttributeGroupIteratorImpl* iter) : iter_(iter) {}
void operator()(const autovector<MultiCfIteratorInfo>& items) const {
assert(iter_);
iter_->AddToAttributeGroups(items);
}
private:
AttributeGroupIteratorImpl* iter_;
};
MultiCfIteratorImpl<ResetFunc, PopulateFunc> impl_;
MultiCfIteratorImpl impl_;
IteratorAttributeGroups attribute_groups_;
void AddToAttributeGroups(const autovector<MultiCfIteratorInfo>& items);
};
+7 -10
View File
@@ -188,12 +188,10 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
}
std::unique_ptr<FSWritableFile> file;
FileOptions fo_copy;
{
assert(file_options_);
fo_copy = *file_options_;
fo_copy.write_hint = write_hint_;
Status s = NewWritableFile(fs_, blob_file_path, &file, fo_copy);
Status s = NewWritableFile(fs_, blob_file_path, &file, *file_options_);
TEST_SYNC_POINT_CALLBACK(
"BlobFileBuilder::OpenBlobFileIfNeeded:NewWritableFile", &s);
@@ -211,9 +209,7 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
assert(file);
file->SetIOPriority(write_options_->rate_limiter_priority);
// Subsequent attempts to override the hint via SetWriteLifeTimeHint
// with the very same value will be ignored by the fs.
file->SetWriteLifeTimeHint(fo_copy.write_hint);
file->SetWriteLifeTimeHint(write_hint_);
FileTypeSet tmp_set = immutable_options_->checksum_handoff_file_types;
Statistics* const statistics = immutable_options_->stats;
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
@@ -267,9 +263,10 @@ Status BlobFileBuilder::CompressBlobIfNeeded(
// TODO: allow user CompressionOptions, including max_compressed_bytes_per_kb
CompressionOptions opts;
CompressionContext context(blob_compression_type_, opts);
constexpr uint64_t sample_for_compression = 0;
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
blob_compression_type_);
blob_compression_type_, sample_for_compression);
constexpr uint32_t compression_format_version = 2;
@@ -278,8 +275,8 @@ Status BlobFileBuilder::CompressBlobIfNeeded(
{
StopWatch stop_watch(immutable_options_->clock, immutable_options_->stats,
BLOB_DB_COMPRESSION_MICROS);
success = OLD_CompressData(*blob, info, compression_format_version,
compressed_blob);
success =
CompressData(*blob, info, compression_format_version, compressed_blob);
}
if (!success) {
+2 -1
View File
@@ -405,9 +405,10 @@ TEST_F(BlobFileBuilderTest, Compression) {
CompressionOptions opts;
CompressionContext context(kSnappyCompression, opts);
constexpr uint64_t sample_for_compression = 0;
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
kSnappyCompression);
kSnappyCompression, sample_for_compression);
std::string compressed_value;
ASSERT_TRUE(Snappy_Compress(info, uncompressed_value.data(),
-10
View File
@@ -42,7 +42,6 @@ Status BlobFileCache::GetBlobFileReader(
assert(blob_file_reader);
assert(blob_file_reader->IsEmpty());
// NOTE: sharing same Cache with table_cache
const Slice key = GetSliceForKey(&blob_file_number);
assert(cache_);
@@ -99,13 +98,4 @@ Status BlobFileCache::GetBlobFileReader(
return Status::OK();
}
void BlobFileCache::Evict(uint64_t blob_file_number) {
// NOTE: sharing same Cache with table_cache
const Slice key = GetSliceForKey(&blob_file_number);
assert(cache_);
cache_.get()->Erase(key);
}
} // namespace ROCKSDB_NAMESPACE
-9
View File
@@ -36,15 +36,6 @@ class BlobFileCache {
uint64_t blob_file_number,
CacheHandleGuard<BlobFileReader>* blob_file_reader);
// Called when a blob file is obsolete to ensure it is removed from the cache
// to avoid effectively leaking the open file and assicated memory
void Evict(uint64_t blob_file_number);
// Used to identify cache entries for blob files (not normally useful)
static const Cache::CacheItemHelper* GetHelper() {
return CacheInterface::GetBasicHelper();
}
private:
using CacheInterface =
BasicTypedCacheInterface<BlobFileReader, CacheEntryRole::kMisc>;
-1
View File
@@ -6,7 +6,6 @@
#pragma once
#include <cassert>
#include <cstdint>
#include <iosfwd>
#include <memory>
#include <string>
+9 -12
View File
@@ -250,8 +250,7 @@ Status BlobFileReader::ReadFromFile(const RandomAccessFileReader* file_reader,
Status s;
IOOptions io_options;
IODebugContext dbg;
s = file_reader->PrepareIOOptions(read_options, io_options, &dbg);
s = file_reader->PrepareIOOptions(read_options, io_options);
if (!s.ok()) {
return s;
}
@@ -260,13 +259,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, &dbg);
aligned_buf);
} 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, &dbg);
aligned_scratch);
}
if (!s.ok()) {
@@ -335,8 +334,7 @@ Status BlobFileReader::GetBlob(
constexpr bool for_compaction = true;
IOOptions io_options;
IODebugContext dbg;
s = file_reader_->PrepareIOOptions(read_options, io_options, &dbg);
s = file_reader_->PrepareIOOptions(read_options, io_options);
if (!s.ok()) {
return s;
}
@@ -465,11 +463,10 @@ void BlobFileReader::MultiGetBlob(
PERF_COUNTER_ADD(blob_read_count, num_blobs);
PERF_COUNTER_ADD(blob_read_byte, total_len);
IOOptions opts;
IODebugContext dbg;
s = file_reader_->PrepareIOOptions(read_options, opts, &dbg);
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, &dbg);
direct_io ? &aligned_buf : nullptr);
}
if (!s.ok()) {
for (auto& req : read_reqs) {
@@ -605,9 +602,9 @@ Status BlobFileReader::UncompressBlobIfNeeded(
{
PERF_TIMER_GUARD(blob_decompress_time);
StopWatch stop_watch(clock, statistics, BLOB_DB_DECOMPRESSION_MICROS);
output = OLD_UncompressData(info, value_slice.data(), value_slice.size(),
&uncompressed_size, compression_format_version,
allocator);
output = UncompressData(info, value_slice.data(), value_slice.size(),
&uncompressed_size, compression_format_version,
allocator);
}
TEST_SYNC_POINT_CALLBACK(
+4 -3
View File
@@ -75,14 +75,15 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
} else {
CompressionOptions opts;
CompressionContext context(compression, opts);
constexpr uint64_t sample_for_compression = 0;
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
compression);
compression, sample_for_compression);
constexpr uint32_t compression_format_version = 2;
for (size_t i = 0; i < num; ++i) {
ASSERT_TRUE(OLD_CompressData(blobs[i], info, compression_format_version,
&compressed_blobs[i]));
ASSERT_TRUE(CompressData(blobs[i], info, compression_format_version,
&compressed_blobs[i]));
blobs_to_write[i] = compressed_blobs[i];
blob_sizes[i] = compressed_blobs[i].size();
}
+8 -9
View File
@@ -20,24 +20,23 @@
namespace ROCKSDB_NAMESPACE {
BlobSource::BlobSource(const ImmutableOptions& immutable_options,
const MutableCFOptions& mutable_cf_options,
BlobSource::BlobSource(const ImmutableOptions* immutable_options,
const std::string& db_id,
const std::string& db_session_id,
BlobFileCache* blob_file_cache)
: db_id_(db_id),
db_session_id_(db_session_id),
statistics_(immutable_options.statistics.get()),
statistics_(immutable_options->statistics.get()),
blob_file_cache_(blob_file_cache),
blob_cache_(immutable_options.blob_cache),
lowest_used_cache_tier_(immutable_options.lowest_used_cache_tier) {
blob_cache_(immutable_options->blob_cache),
lowest_used_cache_tier_(immutable_options->lowest_used_cache_tier) {
auto bbto =
mutable_cf_options.table_factory->GetOptions<BlockBasedTableOptions>();
immutable_options->table_factory->GetOptions<BlockBasedTableOptions>();
if (bbto &&
bbto->cache_usage_options.options_overrides.at(CacheEntryRole::kBlobCache)
.charged == CacheEntryRoleOptions::Decision::kEnabled) {
blob_cache_ = SharedCacheInterface{std::make_shared<ChargedCache>(
immutable_options.blob_cache, bbto->block_cache)};
immutable_options->blob_cache, bbto->block_cache)};
}
}
@@ -82,8 +81,8 @@ Status BlobSource::PutBlobIntoCache(
assert(cached_blob->IsEmpty());
TypedHandle* cache_handle = nullptr;
const Status s = InsertEntryIntoCache(cache_key, blob->get(), &cache_handle,
Cache::Priority::BOTTOM);
const Status s = InsertEntryIntoCache(cache_key, blob->get(),
&cache_handle, Cache::Priority::BOTTOM);
if (s.ok()) {
blob->release();
+1 -5
View File
@@ -21,7 +21,6 @@
namespace ROCKSDB_NAMESPACE {
struct ImmutableOptions;
struct MutableCFOptions;
class Status;
class FilePrefetchBuffer;
class Slice;
@@ -32,10 +31,7 @@ class Slice;
// storage with minimal cost.
class BlobSource {
public:
// NOTE: db_id, db_session_id, and blob_file_cache are saved by reference or
// pointer.
BlobSource(const ImmutableOptions& immutable_options,
const MutableCFOptions& mutable_cf_options,
BlobSource(const ImmutableOptions* immutable_options,
const std::string& db_id, const std::string& db_session_id,
BlobFileCache* blob_file_cache);
+18 -25
View File
@@ -77,14 +77,15 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
} else {
CompressionOptions opts;
CompressionContext context(compression, opts);
constexpr uint64_t sample_for_compression = 0;
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
compression);
compression, sample_for_compression);
constexpr uint32_t compression_format_version = 2;
for (size_t i = 0; i < num; ++i) {
ASSERT_TRUE(OLD_CompressData(blobs[i], info, compression_format_version,
&compressed_blobs[i]));
ASSERT_TRUE(CompressData(blobs[i], info, compression_format_version,
&compressed_blobs[i]));
blobs_to_write[i] = compressed_blobs[i];
blob_sizes[i] = compressed_blobs[i].size();
}
@@ -147,7 +148,6 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr uint32_t column_family_id = 1;
constexpr bool has_ttl = false;
@@ -193,8 +193,8 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
backing_cache.get(), &immutable_options, &file_options,
column_family_id, blob_file_read_hist, nullptr /*IOTracer*/);
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
ReadOptions read_options;
read_options.verify_checksums = true;
@@ -464,7 +464,6 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr uint32_t column_family_id = 1;
constexpr bool has_ttl = false;
@@ -499,8 +498,8 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
backing_cache.get(), &immutable_options, &file_options,
column_family_id, nullptr /*HistogramImpl*/, nullptr /*IOTracer*/);
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
ReadOptions read_options;
read_options.verify_checksums = true;
@@ -590,7 +589,6 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr uint32_t column_family_id = 1;
constexpr bool has_ttl = false;
@@ -646,8 +644,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
backing_cache.get(), &immutable_options, &file_options,
column_family_id, blob_file_read_hist, nullptr /*IOTracer*/);
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
ReadOptions read_options;
read_options.verify_checksums = true;
@@ -784,7 +782,6 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr uint32_t column_family_id = 1;
constexpr bool has_ttl = false;
@@ -830,8 +827,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
backing_cache.get(), &immutable_options, &file_options,
column_family_id, blob_file_read_hist, nullptr /*IOTracer*/);
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
ReadOptions read_options;
read_options.verify_checksums = true;
@@ -1108,7 +1105,6 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr uint32_t column_family_id = 1;
constexpr bool has_ttl = false;
@@ -1141,8 +1137,8 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
backing_cache.get(), &immutable_options, &file_options, column_family_id,
blob_file_read_hist, nullptr /*IOTracer*/));
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
CacheHandleGuard<BlobFileReader> file_reader;
ReadOptions read_options;
@@ -1409,7 +1405,6 @@ TEST_F(BlobSourceCacheReservationTest, SimpleCacheReservation) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr ExpirationRange expiration_range;
@@ -1431,8 +1426,8 @@ TEST_F(BlobSourceCacheReservationTest, SimpleCacheReservation) {
backing_cache.get(), &immutable_options, &file_options,
kColumnFamilyId, blob_file_read_hist, nullptr /*IOTracer*/);
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
ConcurrentCacheReservationManager* cache_res_mgr =
static_cast<ChargedCache*>(blob_source.GetBlobCache())
@@ -1524,8 +1519,6 @@ TEST_F(BlobSourceCacheReservationTest, IncreaseCacheReservation) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr size_t blob_size = 24 << 10; // 24KB
for (size_t i = 0; i < kNumBlobs; ++i) {
blob_file_size_ -= blobs_[i].size(); // old blob size
@@ -1553,8 +1546,8 @@ TEST_F(BlobSourceCacheReservationTest, IncreaseCacheReservation) {
backing_cache.get(), &immutable_options, &file_options,
kColumnFamilyId, blob_file_read_hist, nullptr /*IOTracer*/);
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
ConcurrentCacheReservationManager* cache_res_mgr =
static_cast<ChargedCache*>(blob_source.GetBlobCache())
-149
View File
@@ -374,115 +374,6 @@ TEST_F(DBBlobBasicTest, IterateBlobsFromCachePinning) {
}
}
TEST_F(DBBlobBasicTest, IterateBlobsAllowUnpreparedValue) {
Options options = GetDefaultOptions();
options.enable_blob_files = true;
Reopen(options);
constexpr size_t num_blobs = 5;
std::vector<std::string> keys;
std::vector<std::string> blobs;
for (size_t i = 0; i < num_blobs; ++i) {
keys.emplace_back("key" + std::to_string(i));
blobs.emplace_back("blob" + std::to_string(i));
ASSERT_OK(Put(keys[i], blobs[i]));
}
ASSERT_OK(Flush());
ReadOptions read_options;
read_options.allow_unprepared_value = true;
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
{
size_t i = 0;
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
ASSERT_EQ(iter->key(), keys[i]);
ASSERT_TRUE(iter->value().empty());
ASSERT_OK(iter->status());
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(iter->key(), keys[i]);
ASSERT_EQ(iter->value(), blobs[i]);
ASSERT_OK(iter->status());
++i;
}
ASSERT_OK(iter->status());
ASSERT_EQ(i, num_blobs);
}
{
size_t i = 0;
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
ASSERT_TRUE(iter->value().empty());
ASSERT_OK(iter->status());
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
ASSERT_EQ(iter->value(), blobs[num_blobs - 1 - i]);
ASSERT_OK(iter->status());
++i;
}
ASSERT_OK(iter->status());
ASSERT_EQ(i, num_blobs);
}
{
size_t i = 1;
for (iter->Seek(keys[i]); iter->Valid(); iter->Next()) {
ASSERT_EQ(iter->key(), keys[i]);
ASSERT_TRUE(iter->value().empty());
ASSERT_OK(iter->status());
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(iter->key(), keys[i]);
ASSERT_EQ(iter->value(), blobs[i]);
ASSERT_OK(iter->status());
++i;
}
ASSERT_OK(iter->status());
ASSERT_EQ(i, num_blobs);
}
{
size_t i = 1;
for (iter->SeekForPrev(keys[num_blobs - 1 - i]); iter->Valid();
iter->Prev()) {
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
ASSERT_TRUE(iter->value().empty());
ASSERT_OK(iter->status());
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
ASSERT_EQ(iter->value(), blobs[num_blobs - 1 - i]);
ASSERT_OK(iter->status());
++i;
}
ASSERT_OK(iter->status());
ASSERT_EQ(i, num_blobs);
}
}
TEST_F(DBBlobBasicTest, MultiGetBlobs) {
constexpr size_t min_blob_size = 6;
@@ -1764,46 +1655,6 @@ TEST_P(DBBlobBasicIOErrorTest, CompactionFilterReadBlob_IOError) {
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_P(DBBlobBasicIOErrorTest, IterateBlobsAllowUnpreparedValue_IOError) {
Options options;
options.env = fault_injection_env_.get();
options.enable_blob_files = true;
Reopen(options);
constexpr char key[] = "key";
constexpr char blob_value[] = "blob_value";
ASSERT_OK(Put(key, blob_value));
ASSERT_OK(Flush());
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
fault_injection_env_->SetFilesystemActive(false,
Status::IOError(sync_point_));
});
SyncPoint::GetInstance()->EnableProcessing();
ReadOptions read_options;
read_options.allow_unprepared_value = true;
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
iter->SeekToFirst();
ASSERT_TRUE(iter->Valid());
ASSERT_EQ(iter->key(), key);
ASSERT_TRUE(iter->value().empty());
ASSERT_OK(iter->status());
ASSERT_FALSE(iter->PrepareValue());
ASSERT_FALSE(iter->Valid());
ASSERT_TRUE(iter->status().IsIOError());
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_F(DBBlobBasicTest, WarmCacheWithBlobsDuringFlush) {
Options options = GetDefaultOptions();
+36 -41
View File
@@ -53,7 +53,7 @@ TableBuilder* NewTableBuilder(const TableBuilderOptions& tboptions,
assert((tboptions.column_family_id ==
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
tboptions.column_family_name.empty());
return tboptions.moptions.table_factory->NewTableBuilder(tboptions, file);
return tboptions.ioptions.table_factory->NewTableBuilder(tboptions, file);
}
Status BuildTable(
@@ -64,7 +64,7 @@ Status BuildTable(
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
range_del_iters,
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
std::vector<SequenceNumber> snapshots, SequenceNumber earliest_snapshot,
std::vector<SequenceNumber> snapshots,
SequenceNumber earliest_write_conflict_snapshot,
SequenceNumber job_snapshot, SnapshotChecker* snapshot_checker,
bool paranoid_file_checks, InternalStats* internal_stats,
@@ -74,8 +74,8 @@ Status BuildTable(
EventLogger* event_logger, int job_id, TableProperties* table_properties,
Env::WriteLifeTimeHint write_hint, const std::string* full_history_ts_low,
BlobFileCompletionCallback* blob_callback, Version* version,
uint64_t* memtable_payload_bytes, uint64_t* memtable_garbage_bytes,
InternalStats::CompactionStats* flush_stats) {
uint64_t* num_input_entries, uint64_t* memtable_payload_bytes,
uint64_t* memtable_garbage_bytes) {
assert((tboptions.column_family_id ==
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
tboptions.column_family_name.empty());
@@ -145,9 +145,7 @@ Status BuildTable(
bool use_direct_writes = file_options.use_direct_writes;
TEST_SYNC_POINT_CALLBACK("BuildTable:create_file", &use_direct_writes);
#endif // !NDEBUG
FileOptions fo_copy = file_options;
fo_copy.write_hint = write_hint;
IOStatus io_s = NewWritableFile(fs, fname, &file, fo_copy);
IOStatus io_s = NewWritableFile(fs, fname, &file, file_options);
assert(s.ok());
s = io_s;
if (io_status->ok()) {
@@ -165,9 +163,7 @@ Status BuildTable(
table_file_created = true;
FileTypeSet tmp_set = ioptions.checksum_handoff_file_types;
file->SetIOPriority(tboptions.write_options.rate_limiter_priority);
// Subsequent attempts to override the hint via SetWriteLifeTimeHint
// with the very same value will be ignored by the fs.
file->SetWriteLifeTimeHint(fo_copy.write_hint);
file->SetWriteLifeTimeHint(write_hint);
file_writer.reset(new WritableFileWriter(
std::move(file), fname, file_options, ioptions.clock, io_tracer,
ioptions.stats, Histograms::SST_WRITE_MICROS, ioptions.listeners,
@@ -199,9 +195,10 @@ Status BuildTable(
const std::atomic<bool> kManualCompactionCanceledFalse{false};
CompactionIterator c_iter(
iter, ucmp, &merge, kMaxSequenceNumber, &snapshots, earliest_snapshot,
iter, ucmp, &merge, kMaxSequenceNumber, &snapshots,
earliest_write_conflict_snapshot, job_snapshot, snapshot_checker, env,
ShouldReportDetailedTime(env, ioptions.stats), range_del_agg.get(),
ShouldReportDetailedTime(env, ioptions.stats),
true /* internal key corruption is not ok */, range_del_agg.get(),
blob_file_builder.get(), ioptions.allow_data_in_errors,
ioptions.enforce_single_del_contracts,
/*manual_compaction_canceled=*/kManualCompactionCanceledFalse,
@@ -209,6 +206,10 @@ Status BuildTable(
/*compaction=*/nullptr, compaction_filter.get(),
/*shutting_down=*/nullptr, db_options.info_log, full_history_ts_low);
const size_t ts_sz = ucmp->timestamp_size();
const bool logical_strip_timestamp =
ts_sz > 0 && !ioptions.persist_user_defined_timestamps;
SequenceNumber smallest_preferred_seqno = kMaxSequenceNumber;
std::string key_after_flush_buf;
std::string value_buf;
@@ -217,9 +218,20 @@ Status BuildTable(
const Slice& key = c_iter.key();
const Slice& value = c_iter.value();
ParsedInternalKey ikey = c_iter.ikey();
Slice key_after_flush = key;
key_after_flush_buf.assign(key.data(), key.size());
Slice key_after_flush = key_after_flush_buf;
Slice value_after_flush = value;
// If user defined timestamps will be stripped from user key after flush,
// the in memory version of the key act logically the same as one with a
// minimum timestamp. We update the timestamp here so file boundary and
// output validator, block builder all see the effect of the stripping.
if (logical_strip_timestamp) {
key_after_flush_buf.clear();
ReplaceInternalKeyWithMinTimestamp(&key_after_flush_buf, key, ts_sz);
key_after_flush = key_after_flush_buf;
}
if (ikey.type == kTypeValuePreferredSeqno) {
auto [unpacked_value, unix_write_time] =
ParsePackedValueWithWriteTime(value);
@@ -235,7 +247,6 @@ Status BuildTable(
std::min(smallest_preferred_seqno, preferred_seqno);
} else {
// Cannot get a useful preferred seqno, convert it to a kTypeValue.
key_after_flush_buf.assign(key.data(), key.size());
UpdateInternalKey(&key_after_flush_buf, ikey.sequence, kTypeValue);
ikey = ParsedInternalKey(ikey.user_key, ikey.sequence, kTypeValue);
key_after_flush = key_after_flush_buf;
@@ -252,10 +263,6 @@ Status BuildTable(
}
builder->Add(key_after_flush, value_after_flush);
if (flush_stats) {
flush_stats->num_output_records++;
}
s = meta->UpdateBoundaries(key_after_flush, value_after_flush,
ikey.sequence, ikey.type);
if (!s.ok()) {
@@ -284,12 +291,13 @@ Status BuildTable(
Slice last_tombstone_start_user_key{};
for (range_del_it->SeekToFirst(); range_del_it->Valid();
range_del_it->Next()) {
auto tombstone = range_del_it->Tombstone();
// When user timestamp should not be persisted, we logically strip a
// range tombstone's start and end key's timestamp (replace it with min
// timestamp) before passing them along to table builder and to update
// file boundaries.
auto tombstone = range_del_it->Tombstone(logical_strip_timestamp);
std::pair<InternalKey, Slice> kv = tombstone.Serialize();
builder->Add(kv.first.Encode(), kv.second);
if (flush_stats) {
flush_stats->num_output_records++;
}
InternalKey tombstone_end = tombstone.SerializeEndKey();
meta->UpdateBoundariesForRange(kv.first, tombstone_end, tombstone.seq_,
tboptions.internal_comparator);
@@ -311,9 +319,9 @@ Status BuildTable(
TEST_SYNC_POINT("BuildTable:BeforeFinishBuildTable");
const bool empty = builder->IsEmpty();
if (flush_stats) {
if (num_input_entries != nullptr) {
assert(c_iter.HasNumInputEntryScanned());
flush_stats->num_input_records =
*num_input_entries =
c_iter.NumInputEntryScanned() + num_unfragmented_tombstones;
}
if (!s.ok() || empty) {
@@ -340,12 +348,6 @@ Status BuildTable(
}
if (s.ok() && !empty) {
if (flush_stats) {
flush_stats->bytes_written_pre_comp = builder->PreCompressionSize();
// Add worker CPU micros here. Caller needs to add CPU micros from
// calling thread.
flush_stats->cpu_micros += builder->GetWorkerCPUMicros();
}
uint64_t file_size = builder->FileSize();
meta->fd.file_size = file_size;
meta->tail_size = builder->GetTailSize();
@@ -436,7 +438,8 @@ Status BuildTable(
// the goal is to cache it here for further user reads.
std::unique_ptr<InternalIterator> it(table_cache->NewIterator(
tboptions.read_options, file_options, tboptions.internal_comparator,
*meta, nullptr /* range_del_agg */, mutable_cf_options, nullptr,
*meta, nullptr /* range_del_agg */,
mutable_cf_options.prefix_extractor, nullptr,
(internal_stats == nullptr) ? nullptr
: internal_stats->GetFileReadHist(0),
TableReaderCaller::kFlush, /*arena=*/nullptr,
@@ -444,7 +447,8 @@ Status BuildTable(
MaxFileSizeForL0MetaPin(mutable_cf_options),
/*smallest_compaction_key=*/nullptr,
/*largest_compaction_key*/ nullptr,
/*allow_unprepared_value*/ false));
/*allow_unprepared_value*/ false,
mutable_cf_options.block_protection_bytes_per_key));
s = it->status();
if (s.ok() && paranoid_file_checks) {
OutputValidator file_validator(tboptions.internal_comparator,
@@ -476,18 +480,9 @@ Status BuildTable(
Status prepare =
WritableFileWriter::PrepareIOOptions(tboptions.write_options, opts);
if (prepare.ok()) {
// FIXME: track file for "slow" deletion, e.g. into the
// VersionSet::obsolete_files_ pipeline
Status ignored = fs->DeleteFile(fname, opts, dbg);
ignored.PermitUncheckedError();
}
// Ensure we don't leak table cache entries when throwing away output
// files. (The usual logic in PurgeObsoleteFiles is not applicable because
// this function deletes the obsolete file itself, while they should
// probably go into the VersionSet::obsolete_files_ pipeline.)
TableCache::ReleaseObsolete(table_cache->get_cache().get(),
meta->fd.GetNumber(), nullptr /*handle*/,
mutable_cf_options.uncache_aggressiveness);
}
assert(blob_file_additions || blob_file_paths.empty());
+5 -6
View File
@@ -10,7 +10,6 @@
#include <utility>
#include <vector>
#include "db/internal_stats.h"
#include "db/range_tombstone_fragmenter.h"
#include "db/seqno_to_time_mapping.h"
#include "db/table_properties_collector.h"
@@ -35,6 +34,7 @@ class SnapshotChecker;
class TableCache;
class TableBuilder;
class WritableFileWriter;
class InternalStats;
class BlobFileCompletionCallback;
// Convenience function for NewTableBuilder on the embedded table_factory.
@@ -49,7 +49,6 @@ TableBuilder* NewTableBuilder(const TableBuilderOptions& tboptions,
//
// @param column_family_name Name of the column family that is also identified
// by column_family_id, or empty string if unknown.
// @param flush_stats treat flush as level 0 compaction in internal stats
Status BuildTable(
const std::string& dbname, VersionSet* versions,
const ImmutableDBOptions& db_options, const TableBuilderOptions& tboptions,
@@ -58,7 +57,7 @@ Status BuildTable(
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
range_del_iters,
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
std::vector<SequenceNumber> snapshots, SequenceNumber earliest_snapshot,
std::vector<SequenceNumber> snapshots,
SequenceNumber earliest_write_conflict_snapshot,
SequenceNumber job_snapshot, SnapshotChecker* snapshot_checker,
bool paranoid_file_checks, InternalStats* internal_stats,
@@ -70,8 +69,8 @@ Status BuildTable(
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET,
const std::string* full_history_ts_low = nullptr,
BlobFileCompletionCallback* blob_callback = nullptr,
Version* version = nullptr, uint64_t* memtable_payload_bytes = nullptr,
uint64_t* memtable_garbage_bytes = nullptr,
InternalStats::CompactionStats* flush_stats = nullptr);
Version* version = nullptr, uint64_t* num_input_entries = nullptr,
uint64_t* memtable_payload_bytes = nullptr,
uint64_t* memtable_garbage_bytes = nullptr);
} // namespace ROCKSDB_NAMESPACE
+148 -1779
View File
File diff suppressed because it is too large. Load diff
+25 -976
View File
File diff suppressed because it is too large. Load diff
+10 -35
View File
@@ -9,14 +9,17 @@
namespace ROCKSDB_NAMESPACE {
// EXPERIMENTAL
class CoalescingIterator : public Iterator {
public:
CoalescingIterator(
const ReadOptions& read_options, const Comparator* comparator,
std::vector<std::pair<ColumnFamilyHandle*, std::unique_ptr<Iterator>>>&&
cfh_iter_pairs)
: impl_(read_options, comparator, std::move(cfh_iter_pairs),
ResetFunc(this), PopulateFunc(this)) {}
CoalescingIterator(const Comparator* comparator,
const std::vector<ColumnFamilyHandle*>& column_families,
const std::vector<Iterator*>& child_iterators)
: impl_(
comparator, column_families, child_iterators, [this]() { Reset(); },
[this](const autovector<MultiCfIteratorInfo>& items) {
Coalesce(items);
}) {}
~CoalescingIterator() override {}
// No copy allowed
@@ -47,36 +50,8 @@ class CoalescingIterator : public Iterator {
wide_columns_.clear();
}
bool PrepareValue() override { return impl_.PrepareValue(); }
private:
class ResetFunc {
public:
explicit ResetFunc(CoalescingIterator* iter) : iter_(iter) {}
void operator()() const {
assert(iter_);
iter_->Reset();
}
private:
CoalescingIterator* iter_;
};
class PopulateFunc {
public:
explicit PopulateFunc(CoalescingIterator* iter) : iter_(iter) {}
void operator()(const autovector<MultiCfIteratorInfo>& items) const {
assert(iter_);
iter_->Coalesce(items);
}
private:
CoalescingIterator* iter_;
};
MultiCfIteratorImpl<ResetFunc, PopulateFunc> impl_;
MultiCfIteratorImpl impl_;
Slice value_;
WideColumns wide_columns_;
+82 -169
View File
@@ -53,7 +53,7 @@ ColumnFamilyHandleImpl::ColumnFamilyHandleImpl(
ColumnFamilyHandleImpl::~ColumnFamilyHandleImpl() {
if (cfd_ != nullptr) {
for (auto& listener : cfd_->ioptions().listeners) {
for (auto& listener : cfd_->ioptions()->listeners) {
listener->OnColumnFamilyHandleDeletionStarted(this);
}
// Job id == 0 means that this is not our background process, but rather
@@ -110,48 +110,23 @@ void GetInternalTblPropCollFactory(
}
}
Status CheckCompressionSupportedWithManager(
CompressionType type, UnownedPtr<CompressionManager> mgr) {
if (mgr) {
if (!mgr->SupportsCompressionType(type)) {
return Status::NotSupported("Compression type " +
CompressionTypeToString(type) +
" is not recognized/supported by this "
"version of CompressionManager " +
mgr->GetId());
}
} else {
if (!CompressionTypeSupported(type)) {
if (type <= kLastBuiltinCompression) {
return Status::InvalidArgument("Compression type " +
CompressionTypeToString(type) +
" is not linked with the binary.");
} else {
return Status::NotSupported(
"Compression type " + CompressionTypeToString(type) +
" is not recognized/supported by built-in CompressionManager.");
}
}
}
return Status::OK();
}
Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options) {
if (!cf_options.compression_per_level.empty()) {
for (size_t level = 0; level < cf_options.compression_per_level.size();
++level) {
Status s = CheckCompressionSupportedWithManager(
cf_options.compression_per_level[level],
cf_options.compression_manager.get());
if (!s.ok()) {
return s;
if (!CompressionTypeSupported(cf_options.compression_per_level[level])) {
return Status::InvalidArgument(
"Compression type " +
CompressionTypeToString(cf_options.compression_per_level[level]) +
" is not linked with the binary.");
}
}
} else {
Status s = CheckCompressionSupportedWithManager(
cf_options.compression, cf_options.compression_manager.get());
if (!s.ok()) {
return s;
if (!CompressionTypeSupported(cf_options.compression)) {
return Status::InvalidArgument(
"Compression type " +
CompressionTypeToString(cf_options.compression) +
" is not linked with the binary.");
}
}
if (cf_options.compression_opts.zstd_max_train_bytes > 0) {
@@ -193,8 +168,7 @@ Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options) {
}
if (!cf_options.memtable_factory->IsInsertConcurrentlySupported()) {
return Status::InvalidArgument(
"Memtable doesn't allow concurrent writes "
"(allow_concurrent_memtable_write)");
"Memtable doesn't concurrent writes (allow_concurrent_memtable_write)");
}
return Status::OK();
}
@@ -225,9 +199,8 @@ const uint64_t kDefaultTtl = 0xfffffffffffffffe;
const uint64_t kDefaultPeriodicCompSecs = 0xfffffffffffffffe;
} // anonymous namespace
ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
bool read_only,
const ColumnFamilyOptions& src) {
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
const ColumnFamilyOptions& src) {
ColumnFamilyOptions result = src;
size_t clamp_max = std::conditional<
sizeof(size_t) == 4, std::integral_constant<size_t, 0xffffffff>,
@@ -266,10 +239,6 @@ ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
result.min_write_buffer_number_to_merge = 1;
}
if (result.disallow_memtable_writes) {
// A simple memtable that enforces MarkReadOnly (unlike skip list)
result.memtable_factory = std::make_shared<VectorRepFactory>();
}
if (result.num_levels < 1) {
result.num_levels = 1;
@@ -280,18 +249,22 @@ ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
}
if (result.compaction_style == kCompactionStyleUniversal &&
(db_options.allow_ingest_behind || result.cf_allow_ingest_behind) &&
result.num_levels < 3) {
db_options.allow_ingest_behind && result.num_levels < 3) {
result.num_levels = 3;
}
if (result.max_write_buffer_number < 2) {
result.max_write_buffer_number = 2;
}
// fall back max_write_buffer_number_to_maintain if
// max_write_buffer_size_to_maintain is not set
if (result.max_write_buffer_size_to_maintain < 0) {
result.max_write_buffer_size_to_maintain =
result.max_write_buffer_number *
static_cast<int64_t>(result.write_buffer_size);
} else if (result.max_write_buffer_size_to_maintain == 0 &&
result.max_write_buffer_number_to_maintain < 0) {
result.max_write_buffer_number_to_maintain = result.max_write_buffer_number;
}
// bloom filter size shouldn't exceed 1/4 of memtable size.
if (result.memtable_prefix_bloom_size_ratio > 0.25) {
@@ -462,33 +435,6 @@ ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
result.periodic_compaction_seconds = 0;
}
if (read_only && (result.preserve_internal_time_seconds > 0 ||
result.preclude_last_level_data_seconds > 0)) {
// With no writes coming in, we don't need periodic SeqnoToTime entries.
// Existing SST files may or may not have that info associated with them.
ROCKS_LOG_WARN(
db_options.info_log.get(),
"preserve_internal_time_seconds and preclude_last_level_data_seconds "
"are ignored in read-only DB");
result.preserve_internal_time_seconds = 0;
result.preclude_last_level_data_seconds = 0;
}
if (read_only) {
if (result.memtable_op_scan_flush_trigger) {
ROCKS_LOG_WARN(db_options.info_log.get(),
"option memtable_op_scan_flush_trigger is sanitized to "
"0(disabled) for read only DB.");
result.memtable_op_scan_flush_trigger = 0;
}
if (result.memtable_avg_op_scan_flush_trigger) {
ROCKS_LOG_WARN(
db_options.info_log.get(),
"option memtable_avg_op_scan_flush_trigger is sanitized to "
"0(disabled) for read only DB.");
result.memtable_avg_op_scan_flush_trigger = 0;
}
}
return result;
}
@@ -520,7 +466,7 @@ void SuperVersion::Cleanup() {
// decrement reference to the immutable MemtableList
// this SV object was pointing to.
imm->Unref(&to_delete);
ReadOnlyMemTable* m = mem->Unref();
MemTable* m = mem->Unref();
if (m != nullptr) {
auto* memory_usage = current->cfd()->imm()->current_memory_usage();
assert(*memory_usage >= m->ApproximateMemoryUsage());
@@ -546,17 +492,6 @@ void SuperVersion::Init(
imm->Ref();
current->Ref();
refs.store(1, std::memory_order_relaxed);
// There should be at least one mapping entry iff time tracking is enabled.
#ifndef NDEBUG
MinAndMaxPreserveSeconds preserve_info{mutable_cf_options};
if (preserve_info.IsEnabled()) {
assert(seqno_to_time_mapping);
assert(!seqno_to_time_mapping->Empty());
} else {
assert(seqno_to_time_mapping == nullptr);
}
#endif // NDEBUG
}
namespace {
@@ -595,7 +530,7 @@ ColumnFamilyData::ColumnFamilyData(
const FileOptions* file_options, ColumnFamilySet* column_family_set,
BlockCacheTracer* const block_cache_tracer,
const std::shared_ptr<IOTracer>& io_tracer, const std::string& db_id,
const std::string& db_session_id, bool read_only)
const std::string& db_session_id)
: id_(id),
name_(name),
dummy_versions_(_dummy_versions),
@@ -603,9 +538,8 @@ ColumnFamilyData::ColumnFamilyData(
refs_(0),
initialized_(false),
dropped_(false),
flush_skip_reschedule_(false),
internal_comparator_(cf_options.comparator),
initial_cf_options_(SanitizeCfOptions(db_options, read_only, cf_options)),
initial_cf_options_(SanitizeOptions(db_options, cf_options)),
ioptions_(db_options, initial_cf_options_),
mutable_cf_options_(initial_cf_options_),
is_delete_range_supported_(
@@ -613,6 +547,7 @@ ColumnFamilyData::ColumnFamilyData(
write_buffer_manager_(write_buffer_manager),
mem_(nullptr),
imm_(ioptions_.min_write_buffer_number_to_merge,
ioptions_.max_write_buffer_number_to_maintain,
ioptions_.max_write_buffer_size_to_maintain),
super_version_(nullptr),
super_version_number_(0),
@@ -657,10 +592,10 @@ ColumnFamilyData::ColumnFamilyData(
block_cache_tracer, io_tracer,
db_session_id));
blob_file_cache_.reset(
new BlobFileCache(_table_cache, &ioptions(), soptions(), id_,
new BlobFileCache(_table_cache, ioptions(), soptions(), id_,
internal_stats_->GetBlobFileReadHist(), io_tracer));
blob_source_.reset(new BlobSource(ioptions_, mutable_cf_options_, db_id,
db_session_id, blob_file_cache_.get()));
blob_source_.reset(new BlobSource(ioptions(), db_id, db_session_id,
blob_file_cache_.get()));
if (ioptions_.compaction_style == kCompactionStyleLevel) {
compaction_picker_.reset(
@@ -757,9 +692,9 @@ ColumnFamilyData::~ColumnFamilyData() {
if (mem_ != nullptr) {
delete mem_->Unref();
}
autovector<ReadOnlyMemTable*> to_delete;
autovector<MemTable*> to_delete;
imm_.current()->Unref(&to_delete);
for (auto* m : to_delete) {
for (MemTable* m : to_delete) {
delete m;
}
@@ -965,11 +900,7 @@ uint64_t GetPendingCompactionBytesForCompactionSpeedup(
return slowdown_threshold;
}
// Prevent a small CF from triggering parallel compactions for other CFs.
// Require compaction debt to be more than a full L0 to Lbase compaction.
const uint64_t kMinDebtSize = 2 * mutable_cf_options.max_bytes_for_level_base;
uint64_t size_threshold =
std::max(bottommost_files_size / kBottommostSizeDivisor, kMinDebtSize);
uint64_t size_threshold = bottommost_files_size / kBottommostSizeDivisor;
return std::min(size_threshold, slowdown_threshold);
}
@@ -1032,7 +963,7 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
auto write_stall_condition_and_cause = GetWriteStallConditionAndCause(
imm()->NumNotFlushed(), vstorage->l0_delay_trigger_count(),
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options,
ioptions());
*ioptions());
write_stall_condition = write_stall_condition_and_cause.first;
auto write_stall_cause = write_stall_condition_and_cause.second;
@@ -1224,13 +1155,12 @@ MemTable* ColumnFamilyData::ConstructNewMemtable(
write_buffer_manager_, earliest_seq, id_);
}
void ColumnFamilyData::CreateNewMemtable(SequenceNumber earliest_seq) {
void ColumnFamilyData::CreateNewMemtable(
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq) {
if (mem_ != nullptr) {
delete mem_->Unref();
}
// NOTE: db mutex must be locked for SetMemtable, so safe for
// GetLatestMutableCFOptions
SetMemtable(ConstructNewMemtable(GetLatestMutableCFOptions(), earliest_seq));
SetMemtable(ConstructNewMemtable(mutable_cf_options, earliest_seq));
mem_->Ref();
}
@@ -1241,14 +1171,10 @@ bool ColumnFamilyData::NeedsCompaction() const {
Compaction* ColumnFamilyData::PickCompaction(
const MutableCFOptions& mutable_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& existing_snapshots,
const SnapshotChecker* snapshot_checker, LogBuffer* log_buffer,
bool require_max_output_level) {
const MutableDBOptions& mutable_db_options, LogBuffer* log_buffer) {
auto* result = compaction_picker_->PickCompaction(
GetName(), mutable_options, mutable_db_options, existing_snapshots,
snapshot_checker, current_->storage_info(), log_buffer,
require_max_output_level);
GetName(), mutable_options, mutable_db_options, current_->storage_info(),
log_buffer);
if (result != nullptr) {
result->FinalizeInputInfo(current_);
}
@@ -1274,10 +1200,8 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
read_opts.total_order_seek = true;
MergeIteratorBuilder merge_iter_builder(&internal_comparator_, &arena);
merge_iter_builder.AddIterator(super_version->mem->NewIterator(
read_opts, /*seqno_to_time_mapping=*/nullptr, &arena,
/*prefix_extractor=*/nullptr, /*for_flush=*/false));
read_opts, /*seqno_to_time_mapping=*/nullptr, &arena));
super_version->imm->AddIterators(read_opts, /*seqno_to_time_mapping=*/nullptr,
/*prefix_extractor=*/nullptr,
&merge_iter_builder,
false /* add_range_tombstone_iter */);
ScopedArenaPtr<InternalIterator> memtable_iter(merge_iter_builder.Finish());
@@ -1332,7 +1256,7 @@ Compaction* ColumnFamilyData::CompactRange(
const InternalKey* begin, const InternalKey* end,
InternalKey** compaction_end, bool* conflict,
uint64_t max_file_num_to_ignore, const std::string& trim_ts) {
auto* result = compaction_picker_->PickCompactionForCompactRange(
auto* result = compaction_picker_->CompactRange(
GetName(), mutable_cf_options, mutable_db_options,
current_->storage_info(), input_level, output_level,
compact_range_options, begin, end, compaction_end, conflict,
@@ -1405,17 +1329,20 @@ bool ColumnFamilyData::ReturnThreadLocalSuperVersion(SuperVersion* sv) {
return false;
}
void ColumnFamilyData::InstallSuperVersion(
SuperVersionContext* sv_context, InstrumentedMutex* db_mutex,
std::optional<std::shared_ptr<SeqnoToTimeMapping>>
new_seqno_to_time_mapping) {
void ColumnFamilyData::InstallSuperVersion(SuperVersionContext* sv_context,
InstrumentedMutex* db_mutex) {
db_mutex->AssertHeld();
return InstallSuperVersion(sv_context, mutable_cf_options_);
}
void ColumnFamilyData::InstallSuperVersion(
SuperVersionContext* sv_context,
const MutableCFOptions& mutable_cf_options) {
SuperVersion* new_superversion = sv_context->new_superversion.release();
new_superversion->mutable_cf_options = GetLatestMutableCFOptions();
new_superversion->mutable_cf_options = mutable_cf_options;
new_superversion->Init(this, mem_, imm_.current(), current_,
new_seqno_to_time_mapping.has_value()
? std::move(new_seqno_to_time_mapping.value())
sv_context->new_seqno_to_time_mapping
? std::move(sv_context->new_seqno_to_time_mapping)
: super_version_
? super_version_->ShareSeqnoToTimeMapping()
: nullptr);
@@ -1428,7 +1355,7 @@ void ColumnFamilyData::InstallSuperVersion(
// currently RecalculateWriteStallConditions() treats it as further slowing
// down is needed.
super_version_->write_stall_condition =
RecalculateWriteStallConditions(new_superversion->mutable_cf_options);
RecalculateWriteStallConditions(mutable_cf_options);
} else {
super_version_->write_stall_condition =
old_superversion->write_stall_condition;
@@ -1441,15 +1368,14 @@ void ColumnFamilyData::InstallSuperVersion(
ResetThreadLocalSuperVersions();
if (old_superversion->mutable_cf_options.write_buffer_size !=
new_superversion->mutable_cf_options.write_buffer_size) {
mem_->UpdateWriteBufferSize(
new_superversion->mutable_cf_options.write_buffer_size);
mutable_cf_options.write_buffer_size) {
mem_->UpdateWriteBufferSize(mutable_cf_options.write_buffer_size);
}
if (old_superversion->write_stall_condition !=
new_superversion->write_stall_condition) {
sv_context->PushWriteStallNotification(
old_superversion->write_stall_condition,
new_superversion->write_stall_condition, GetName(), &ioptions());
new_superversion->write_stall_condition, GetName(), ioptions());
}
if (old_superversion->Unref()) {
old_superversion->Cleanup();
@@ -1602,20 +1528,6 @@ Status ColumnFamilyData::ValidateOptions(
}
}
}
if (cf_options.compaction_style == kCompactionStyleUniversal) {
int max_read_amp = cf_options.compaction_options_universal.max_read_amp;
if (max_read_amp < -1) {
return Status::NotSupported(
"CompactionOptionsUniversal::max_read_amp should be at least -1.");
} else if (0 < max_read_amp &&
max_read_amp < cf_options.level0_file_num_compaction_trigger) {
return Status::NotSupported(
"CompactionOptionsUniversal::max_read_amp limits the number of sorted"
" runs but is smaller than the compaction trigger "
"level0_file_num_compaction_trigger.");
}
}
return s;
}
@@ -1626,16 +1538,9 @@ Status ColumnFamilyData::SetOptions(
BuildColumnFamilyOptions(initial_cf_options_, mutable_cf_options_);
ConfigOptions config_opts;
config_opts.mutable_options_only = true;
#ifndef NDEBUG
if (TEST_allowSetOptionsImmutableInMutable) {
config_opts.mutable_options_only = false;
}
#endif
Status s = GetColumnFamilyOptionsFromMap(config_opts, cf_opts, options_map,
&cf_opts);
if (s.ok()) {
// FIXME: we should call SanitizeOptions() too or consolidate it with
// ValidateOptions().
s = ValidateOptions(db_opts, cf_opts);
}
if (s.ok()) {
@@ -1645,6 +1550,28 @@ Status ColumnFamilyData::SetOptions(
return s;
}
// REQUIRES: DB mutex held
Env::WriteLifeTimeHint ColumnFamilyData::CalculateSSTWriteHint(int level) {
if (initial_cf_options_.compaction_style != kCompactionStyleLevel) {
return Env::WLTH_NOT_SET;
}
if (level == 0) {
return Env::WLTH_MEDIUM;
}
int base_level = current_->storage_info()->base_level();
// L1: medium, L2: long, ...
if (level - base_level >= 2) {
return Env::WLTH_EXTREME;
} else if (level < base_level) {
// There is no restriction which prevents level passed in to be smaller
// than base_level.
return Env::WLTH_MEDIUM;
}
return static_cast<Env::WriteLifeTimeHint>(
level - base_level + static_cast<int>(Env::WLTH_MEDIUM));
}
Status ColumnFamilyData::AddDirectories(
std::map<std::string, std::shared_ptr<FSDirectory>>* created_dirs) {
Status s;
@@ -1680,19 +1607,6 @@ FSDirectory* ColumnFamilyData::GetDataDir(size_t path_id) const {
return data_dirs_[path_id].get();
}
void ColumnFamilyData::SetFlushSkipReschedule() {
const Comparator* ucmp = user_comparator();
const size_t ts_sz = ucmp->timestamp_size();
if (ts_sz == 0 || ioptions_.persist_user_defined_timestamps) {
return;
}
flush_skip_reschedule_.store(true);
}
bool ColumnFamilyData::GetAndClearFlushSkipReschedule() {
return flush_skip_reschedule_.exchange(false);
}
bool ColumnFamilyData::ShouldPostponeFlushToRetainUDT(
uint64_t max_memtable_id) {
const Comparator* ucmp = user_comparator();
@@ -1710,9 +1624,6 @@ bool ColumnFamilyData::ShouldPostponeFlushToRetainUDT(
}
for (const Slice& table_newest_udt :
imm()->GetTablesNewestUDT(max_memtable_id)) {
if (table_newest_udt.empty()) {
continue;
}
assert(table_newest_udt.size() == full_history_ts_low.size());
// Checking the newest UDT contained in MemTable with ascending ID up to
// `max_memtable_id`. Return immediately on finding the first MemTable that
@@ -1746,8 +1657,7 @@ ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
dummy_cfd_(new ColumnFamilyData(
ColumnFamilyData::kDummyColumnFamilyDataId, "", nullptr, nullptr,
nullptr, ColumnFamilyOptions(), *db_options, &file_options_, nullptr,
block_cache_tracer, io_tracer, db_id, db_session_id,
/*read_only*/ true)),
block_cache_tracer, io_tracer, db_id, db_session_id)),
default_cfd_cache_(nullptr),
db_name_(dbname),
db_options_(db_options),
@@ -1819,12 +1729,12 @@ size_t ColumnFamilySet::NumberOfColumnFamilies() const {
// under a DB mutex AND write thread
ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
const std::string& name, uint32_t id, Version* dummy_versions,
const ColumnFamilyOptions& options, bool read_only) {
const ColumnFamilyOptions& options) {
assert(column_families_.find(name) == column_families_.end());
ColumnFamilyData* new_cfd = new ColumnFamilyData(
id, name, dummy_versions, table_cache_, write_buffer_manager_, options,
*db_options_, &file_options_, this, block_cache_tracer_, io_tracer_,
db_id_, db_session_id_, read_only);
db_id_, db_session_id_);
column_families_.insert({name, id});
column_family_data_.insert({id, new_cfd});
auto ucmp = new_cfd->user_comparator();
@@ -1912,7 +1822,10 @@ const ImmutableOptions& GetImmutableOptions(ColumnFamilyHandle* column_family) {
const ColumnFamilyData* const cfd = handle->cfd();
assert(cfd);
return cfd->ioptions();
const ImmutableOptions* ioptions = cfd->ioptions();
assert(ioptions);
return *ioptions;
}
} // namespace ROCKSDB_NAMESPACE
+45 -90
View File
@@ -16,7 +16,6 @@
#include "cache/cache_reservation_manager.h"
#include "db/memtable_list.h"
#include "db/snapshot_checker.h"
#include "db/table_cache.h"
#include "db/table_properties_collector.h"
#include "db/write_batch_internal.h"
@@ -207,10 +206,9 @@ struct SuperVersion {
// Accessing members of this class is not thread-safe and requires external
// synchronization (ie db mutex held or on write thread).
ColumnFamilyData* cfd;
ReadOnlyMemTable* mem;
MemTable* mem;
MemTableListVersion* imm;
Version* current;
// TODO: do we really need this in addition to what's in current Version?
MutableCFOptions mutable_cf_options;
// Version number of the current SuperVersion
uint64_t version_number;
@@ -222,8 +220,7 @@ struct SuperVersion {
// enable UDT feature, this is an empty string.
std::string full_history_ts_low;
// An immutable snapshot of the DB's seqno to time mapping, usually shared
// between SuperVersions.
// A shared copy of the DB's seqno to time mapping.
std::shared_ptr<const SeqnoToTimeMapping> seqno_to_time_mapping{nullptr};
// should be called outside the mutex
@@ -271,7 +268,7 @@ struct SuperVersion {
// We need to_delete because during Cleanup(), imm->Unref() returns
// all memtables that we need to free through this vector. We then
// delete all those memtables outside of mutex, during destruction
autovector<ReadOnlyMemTable*> to_delete;
autovector<MemTable*> to_delete;
};
Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
@@ -281,9 +278,8 @@ Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options);
Status CheckCFPathsSupported(const DBOptions& db_options,
const ColumnFamilyOptions& cf_options);
ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
bool read_only,
const ColumnFamilyOptions& src);
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
const ColumnFamilyOptions& src);
// Wrap user defined table properties collector factories `from cf_options`
// into internal ones in internal_tbl_prop_coll_factories. Add a system internal
// one too.
@@ -333,10 +329,6 @@ class ColumnFamilyData {
void SetDropped();
bool IsDropped() const { return dropped_.load(std::memory_order_relaxed); }
void SetFlushSkipReschedule();
bool GetAndClearFlushSkipReschedule();
// thread-safe
int NumberLevels() const { return ioptions_.num_levels; }
@@ -345,17 +337,17 @@ class ColumnFamilyData {
// thread-safe
const FileOptions* soptions() const;
const ImmutableOptions& ioptions() const { return ioptions_; }
const ImmutableOptions* ioptions() const { return &ioptions_; }
// REQUIRES: DB mutex held
// This returns the MutableCFOptions used by current SuperVersion
// You should use this API to reference MutableCFOptions most of the time.
const MutableCFOptions& GetCurrentMutableCFOptions() const {
return super_version_->mutable_cf_options;
const MutableCFOptions* GetCurrentMutableCFOptions() const {
return &(super_version_->mutable_cf_options);
}
// REQUIRES: DB mutex held
// This returns the latest MutableCFOptions, which may be not in effect yet.
const MutableCFOptions& GetLatestMutableCFOptions() const {
return mutable_cf_options_;
const MutableCFOptions* GetLatestMutableCFOptions() const {
return &mutable_cf_options_;
}
// REQUIRES: DB mutex held
@@ -382,50 +374,38 @@ class ColumnFamilyData {
return mem()->GetFirstSequenceNumber() == 0 && imm()->NumNotFlushed() == 0;
}
Version* current() { return current_; }
Version* dummy_versions() { return dummy_versions_; }
Version* current() { return current_; } // REQUIRE: DB mutex held
void SetCurrent(Version* _current); // REQUIRE: DB mutex held
uint64_t GetNumLiveVersions() const; // REQUIRE: DB mutex held
uint64_t GetTotalSstFilesSize() const; // REQUIRE: DB mutex held
uint64_t GetLiveSstFilesSize() const; // REQUIRE: DB mutex held
uint64_t GetTotalBlobFileSize() const; // REQUIRE: DB mutex held
// REQUIRE: DB mutex held
void SetCurrent(Version* _current);
uint64_t GetNumLiveVersions() const; // REQUIRE: DB mutex held
uint64_t GetTotalSstFilesSize() const; // REQUIRE: DB mutex held
uint64_t GetLiveSstFilesSize() const; // REQUIRE: DB mutex held
uint64_t GetTotalBlobFileSize() const; // REQUIRE: DB mutex held
void SetMemtable(MemTable* new_mem) {
AssignMemtableID(new_mem);
uint64_t memtable_id = last_memtable_id_.fetch_add(1) + 1;
new_mem->SetID(memtable_id);
mem_ = new_mem;
if (ioptions_.disallow_memtable_writes) {
mem_->MarkImmutable();
}
}
void AssignMemtableID(ReadOnlyMemTable* new_imm) {
new_imm->SetID(++last_memtable_id_);
}
// calculate the oldest log needed for the durability of this column family
uint64_t OldestLogToKeep();
// See Memtable constructor for explanation of earliest_seq param.
// `mutable_cf_options` might need to be a saved copy if calling this without
// holding the DB mutex.
MemTable* ConstructNewMemtable(const MutableCFOptions& mutable_cf_options,
SequenceNumber earliest_seq);
void CreateNewMemtable(SequenceNumber earliest_seq);
void CreateNewMemtable(const MutableCFOptions& mutable_cf_options,
SequenceNumber earliest_seq);
TableCache* table_cache() const { return table_cache_.get(); }
BlobFileCache* blob_file_cache() const { return blob_file_cache_.get(); }
BlobSource* blob_source() const { return blob_source_.get(); }
// See documentation in compaction_picker.h
// REQUIRES: DB mutex held
bool NeedsCompaction() const;
// REQUIRES: DB mutex held
Compaction* PickCompaction(
const MutableCFOptions& mutable_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& existing_snapshots,
const SnapshotChecker* snapshot_checker, LogBuffer* log_buffer,
bool require_max_output_level = false);
Compaction* PickCompaction(const MutableCFOptions& mutable_options,
const MutableDBOptions& mutable_db_options,
LogBuffer* log_buffer);
// Check if the passed range overlap with any running compactions.
// REQUIRES: DB mutex held
@@ -489,14 +469,15 @@ class ColumnFamilyData {
uint64_t GetSuperVersionNumber() const {
return super_version_number_.load();
}
uint64_t GetSuperVersionNumberRelaxed() const {
return super_version_number_.load(std::memory_order_relaxed);
}
// Only intended for use by DBImpl::InstallSuperVersion() and variants
// will return a pointer to SuperVersion* if previous SuperVersion
// if its reference count is zero and needs deletion or nullptr if not
// As argument takes a pointer to allocated SuperVersion to enable
// the clients to allocate SuperVersion outside of mutex.
// IMPORTANT: Only call this from DBImpl::InstallSuperVersion()
void InstallSuperVersion(SuperVersionContext* sv_context,
InstrumentedMutex* db_mutex,
std::optional<std::shared_ptr<SeqnoToTimeMapping>>
new_seqno_to_time_mapping = {});
const MutableCFOptions& mutable_cf_options);
void InstallSuperVersion(SuperVersionContext* sv_context,
InstrumentedMutex* db_mutex);
void ResetThreadLocalSuperVersions();
@@ -526,6 +507,8 @@ class ColumnFamilyData {
return initial_cf_options_;
}
Env::WriteLifeTimeHint CalculateSSTWriteHint(int level);
// created_dirs remembers directory created, so that we don't need to call
// the same data creation operation again.
Status AddDirectories(
@@ -538,12 +521,6 @@ class ColumnFamilyData {
assert(!ts_low.empty());
const Comparator* ucmp = user_comparator();
assert(ucmp);
// Guard against resurrected full_history_ts_low persisted in MANIFEST
// from previous DB sessions. This could happen if UDT was enabled and then
// disabled.
if (ucmp->timestamp_size() == 0) {
return;
}
if (full_history_ts_low_.empty() ||
ucmp->CompareTimestamp(ts_low, full_history_ts_low_) > 0) {
full_history_ts_low_ = std::move(ts_low);
@@ -551,11 +528,6 @@ class ColumnFamilyData {
}
const std::string& GetFullHistoryTsLow() const {
const Comparator* ucmp = user_comparator();
assert(ucmp);
if (ucmp->timestamp_size() == 0) {
assert(full_history_ts_low_.empty());
}
return full_history_ts_low_;
}
@@ -596,25 +568,18 @@ class ColumnFamilyData {
// of its files (if missing)
void RecoverEpochNumbers();
int GetUnflushedMemTableCountForWriteStallCheck() const {
return (mem_->IsEmpty() ? 0 : 1) + imm_.NumNotFlushed();
}
// thread-safe, DB mutex not needed.
bool AllowIngestBehind() const {
return ioptions_.cf_allow_ingest_behind || ioptions_.allow_ingest_behind;
}
private:
friend class ColumnFamilySet;
ColumnFamilyData(
uint32_t id, const std::string& name, Version* dummy_versions,
Cache* table_cache, WriteBufferManager* write_buffer_manager,
const ColumnFamilyOptions& options, const ImmutableDBOptions& db_options,
const FileOptions* file_options, ColumnFamilySet* column_family_set,
BlockCacheTracer* const block_cache_tracer,
const std::shared_ptr<IOTracer>& io_tracer, const std::string& db_id,
const std::string& db_session_id, bool read_only);
ColumnFamilyData(uint32_t id, const std::string& name,
Version* dummy_versions, Cache* table_cache,
WriteBufferManager* write_buffer_manager,
const ColumnFamilyOptions& options,
const ImmutableDBOptions& db_options,
const FileOptions* file_options,
ColumnFamilySet* column_family_set,
BlockCacheTracer* const block_cache_tracer,
const std::shared_ptr<IOTracer>& io_tracer,
const std::string& db_id, const std::string& db_session_id);
std::vector<std::string> GetDbPaths() const;
@@ -627,15 +592,6 @@ class ColumnFamilyData {
std::atomic<bool> initialized_;
std::atomic<bool> dropped_; // true if client dropped it
// When user-defined timestamps in memtable only feature is enabled, this
// flag indicates a successfully requested flush that should
// skip being rescheduled and haven't undergone the rescheduling check yet.
// This flag is cleared when a check skips rescheduling a FlushRequest.
// With this flag, automatic flushes in regular cases can continue to
// retain UDTs by getting rescheduled as usual while manual flushes and
// error recovery flushes will proceed without getting rescheduled.
std::atomic<bool> flush_skip_reschedule_;
const InternalKeyComparator internal_comparator_;
InternalTblPropCollFactories internal_tbl_prop_coll_factories_;
@@ -698,7 +654,7 @@ class ColumnFamilyData {
bool allow_2pc_;
// Memtable id to track flush.
uint64_t last_memtable_id_;
std::atomic<uint64_t> last_memtable_id_;
// Directories corresponding to cf_paths.
std::vector<std::shared_ptr<FSDirectory>> data_dirs_;
@@ -776,8 +732,7 @@ class ColumnFamilySet {
ColumnFamilyData* CreateColumnFamily(const std::string& name, uint32_t id,
Version* dummy_version,
const ColumnFamilyOptions& options,
bool read_only);
const ColumnFamilyOptions& options);
const UnorderedMap<uint32_t, size_t>& GetRunningColumnFamiliesTimestampSize()
const {
+80 -353
View File
@@ -23,7 +23,6 @@
#include "rocksdb/db.h"
#include "rocksdb/env.h"
#include "rocksdb/iterator.h"
#include "rocksdb/listener.h"
#include "rocksdb/utilities/object_registry.h"
#include "test_util/sync_point.h"
#include "test_util/testharness.h"
@@ -35,13 +34,6 @@
#include "utilities/merge_operators.h"
namespace ROCKSDB_NAMESPACE {
namespace {
std::string EncodeAsUint64(uint64_t number) {
std::string result;
PutFixed64(&result, number);
return result;
}
} // namespace
static const int kValueSize = 1000;
@@ -72,6 +64,7 @@ class ColumnFamilyTestBase : public testing::Test {
env_->skip_fsync_ = true;
dbname_ = test::PerThreadDBPath("column_family_test");
db_options_.create_if_missing = true;
db_options_.fail_if_options_file_error = true;
db_options_.env = env_;
}
@@ -270,8 +263,7 @@ class ColumnFamilyTestBase : public testing::Test {
// them.
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
ConfigOptions(), desc.options,
SanitizeCfOptions(dbfull()->immutable_db_options(),
/*read_only*/ false, current_cf_opt)));
SanitizeOptions(dbfull()->immutable_db_options(), current_cf_opt)));
cfi++;
}
}
@@ -2175,7 +2167,7 @@ TEST_P(ColumnFamilyTest, FlushStaleColumnFamilies) {
ASSERT_TRUE(has_cf2_sst);
ASSERT_OK(Flush(0));
ASSERT_EQ(0, dbfull()->TEST_wals_total_size());
ASSERT_EQ(0, dbfull()->TEST_total_log_size());
Close();
}
@@ -2232,7 +2224,7 @@ TEST_P(ColumnFamilyTest, CreateMissingColumnFamilies) {
ASSERT_EQ(my_fs->options_files_created.load(), 2);
}
TEST_P(ColumnFamilyTest, SanitizeCfOptions) {
TEST_P(ColumnFamilyTest, SanitizeOptions) {
DBOptions db_options;
for (int s = kCompactionStyleLevel; s <= kCompactionStyleUniversal; ++s) {
for (int l = 0; l <= 2; l++) {
@@ -2248,8 +2240,8 @@ TEST_P(ColumnFamilyTest, SanitizeCfOptions) {
original.write_buffer_size =
l * 4 * 1024 * 1024 + i * 1024 * 1024 + j * 1024 + k;
ColumnFamilyOptions result = SanitizeCfOptions(
ImmutableDBOptions(db_options), /*read_only*/ false, original);
ColumnFamilyOptions result =
SanitizeOptions(ImmutableDBOptions(db_options), original);
ASSERT_TRUE(result.level0_stop_writes_trigger >=
result.level0_slowdown_writes_trigger);
ASSERT_TRUE(result.level0_slowdown_writes_trigger >=
@@ -3012,25 +3004,19 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForCompactionDebt) {
ASSERT_OK(db_->Flush(FlushOptions()));
{
// 1MB debt is way bigger than bottommost data so definitely triggers
// speedup.
VersionStorageInfo* vstorage = cfd->current()->storage_info();
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */,
dbmu);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
// Eight bytes is way smaller than bottommost data so definitely does not
// trigger speedup.
vstorage->TEST_set_estimated_compaction_needed_bytes(8, dbmu);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
// 1MB is much larger than bottommost level size. However, since it's too
// small in terms of absolute size, it does not trigger parallel compaction
// in this case (see GetPendingCompactionBytesForCompactionSpeedup()).
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */,
dbmu);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
vstorage->TEST_set_estimated_compaction_needed_bytes(
2 * mutable_cf_options.max_bytes_for_level_base, dbmu);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
}
}
@@ -3073,20 +3059,12 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForMarkedFiles) {
WaitForCompaction();
AssertFilesPerLevel("0,1", 0 /* cf */);
// We should calculate the limit by obtaining the number of env background
// threads, because the current test case will share the same env
// with another case that may have already increased the number of
// background threads which is larger than kParallelismLimit
const auto limit = env_->GetBackgroundThreads(Env::Priority::LOW);
// Block the compaction thread pool so marked files accumulate in L0.
std::vector<std::shared_ptr<test::SleepingBackgroundTask>> sleeping_tasks;
for (int i = 0; i < limit; i++) {
sleeping_tasks.emplace_back(
std::make_shared<test::SleepingBackgroundTask>());
test::SleepingBackgroundTask sleeping_tasks[kParallelismLimit];
for (int i = 0; i < kParallelismLimit; i++) {
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
sleeping_tasks[i].get(), Env::Priority::LOW);
sleeping_tasks[i]->WaitUntilSleeping();
&sleeping_tasks[i], Env::Priority::LOW);
sleeping_tasks[i].WaitUntilSleeping();
}
// Zero marked upper-level files. No speedup.
@@ -3105,9 +3083,9 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForMarkedFiles) {
ASSERT_EQ(kParallelismLimit, dbfull()->TEST_BGCompactionsAllowed());
AssertFilesPerLevel("2,1", 0 /* cf */);
for (int i = 0; i < limit; i++) {
sleeping_tasks[i]->WakeUp();
sleeping_tasks[i]->WaitUntilDone();
for (int i = 0; i < kParallelismLimit; i++) {
sleeping_tasks[i].WakeUp();
sleeping_tasks[i].WaitUntilDone();
}
}
@@ -3192,8 +3170,6 @@ TEST_P(ColumnFamilyTest, IteratorCloseWALFile1) {
SpecialEnv env(Env::Default());
db_options_.env = &env;
db_options_.max_background_flushes = 1;
// When this option is removed, the test will need re-engineering
db_options_.background_close_inactive_wals = true;
column_family_options_.memtable_factory.reset(
test::NewSpecialSkipListFactory(2));
Open();
@@ -3246,8 +3222,6 @@ TEST_P(ColumnFamilyTest, IteratorCloseWALFile2) {
env.SetBackgroundThreads(2, Env::HIGH);
db_options_.env = &env;
db_options_.max_background_flushes = 1;
// When this option is removed, the test will need re-engineering
db_options_.background_close_inactive_wals = true;
column_family_options_.memtable_factory.reset(
test::NewSpecialSkipListFactory(2));
Open();
@@ -3305,8 +3279,6 @@ TEST_P(ColumnFamilyTest, ForwardIteratorCloseWALFile) {
env.SetBackgroundThreads(2, Env::HIGH);
db_options_.env = &env;
db_options_.max_background_flushes = 1;
// When this option is removed, the test will need re-engineering
db_options_.background_close_inactive_wals = true;
column_family_options_.memtable_factory.reset(
test::NewSpecialSkipListFactory(3));
column_family_options_.level0_file_num_compaction_trigger = 2;
@@ -3631,9 +3603,7 @@ TEST(ColumnFamilyTest, ValidateMemtableKVChecksumOption) {
}
// Tests the flushing behavior of a column family to retain user-defined
// timestamp when `persist_user_defined_timestamp` is false. The behavior of
// auto flush is it makes some effort to retain user-defined timestamps while
// the behavior of manual flush is that it skips retaining UDTs.
// timestamp when `persist_user_defined_timestamp` is false.
class ColumnFamilyRetainUDTTest : public ColumnFamilyTestBase {
public:
ColumnFamilyRetainUDTTest() : ColumnFamilyTestBase(kLatestFormatVersion) {}
@@ -3651,27 +3621,6 @@ class ColumnFamilyRetainUDTTest : public ColumnFamilyTestBase {
return db_->Put(WriteOptions(), handles_[cf], Slice(key), Slice(ts),
Slice(value));
}
std::string Get(int cf, const std::string& key, const std::string& read_ts) {
ReadOptions ropts;
Slice timestamp = read_ts;
ropts.timestamp = &timestamp;
std::string value;
Status s = db_->Get(ropts, handles_[cf], Slice(key), &value);
if (s.IsNotFound()) {
return "NOT_FOUND";
} else if (s.ok()) {
return value;
}
return "";
}
void CheckEffectiveCutoffTime(uint64_t expected_cutoff) {
std::string effective_full_history_ts_low;
EXPECT_OK(
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
EXPECT_EQ(EncodeAsUint64(expected_cutoff), effective_full_history_ts_low);
}
};
class TestTsComparator : public Comparator {
@@ -3715,9 +3664,7 @@ TEST_F(ColumnFamilyRetainUDTTest, SanityCheck) {
Close();
}
class AutoFlushRetainUDTTest : public ColumnFamilyRetainUDTTest {};
TEST_F(AutoFlushRetainUDTTest, FullHistoryTsLowNotSet) {
TEST_F(ColumnFamilyRetainUDTTest, FullHistoryTsLowNotSet) {
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
ASSERT_NE(nullptr, arg);
@@ -3727,22 +3674,28 @@ TEST_F(AutoFlushRetainUDTTest, FullHistoryTsLowNotSet) {
SyncPoint::GetInstance()->EnableProcessing();
Open();
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
// No `full_history_ts_low` explicitly set by user, auto flush is continued
std::string write_ts;
PutFixed64(&write_ts, 1);
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
// No `full_history_ts_low` explicitly set by user, flush is continued
// without checking if its UDTs expired.
ASSERT_OK(dbfull()->TEST_SwitchWAL());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(Flush(0));
// After flush, `full_history_ts_low` should be automatically advanced to
// the effective cutoff timestamp: write_ts + 1
CheckEffectiveCutoffTime(2);
std::string cutoff_ts;
PutFixed64(&cutoff_ts, 2);
std::string effective_full_history_ts_low;
ASSERT_OK(
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
Close();
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_F(AutoFlushRetainUDTTest, AllKeysExpired) {
TEST_F(ColumnFamilyRetainUDTTest, AllKeysExpired) {
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
ASSERT_NE(nullptr, arg);
@@ -3752,22 +3705,27 @@ TEST_F(AutoFlushRetainUDTTest, AllKeysExpired) {
SyncPoint::GetInstance()->EnableProcessing();
Open();
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(3)));
// All keys expired w.r.t the configured `full_history_ts_low`, auto flush
// continue without the need for a re-schedule.
ASSERT_OK(dbfull()->TEST_SwitchWAL());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
std::string write_ts;
PutFixed64(&write_ts, 1);
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
std::string cutoff_ts;
PutFixed64(&cutoff_ts, 3);
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
// All keys expired w.r.t the configured `full_history_ts_low`, flush continue
// without the need for a re-schedule.
ASSERT_OK(Flush(0));
// `full_history_ts_low` stays unchanged after flush.
CheckEffectiveCutoffTime(3);
std::string effective_full_history_ts_low;
ASSERT_OK(
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
Close();
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_F(AutoFlushRetainUDTTest, NotAllKeysExpiredFlushToAvoidWriteStall) {
TEST_F(ColumnFamilyRetainUDTTest, NotAllKeysExpiredFlushToAvoidWriteStall) {
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
ASSERT_NE(nullptr, arg);
@@ -3777,303 +3735,72 @@ TEST_F(AutoFlushRetainUDTTest, NotAllKeysExpiredFlushToAvoidWriteStall) {
SyncPoint::GetInstance()->EnableProcessing();
Open();
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(1)));
std::string cutoff_ts;
std::string write_ts;
PutFixed64(&write_ts, 1);
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
PutFixed64(&cutoff_ts, 1);
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
ASSERT_OK(db_->SetOptions(handles_[0], {{"max_write_buffer_number", "1"}}));
// Not all keys expired, but auto flush is continued without a re-schedule
// because of risk of write stall.
ASSERT_OK(dbfull()->TEST_SwitchWAL());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
// Not all keys expired, but flush is continued without a re-schedule because
// of risk of write stall.
ASSERT_OK(Flush(0));
// After flush, `full_history_ts_low` should be automatically advanced to
// the effective cutoff timestamp: write_ts + 1
CheckEffectiveCutoffTime(2);
std::string effective_full_history_ts_low;
ASSERT_OK(
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
cutoff_ts.clear();
PutFixed64(&cutoff_ts, 2);
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
Close();
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_F(AutoFlushRetainUDTTest, NotAllKeysExpiredFlushRescheduled) {
std::atomic<int> local_counter{1};
TEST_F(ColumnFamilyRetainUDTTest, NotAllKeysExpiredFlushRescheduled) {
std::string cutoff_ts;
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::AfterRetainUDTReschedule:cb", [&](void* /*arg*/) {
// Increasing full_history_ts_low so all keys expired after the initial
// FlushRequest is rescheduled
ASSERT_OK(
db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(3)));
cutoff_ts.clear();
PutFixed64(&cutoff_ts, 3);
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
});
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
ASSERT_NE(nullptr, arg);
auto reschedule_count = *static_cast<int*>(arg);
ASSERT_EQ(2, reschedule_count);
local_counter.fetch_add(1);
});
SyncPoint::GetInstance()->EnableProcessing();
Open();
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(1)));
std::string write_ts;
PutFixed64(&write_ts, 1);
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
PutFixed64(&cutoff_ts, 1);
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
// Not all keys expired, and there is no risk of write stall. Flush is
// rescheduled. The actual flush happens after `full_history_ts_low` is
// increased to mark all keys expired.
ASSERT_OK(dbfull()->TEST_SwitchWAL());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
// Make sure callback is not skipped.
ASSERT_EQ(2, local_counter);
ASSERT_OK(Flush(0));
CheckEffectiveCutoffTime(3);
std::string effective_full_history_ts_low;
ASSERT_OK(
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
// `full_history_ts_low` stays unchanged.
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
Close();
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
class ManualFlushSkipRetainUDTTest : public ColumnFamilyRetainUDTTest {
public:
// Write an entry with timestamp that is not expired w.r.t cutoff timestamp,
// and make sure automatic flush would be rescheduled to retain UDT.
void CheckAutomaticFlushRetainUDT(uint64_t write_ts) {
std::atomic<int> local_counter{1};
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::AfterRetainUDTReschedule:cb", [&](void* /*arg*/) {
// Increasing full_history_ts_low so all keys expired after the
// initial FlushRequest is rescheduled
ASSERT_OK(db_->IncreaseFullHistoryTsLow(
handles_[0], EncodeAsUint64(write_ts + 1)));
});
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
ASSERT_NE(nullptr, arg);
auto reschedule_count = *static_cast<int*>(arg);
ASSERT_EQ(2, reschedule_count);
local_counter.fetch_add(1);
});
SyncPoint::GetInstance()->EnableProcessing();
EXPECT_OK(Put(0, "foo", EncodeAsUint64(write_ts),
"foo" + std::to_string(write_ts)));
EXPECT_OK(dbfull()->TEST_SwitchWAL());
EXPECT_OK(dbfull()->TEST_WaitForFlushMemTable());
// Make sure callback is not skipped.
EXPECT_EQ(2, local_counter);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
};
TEST_F(ManualFlushSkipRetainUDTTest, ManualFlush) {
Open();
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
// Manual flush proceeds without trying to retain UDT.
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
ASSERT_OK(Flush(0));
CheckEffectiveCutoffTime(2);
CheckAutomaticFlushRetainUDT(3);
Close();
}
TEST_F(ManualFlushSkipRetainUDTTest, FlushRemovesStaleEntries) {
column_family_options_.max_write_buffer_number = 4;
Open();
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
ColumnFamilyHandle* cfh = db_->DefaultColumnFamily();
ColumnFamilyData* cfd =
static_cast_with_check<ColumnFamilyHandleImpl>(cfh)->cfd();
for (int version = 0; version < 100; version++) {
if (version == 50) {
ASSERT_OK(static_cast_with_check<DBImpl>(db_)->TEST_SwitchMemtable(cfd));
}
ASSERT_OK(
Put(0, "foo", EncodeAsUint64(version), "v" + std::to_string(version)));
}
ASSERT_OK(Flush(0));
TablePropertiesCollection tables_properties;
ASSERT_OK(db_->GetPropertiesOfAllTables(&tables_properties));
ASSERT_EQ(1, tables_properties.size());
std::shared_ptr<const TableProperties> table_properties =
tables_properties.begin()->second;
ASSERT_EQ(1, table_properties->num_entries);
ASSERT_EQ(0, table_properties->num_deletions);
ASSERT_EQ(0, table_properties->num_range_deletions);
CheckEffectiveCutoffTime(100);
CheckAutomaticFlushRetainUDT(101);
Close();
}
TEST_F(ManualFlushSkipRetainUDTTest, RangeDeletionFlushRemovesStaleEntries) {
column_family_options_.max_write_buffer_number = 4;
Open();
// TODO(yuzhangyu): a non 0 full history ts low is needed for this garbage
// collection to kick in. This doesn't work well for the very first flush of
// the column family. Not a big issue, but would be nice to improve this.
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(9)));
for (int i = 10; i < 100; i++) {
ASSERT_OK(Put(0, "foo" + std::to_string(i), EncodeAsUint64(i),
"val" + std::to_string(i)));
if (i % 2 == 1) {
ASSERT_OK(db_->DeleteRange(WriteOptions(), "foo" + std::to_string(i - 1),
"foo" + std::to_string(i), EncodeAsUint64(i)));
}
}
ASSERT_OK(Flush(0));
CheckEffectiveCutoffTime(100);
std::string read_ts = EncodeAsUint64(100);
std::string min_ts = EncodeAsUint64(0);
ReadOptions ropts;
Slice read_ts_slice = read_ts;
std::string value;
ropts.timestamp = &read_ts_slice;
{
Iterator* iter = db_->NewIterator(ropts);
iter->SeekToFirst();
int i = 11;
while (iter->Valid()) {
ASSERT_TRUE(iter->Valid());
ASSERT_EQ("foo" + std::to_string(i), iter->key());
ASSERT_EQ("val" + std::to_string(i), iter->value());
ASSERT_EQ(min_ts, iter->timestamp());
iter->Next();
i += 2;
}
ASSERT_OK(iter->status());
delete iter;
}
TablePropertiesCollection tables_properties;
ASSERT_OK(db_->GetPropertiesOfAllTables(&tables_properties));
ASSERT_EQ(1, tables_properties.size());
std::shared_ptr<const TableProperties> table_properties =
tables_properties.begin()->second;
// 45 point data + 45 range deletions. 45 obsolete point data are garbage
// collected.
ASSERT_EQ(90, table_properties->num_entries);
ASSERT_EQ(45, table_properties->num_deletions);
ASSERT_EQ(45, table_properties->num_range_deletions);
Close();
}
TEST_F(ManualFlushSkipRetainUDTTest, ManualCompaction) {
Open();
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
// Manual compaction proceeds without trying to retain UDT.
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v2"));
ASSERT_OK(
db_->CompactRange(CompactRangeOptions(), handles_[0], nullptr, nullptr));
CheckEffectiveCutoffTime(2);
CheckAutomaticFlushRetainUDT(3);
Close();
}
TEST_F(ManualFlushSkipRetainUDTTest, BulkLoading) {
Open();
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
// Test flush behavior in bulk loading scenarios.
Options options(db_options_, column_family_options_);
std::string sst_files_dir = dbname_ + "/sst_files/";
ASSERT_OK(DestroyDir(env_, sst_files_dir));
ASSERT_OK(env_->CreateDir(sst_files_dir));
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file1 = sst_files_dir + "file1.sst";
ASSERT_OK(sst_file_writer.Open(file1));
ASSERT_OK(sst_file_writer.Put("foo", EncodeAsUint64(0), "v2"));
ExternalSstFileInfo file1_info;
ASSERT_OK(sst_file_writer.Finish(&file1_info));
// Bulk loading in UDT mode doesn't support external file key range overlap
// with DB key range.
ASSERT_TRUE(db_->IngestExternalFile({file1}, IngestExternalFileOptions())
.IsInvalidArgument());
std::string file2 = sst_files_dir + "file2.sst";
ASSERT_OK(sst_file_writer.Open(file2));
ASSERT_OK(sst_file_writer.Put("bar", EncodeAsUint64(0), "val"));
ExternalSstFileInfo file2_info;
ASSERT_OK(sst_file_writer.Finish(&file2_info));
// A successful bulk loading, and it doesn't trigger any flush. As a result
// the effective cutoff timestamp is also unchanged.
ASSERT_OK(db_->IngestExternalFile({file2}, IngestExternalFileOptions()));
ASSERT_EQ(Get(0, "foo", EncodeAsUint64(1)), "v1");
ASSERT_EQ(Get(0, "bar", EncodeAsUint64(0)), "val");
CheckEffectiveCutoffTime(0);
CheckAutomaticFlushRetainUDT(1);
Close();
}
TEST_F(ManualFlushSkipRetainUDTTest, AutomaticFlushQueued) {
Open();
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
ASSERT_OK(dbfull()->TEST_SwitchWAL());
CheckEffectiveCutoffTime(0);
// Default `max_write_buffer_number=2` used, writing another memtable can get
// automatic flush to proceed because of memory pressure. Not doing that so
// we can test automatic flush gets to proceed because of an ongoing manual
// flush attempt.
ASSERT_OK(Flush(0));
CheckEffectiveCutoffTime(2);
CheckAutomaticFlushRetainUDT(3);
Close();
}
TEST_F(ManualFlushSkipRetainUDTTest, ConcurrentManualFlushes) {
Open();
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
std::vector<ROCKSDB_NAMESPACE::port::Thread> manual_flush_tds;
std::atomic<int> next_ts{0};
std::mutex mtx;
std::condition_variable cv;
auto manual_flush = [&](int write_ts) {
{
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock,
[&write_ts, &next_ts] { return write_ts == next_ts.load(); });
ASSERT_OK(Put(0, "foo" + std::to_string(write_ts),
EncodeAsUint64(write_ts),
"val_" + std::to_string(write_ts)));
next_ts.fetch_add(1);
cv.notify_all();
}
if (write_ts % 2 == 0) {
ASSERT_OK(Flush(0));
} else {
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), handles_[0], nullptr,
nullptr));
}
};
for (int write_ts = 0; write_ts < 10; write_ts++) {
manual_flush_tds.emplace_back(manual_flush, write_ts);
}
for (auto& td : manual_flush_tds) {
td.join();
}
CheckEffectiveCutoffTime(10);
CheckAutomaticFlushRetainUDT(11);
Close();
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+1 -230
View File
@@ -3,6 +3,7 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#include <mutex>
#include <string>
#include <thread>
@@ -441,10 +442,6 @@ TEST_F(CompactFilesTest, SentinelCompressionType) {
}
TEST_F(CompactFilesTest, CompressionWithBlockAlign) {
if (!Snappy_Supported()) {
ROCKSDB_GTEST_SKIP("Test requires Snappy support");
return;
}
Options options;
options.compression = CompressionType::kNoCompression;
options.create_if_missing = true;
@@ -534,232 +531,6 @@ TEST_F(CompactFilesTest, GetCompactionJobInfo) {
delete db;
}
// Helper function to generate zero-padded keys
// e.g., MakeKey("a", 5) -> "a05", MakeKey("b", 42) -> "b42"
static std::string MakeKey(const std::string& prefix, int index) {
return prefix + (index < 10 ? "0" : "") + std::to_string(index);
}
TEST_F(CompactFilesTest, TrivialMoveNonOverlappingFiles) {
Options options;
options.create_if_missing = true;
options.disable_auto_compactions = true;
options.compression = kNoCompression;
options.level_compaction_dynamic_level_bytes = false;
DB* db = nullptr;
ASSERT_OK(DestroyDB(db_name_, options));
Status s = DB::Open(options, db_name_, &db);
ASSERT_OK(s);
ASSERT_NE(db, nullptr);
// Create 3 non-overlapping files in L0
// File 1: keys [a00-a99]
for (int i = 0; i < 100; i++) {
std::string key = MakeKey("a", i);
ASSERT_OK(db->Put(WriteOptions(), key, "value_" + key));
}
ASSERT_OK(db->Flush(FlushOptions()));
// File 2: keys [b00-b99]
for (int i = 0; i < 100; i++) {
std::string key = MakeKey("b", i);
ASSERT_OK(db->Put(WriteOptions(), key, "value_" + key));
}
ASSERT_OK(db->Flush(FlushOptions()));
// File 3: keys [c00-c99]
for (int i = 0; i < 100; i++) {
std::string key = MakeKey("c", i);
ASSERT_OK(db->Put(WriteOptions(), key, "value_" + key));
}
ASSERT_OK(db->Flush(FlushOptions()));
// Verify files are in L0
ColumnFamilyMetaData meta;
db->GetColumnFamilyMetaData(&meta);
ASSERT_EQ(meta.levels[0].files.size(), 3);
ASSERT_EQ(meta.levels[1].files.size(), 0);
// Get L0 files
std::vector<std::string> l0_files;
for (const auto& file : meta.levels[0].files) {
l0_files.push_back(file.db_path + "/" + file.name);
}
CompactionOptions compact_option;
compact_option.allow_trivial_move = true;
// Compact all L0 files to L1 (non-overlapping in L1)
ASSERT_OK(db->CompactFiles(compact_option, l0_files, 1));
// Verify files are now in L1
db->GetColumnFamilyMetaData(&meta);
ASSERT_EQ(meta.levels[0].files.size(), 0);
ASSERT_EQ(meta.levels[1].files.size(), 3);
// Get the first file from L1 (should be the one with keys a00-a99)
std::string l1_file_to_move;
std::vector<std::string> l1_files_to_move_later;
uint64_t l1_file_number = 0;
for (const auto& file : meta.levels[1].files) {
if (file.smallestkey[0] == 'a') {
l1_file_to_move = file.db_path + "/" + file.name;
l1_file_number = file.file_number;
} else {
l1_files_to_move_later.push_back(file.db_path + "/" + file.name);
}
}
ASSERT_FALSE(l1_file_to_move.empty());
// Set up sync point to verify trivial move path is taken
bool trivial_move_executed = false;
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::CompactFilesImpl:TrivialMove",
[&](void* /*arg*/) { trivial_move_executed = true; });
SyncPoint::GetInstance()->EnableProcessing();
// Move the file from L1 to L6 - this should be a trivial move
// because the file doesn't overlap with anything in L6
std::vector<std::string> files_to_move = {l1_file_to_move};
ASSERT_OK(db->CompactFiles(compact_option, files_to_move, 6));
// Verify trivial move was executed
ASSERT_TRUE(trivial_move_executed);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
// Verify the file is now in L6
db->GetColumnFamilyMetaData(&meta);
ASSERT_EQ(meta.levels[1].files.size(), 2); // Two files remain in L1
ASSERT_EQ(meta.levels[6].files.size(), 1); // One file in L6
// Verify it's the correct file in L6
bool found_file_in_l6 = false;
for (const auto& file : meta.levels[6].files) {
if (file.file_number == l1_file_number) {
found_file_in_l6 = true;
// Verify key range hasn't changed
ASSERT_EQ(file.smallestkey[0], 'a');
ASSERT_EQ(file.largestkey[0], 'a');
break;
}
}
ASSERT_TRUE(found_file_in_l6);
// Move the other 2 files from L1 to L6, with allow_trivial_move set to false.
// This will trigger a normal compaction, so the 2 files will be compacted
// into a single file in L6.
ASSERT_OK(db->CompactFiles(CompactionOptions(), l1_files_to_move_later, 6));
// Verify files in L6
db->GetColumnFamilyMetaData(&meta);
ASSERT_EQ(meta.levels[1].files.size(), 0); // Zero files remain in L1
ASSERT_EQ(meta.levels[6].files.size(), 2); // Two file in L6
// Verify data integrity - all keys should still be readable
for (int i = 0; i < 100; i++) {
std::string key = MakeKey("a", i);
std::string value;
ASSERT_OK(db->Get(ReadOptions(), key, &value));
ASSERT_EQ(value, "value_" + key);
}
delete db;
}
TEST_F(CompactFilesTest, TrivialMoveBlockedByOverlap) {
Options options;
options.create_if_missing = true;
options.disable_auto_compactions = true;
options.compression = kNoCompression;
options.level_compaction_dynamic_level_bytes = false;
options.num_levels = 7;
DB* db = nullptr;
ASSERT_OK(DestroyDB(db_name_, options));
Status s = DB::Open(options, db_name_, &db);
ASSERT_OK(s);
ASSERT_NE(db, nullptr);
// Create a file in L6 with keys [m00-m99] (wide range)
for (int i = 0; i < 100; i++) {
std::string key = MakeKey("m", i);
ASSERT_OK(db->Put(WriteOptions(), key, "value_" + key));
}
ASSERT_OK(db->Flush(FlushOptions()));
// Get L0 file
ColumnFamilyMetaData meta;
db->GetColumnFamilyMetaData(&meta);
std::vector<std::string> l0_files;
for (const auto& file : meta.levels[0].files) {
l0_files.push_back(file.db_path + "/" + file.name);
}
CompactionOptions compact_option;
compact_option.allow_trivial_move = true;
// Move to L6
ASSERT_OK(db->CompactFiles(compact_option, l0_files, 6));
// Now create a file in L1 with overlapping keys [m50-m60]
for (int i = 50; i <= 60; i++) {
std::string key = "m" + std::to_string(i);
ASSERT_OK(db->Put(WriteOptions(), key, "updated_value_" + key));
}
ASSERT_OK(db->Flush(FlushOptions()));
// Get the L0 file
db->GetColumnFamilyMetaData(&meta);
std::vector<std::string> l0_files_2;
for (const auto& file : meta.levels[0].files) {
l0_files_2.push_back(file.db_path + "/" + file.name);
}
// Move to L1
ASSERT_OK(db->CompactFiles(compact_option, l0_files_2, 1));
// Get the L1 file
db->GetColumnFamilyMetaData(&meta);
ASSERT_EQ(meta.levels[1].files.size(), 1);
std::string l1_file =
meta.levels[1].files[0].db_path + "/" + meta.levels[1].files[0].name;
// Set up sync point to verify full compaction path is taken
bool trivial_move_executed = false;
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::CompactFilesImpl:TrivialMove",
[&](void* /*arg*/) { trivial_move_executed = true; });
SyncPoint::GetInstance()->EnableProcessing();
// Try to move from L1 to L6 - this should NOT be a trivial move
// because the file overlaps with the existing file in L6
ASSERT_OK(db->CompactFiles(compact_option, {l1_file}, 6));
// Verify trivial move was NOT executed (full compaction happened)
ASSERT_FALSE(trivial_move_executed);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
// Verify the result - should have merged data in L6
db->GetColumnFamilyMetaData(&meta);
ASSERT_EQ(meta.levels[1].files.size(), 0); // L1 should be empty
// L6 should have the merged file (may be 1 file if merged, or 2 if not)
ASSERT_GE(meta.levels[6].files.size(), 1);
// Verify updated values are present
for (int i = 50; i <= 60; i++) {
std::string key = "m" + std::to_string(i);
std::string value;
ASSERT_OK(db->Get(ReadOptions(), key, &value));
ASSERT_EQ(value, "updated_value_" + key);
}
delete db;
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+96 -253
View File
@@ -63,9 +63,7 @@ uint64_t TotalFileSize(const std::vector<FileMetaData*>& files) {
return sum;
}
// TODO(hx235): consider making this function part of the construction so we
// don't forget to call it
void Compaction::FinalizeInputInfo(Version* _input_version) {
void Compaction::SetInputVersion(Version* _input_version) {
input_version_ = _input_version;
cfd_ = input_version_->cfd();
@@ -281,13 +279,11 @@ Compaction::Compaction(
std::vector<CompactionInputFiles> _inputs, int _output_level,
uint64_t _target_file_size, uint64_t _max_compaction_bytes,
uint32_t _output_path_id, CompressionType _compression,
CompressionOptions _compression_opts,
Temperature _output_temperature_override, uint32_t _max_subcompactions,
std::vector<FileMetaData*> _grandparents,
std::optional<SequenceNumber> _earliest_snapshot,
const SnapshotChecker* _snapshot_checker,
CompactionReason _compaction_reason, const std::string& _trim_ts,
double _score, bool l0_files_might_overlap,
CompressionOptions _compression_opts, Temperature _output_temperature,
uint32_t _max_subcompactions, std::vector<FileMetaData*> _grandparents,
bool _manual_compaction, const std::string& _trim_ts, double _score,
bool _deletion_compaction, bool l0_files_might_overlap,
CompactionReason _compaction_reason,
BlobGarbageCollectionPolicy _blob_garbage_collection_policy,
double _blob_garbage_collection_age_cutoff)
: input_vstorage_(vstorage),
@@ -304,15 +300,11 @@ Compaction::Compaction(
output_path_id_(_output_path_id),
output_compression_(_compression),
output_compression_opts_(_compression_opts),
output_temperature_override_(_output_temperature_override),
deletion_compaction_(_compaction_reason == CompactionReason::kFIFOTtl ||
_compaction_reason ==
CompactionReason::kFIFOMaxSize),
output_temperature_(_output_temperature),
deletion_compaction_(_deletion_compaction),
l0_files_might_overlap_(l0_files_might_overlap),
inputs_(PopulateWithAtomicBoundaries(vstorage, std::move(_inputs))),
grandparents_(std::move(_grandparents)),
earliest_snapshot_(_earliest_snapshot),
snapshot_checker_(_snapshot_checker),
score_(_score),
bottommost_level_(
// For simplicity, we don't support the concept of "bottommost level"
@@ -324,8 +316,7 @@ Compaction::Compaction(
? false
: IsBottommostLevel(output_level_, vstorage, inputs_)),
is_full_compaction_(IsFullCompaction(vstorage, inputs_)),
is_manual_compaction_(_compaction_reason ==
CompactionReason::kManualCompaction),
is_manual_compaction_(_manual_compaction),
trim_ts_(_trim_ts),
is_trivial_move_(false),
compaction_reason_(_compaction_reason),
@@ -336,23 +327,25 @@ Compaction::Compaction(
: (_blob_garbage_collection_policy ==
BlobGarbageCollectionPolicy::kDisable
? false
: mutable_cf_options().enable_blob_garbage_collection)),
: mutable_cf_options()->enable_blob_garbage_collection)),
blob_garbage_collection_age_cutoff_(
_blob_garbage_collection_age_cutoff < 0 ||
_blob_garbage_collection_age_cutoff > 1
? mutable_cf_options().blob_garbage_collection_age_cutoff
? mutable_cf_options()->blob_garbage_collection_age_cutoff
: _blob_garbage_collection_age_cutoff),
proximal_level_(
// For simplicity, we don't support the concept of "proximal level"
penultimate_level_(
// For simplicity, we don't support the concept of "penultimate level"
// with `CompactionReason::kExternalSstIngestion` and
// `CompactionReason::kRefitLevel`
_compaction_reason == CompactionReason::kExternalSstIngestion ||
_compaction_reason == CompactionReason::kRefitLevel
? Compaction::kInvalidLevel
: EvaluateProximalLevel(vstorage, mutable_cf_options_,
immutable_options_, start_level_,
output_level_)) {
: EvaluatePenultimateLevel(vstorage, immutable_options_,
start_level_, output_level_)) {
MarkFilesBeingCompacted(true);
if (is_manual_compaction_) {
compaction_reason_ = CompactionReason::kManualCompaction;
}
if (max_subcompactions_ == 0) {
max_subcompactions_ = _mutable_db_options.max_subcompactions;
}
@@ -369,17 +362,12 @@ Compaction::Compaction(
}
#endif
// setup input_levels_ and filtered_input_levels_
// setup input_levels_
{
input_levels_.resize(num_input_levels());
filtered_input_levels_.resize(num_input_levels());
if (earliest_snapshot_.has_value()) {
FilterInputsForCompactionIterator();
} else {
for (size_t which = 0; which < num_input_levels(); which++) {
DoGenerateLevelFilesBrief(&input_levels_[which], inputs_[which].files,
&arena_);
}
for (size_t which = 0; which < num_input_levels(); which++) {
DoGenerateLevelFilesBrief(&input_levels_[which], inputs_[which].files,
&arena_);
}
}
@@ -406,71 +394,53 @@ Compaction::Compaction(
}
}
PopulateProximalLevelOutputRange();
PopulatePenultimateLevelOutputRange();
}
void Compaction::PopulateProximalLevelOutputRange() {
void Compaction::PopulatePenultimateLevelOutputRange() {
if (!SupportsPerKeyPlacement()) {
assert(keep_in_last_level_through_seqno_ == kMaxSequenceNumber);
return;
}
// exclude the last level, the range of all input levels is the safe range
// of keys that can be moved up.
int exclude_level = number_levels_ - 1;
proximal_output_range_type_ = ProximalOutputRangeType::kNonLastRange;
penultimate_output_range_type_ = PenultimateOutputRangeType::kNonLastRange;
// For universal compaction, the proximal_output_range could be extended if
// all proximal level files are included in the compaction (which includes
// the case that the proximal level is empty).
// For universal compaction, the penultimate_output_range could be extended if
// all penultimate level files are included in the compaction (which includes
// the case that the penultimate level is empty).
if (immutable_options_.compaction_style == kCompactionStyleUniversal) {
exclude_level = kInvalidLevel;
proximal_output_range_type_ = ProximalOutputRangeType::kFullRange;
std::set<uint64_t> proximal_inputs;
penultimate_output_range_type_ = PenultimateOutputRangeType::kFullRange;
std::set<uint64_t> penultimate_inputs;
for (const auto& input_lvl : inputs_) {
if (input_lvl.level == proximal_level_) {
if (input_lvl.level == penultimate_level_) {
for (const auto& file : input_lvl.files) {
proximal_inputs.emplace(file->fd.GetNumber());
penultimate_inputs.emplace(file->fd.GetNumber());
}
}
}
auto proximal_files = input_vstorage_->LevelFiles(proximal_level_);
for (const auto& file : proximal_files) {
if (proximal_inputs.find(file->fd.GetNumber()) == proximal_inputs.end()) {
auto penultimate_files = input_vstorage_->LevelFiles(penultimate_level_);
for (const auto& file : penultimate_files) {
if (penultimate_inputs.find(file->fd.GetNumber()) ==
penultimate_inputs.end()) {
exclude_level = number_levels_ - 1;
proximal_output_range_type_ = ProximalOutputRangeType::kNonLastRange;
penultimate_output_range_type_ =
PenultimateOutputRangeType::kNonLastRange;
break;
}
}
}
// FIXME: should make use of `proximal_output_range_type_`.
// FIXME: should make use of `penultimate_output_range_type_`.
// FIXME: when last level's input range does not overlap with
// proximal level, and proximal level input is empty,
// this call will not set proximal_level_smallest_ or
// proximal_level_largest_. No keys will be compacted up.
GetBoundaryInternalKeys(input_vstorage_, inputs_, &proximal_level_smallest_,
&proximal_level_largest_, exclude_level);
if (proximal_output_range_type_ != ProximalOutputRangeType::kFullRange) {
// If not full range in proximal level, must keep everything already
// in the last level there, because moving it back up might cause
// overlap/placement issues that are difficult to resolve properly in the
// presence of range deletes
SequenceNumber max_last_level_seqno = 0;
for (const auto& input_lvl : inputs_) {
if (input_lvl.level == output_level_) {
for (const auto& file : input_lvl.files) {
max_last_level_seqno =
std::max(max_last_level_seqno, file->fd.largest_seqno);
}
}
}
keep_in_last_level_through_seqno_ = max_last_level_seqno;
} else {
keep_in_last_level_through_seqno_ = 0;
}
// penultimate level, and penultimate level input is empty,
// this call will not set penultimate_level_smallest_ or
// penultimate_level_largest_. No keys will be compacted up.
GetBoundaryInternalKeys(input_vstorage_, inputs_,
&penultimate_level_smallest_,
&penultimate_level_largest_, exclude_level);
}
Compaction::~Compaction() {
@@ -483,23 +453,23 @@ Compaction::~Compaction() {
}
bool Compaction::SupportsPerKeyPlacement() const {
return proximal_level_ != kInvalidLevel;
return penultimate_level_ != kInvalidLevel;
}
int Compaction::GetProximalLevel() const { return proximal_level_; }
int Compaction::GetPenultimateLevel() const { return penultimate_level_; }
// smallest_key and largest_key include timestamps if user-defined timestamp is
// enabled.
bool Compaction::OverlapProximalLevelOutputRange(
bool Compaction::OverlapPenultimateLevelOutputRange(
const Slice& smallest_key, const Slice& largest_key) const {
if (!SupportsPerKeyPlacement()) {
return false;
}
// See FIXME in Compaction::PopulateProximalLevelOutputRange().
// See FIXME in Compaction::PopulatePenultimateLevelOutputRange().
// We do not compact any key up in this case.
if (proximal_level_smallest_.size() == 0 ||
proximal_level_largest_.size() == 0) {
if (penultimate_level_smallest_.size() == 0 ||
penultimate_level_largest_.size() == 0) {
return false;
}
@@ -507,35 +477,28 @@ bool Compaction::OverlapProximalLevelOutputRange(
input_vstorage_->InternalComparator()->user_comparator();
return ucmp->CompareWithoutTimestamp(
smallest_key, proximal_level_largest_.user_key()) <= 0 &&
smallest_key, penultimate_level_largest_.user_key()) <= 0 &&
ucmp->CompareWithoutTimestamp(
largest_key, proximal_level_smallest_.user_key()) >= 0;
largest_key, penultimate_level_smallest_.user_key()) >= 0;
}
// key includes timestamp if user-defined timestamp is enabled.
void Compaction::TEST_AssertWithinProximalLevelOutputRange(
const Slice& user_key, bool expect_failure) const {
#ifdef NDEBUG
(void)user_key;
(void)expect_failure;
#else
assert(SupportsPerKeyPlacement());
bool Compaction::WithinPenultimateLevelOutputRange(
const ParsedInternalKey& ikey) const {
if (!SupportsPerKeyPlacement()) {
return false;
}
assert(proximal_level_smallest_.size() > 0);
assert(proximal_level_largest_.size() > 0);
if (penultimate_level_smallest_.size() == 0 ||
penultimate_level_largest_.size() == 0) {
return false;
}
auto* cmp = input_vstorage_->user_comparator();
const InternalKeyComparator* icmp = input_vstorage_->InternalComparator();
// op_type of a key can change during compaction, e.g. Merge -> Put.
if (!(cmp->Compare(user_key, proximal_level_smallest_.user_key()) >= 0)) {
assert(expect_failure);
} else if (!(cmp->Compare(user_key, proximal_level_largest_.user_key()) <=
0)) {
assert(expect_failure);
} else {
assert(!expect_failure);
}
#endif
return icmp->CompareKeySeq(ikey, penultimate_level_smallest_.Encode()) >= 0 &&
icmp->CompareKeySeq(ikey, penultimate_level_largest_.Encode()) <= 0;
}
bool Compaction::InputCompressionMatchesOutput() const {
@@ -589,7 +552,7 @@ bool Compaction::IsTrivialMove() const {
// input files are non overlapping
if ((mutable_cf_options_.compaction_options_universal.allow_trivial_move) &&
(output_level_ != 0) &&
(cfd_->ioptions().compaction_style == kCompactionStyleUniversal)) {
(cfd_->ioptions()->compaction_style == kCompactionStyleUniversal)) {
return is_trivial_move_;
}
@@ -647,9 +610,7 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(
if (bottommost_level_) {
return true;
} else if (output_level_ != 0 &&
cfd_->ioptions().compaction_style == kCompactionStyleLevel) {
// TODO: apply the optimization here to other compaction styles and
// compaction/flush to L0.
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
// Maybe use binary search to find right entry instead of linear search?
const Comparator* user_cmp = cfd_->user_comparator();
for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++) {
@@ -690,7 +651,7 @@ bool Compaction::KeyRangeNotExistsBeyondOutputLevel(
if (bottommost_level_) {
return true /* does not overlap */;
} else if (output_level_ != 0 &&
cfd_->ioptions().compaction_style == kCompactionStyleLevel) {
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
const Comparator* user_cmp = cfd_->user_comparator();
for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++) {
const std::vector<FileMetaData*>& files =
@@ -723,11 +684,12 @@ bool Compaction::KeyRangeNotExistsBeyondOutputLevel(
};
// Mark (or clear) each file that is being compacted
void Compaction::MarkFilesBeingCompacted(bool being_compacted) const {
void Compaction::MarkFilesBeingCompacted(bool mark_as_compacted) {
for (size_t i = 0; i < num_input_levels(); i++) {
for (size_t j = 0; j < inputs_[i].size(); j++) {
assert(being_compacted != inputs_[i][j]->being_compacted);
inputs_[i][j]->being_compacted = being_compacted;
assert(mark_as_compacted ? !inputs_[i][j]->being_compacted
: inputs_[i][j]->being_compacted);
inputs_[i][j]->being_compacted = mark_as_compacted;
}
}
}
@@ -771,7 +733,7 @@ uint64_t Compaction::CalculateTotalInputSize() const {
return size;
}
void Compaction::ReleaseCompactionFiles(const Status& status) {
void Compaction::ReleaseCompactionFiles(Status status) {
MarkFilesBeingCompacted(false);
cfd_->compaction_picker()->ReleaseCompactionFiles(this, status);
}
@@ -782,10 +744,8 @@ void Compaction::ResetNextCompactionIndex() {
}
namespace {
int InputSummary(const std::vector<FileMetaData*>& files,
const std::vector<bool>& files_filtered, char* output,
int InputSummary(const std::vector<FileMetaData*>& files, char* output,
int len) {
assert(files_filtered.empty() || (files.size() == files_filtered.size()));
*output = '\0';
int write = 0;
for (size_t i = 0; i < files.size(); i++) {
@@ -793,14 +753,8 @@ int InputSummary(const std::vector<FileMetaData*>& files,
int ret;
char sztxt[16];
AppendHumanBytes(files.at(i)->fd.GetFileSize(), sztxt, 16);
if (files_filtered.empty()) {
ret = snprintf(output + write, sz, "%" PRIu64 "(%s) ",
files.at(i)->fd.GetNumber(), sztxt);
} else {
ret = snprintf(output + write, sz, "%" PRIu64 "(%s filtered:%s) ",
files.at(i)->fd.GetNumber(), sztxt,
files_filtered.at(i) ? "true" : "false");
}
ret = snprintf(output + write, sz, "%" PRIu64 "(%s) ",
files.at(i)->fd.GetNumber(), sztxt);
if (ret < 0 || ret >= sz) {
break;
}
@@ -826,15 +780,8 @@ void Compaction::Summary(char* output, int len) {
return;
}
}
assert(non_start_level_input_files_filtered_.empty() ||
non_start_level_input_files_filtered_.size() == inputs_.size() - 1);
write += InputSummary(
inputs_[level_iter].files,
(level_iter == 0 || non_start_level_input_files_filtered_.empty())
? std::vector<bool>{}
: non_start_level_input_files_filtered_[level_iter - 1],
output + write, len - write);
write +=
InputSummary(inputs_[level_iter].files, output + write, len - write);
if (write < 0 || write >= len) {
return;
}
@@ -866,12 +813,12 @@ uint64_t Compaction::OutputFilePreallocationSize() const {
}
std::unique_ptr<CompactionFilter> Compaction::CreateCompactionFilter() const {
if (!cfd_->ioptions().compaction_filter_factory) {
if (!cfd_->ioptions()->compaction_filter_factory) {
return nullptr;
}
if (!cfd_->ioptions()
.compaction_filter_factory->ShouldFilterTableFileCreation(
->compaction_filter_factory->ShouldFilterTableFileCreation(
TableFileCreationReason::kCompaction)) {
return nullptr;
}
@@ -890,7 +837,7 @@ std::unique_ptr<CompactionFilter> Compaction::CreateCompactionFilter() const {
"for compaction.");
}
return cfd_->ioptions().compaction_filter_factory->CreateCompactionFilter(
return cfd_->ioptions()->compaction_filter_factory->CreateCompactionFilter(
context);
}
@@ -917,15 +864,10 @@ bool Compaction::ShouldFormSubcompactions() const {
return false;
}
if (mutable_cf_options_.table_factory->Name() ==
TableFactory::kPlainTableName()) {
return false;
}
// Round-Robin pri under leveled compaction allows subcompactions by default
// and the number of subcompactions can be larger than max_subcompactions_
if (cfd_->ioptions().compaction_pri == kRoundRobin &&
cfd_->ioptions().compaction_style == kCompactionStyleLevel) {
if (cfd_->ioptions()->compaction_pri == kRoundRobin &&
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
return output_level_ > 0;
}
@@ -933,9 +875,9 @@ bool Compaction::ShouldFormSubcompactions() const {
return false;
}
if (cfd_->ioptions().compaction_style == kCompactionStyleLevel) {
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
return (start_level_ == 0 || is_manual_compaction_) && output_level_ > 0;
} else if (cfd_->ioptions().compaction_style == kCompactionStyleUniversal) {
} else if (cfd_->ioptions()->compaction_style == kCompactionStyleUniversal) {
return number_levels_ > 1 && output_level_ > 0;
} else {
return false;
@@ -965,25 +907,6 @@ bool Compaction::DoesInputReferenceBlobFiles() const {
return false;
}
uint64_t Compaction::MaxInputFileNewestKeyTime(const InternalKey* start,
const InternalKey* end) const {
uint64_t newest_key_time = kUnknownNewestKeyTime;
const InternalKeyComparator& icmp =
column_family_data()->internal_comparator();
for (const auto& level_files : inputs_) {
for (const auto& file : level_files.files) {
if (start != nullptr && icmp.Compare(file->largest, *start) < 0) {
continue;
}
if (end != nullptr && icmp.Compare(file->smallest, *end) > 0) {
continue;
}
newest_key_time = std::max(newest_key_time, file->TryGetNewestKeyTime());
}
}
return newest_key_time;
}
uint64_t Compaction::MinInputFileOldestAncesterTime(
const InternalKey* start, const InternalKey* end) const {
uint64_t min_oldest_ancester_time = std::numeric_limits<uint64_t>::max();
@@ -1017,9 +940,8 @@ uint64_t Compaction::MinInputFileEpochNumber() const {
return min_epoch_number;
}
int Compaction::EvaluateProximalLevel(
int Compaction::EvaluatePenultimateLevel(
const VersionStorageInfo* vstorage,
const MutableCFOptions& mutable_cf_options,
const ImmutableOptions& immutable_options, const int start_level,
const int output_level) {
// TODO: currently per_key_placement feature only support level and universal
@@ -1032,26 +954,26 @@ int Compaction::EvaluateProximalLevel(
return kInvalidLevel;
}
int proximal_level = output_level - 1;
assert(proximal_level < immutable_options.num_levels);
if (proximal_level <= 0) {
int penultimate_level = output_level - 1;
assert(penultimate_level < immutable_options.num_levels);
if (penultimate_level <= 0) {
return kInvalidLevel;
}
// If the proximal level is not within input level -> output level range
// check if the proximal output level is empty, if it's empty, it could
// also be locked for the proximal output.
// If the penultimate level is not within input level -> output level range
// check if the penultimate output level is empty, if it's empty, it could
// also be locked for the penultimate output.
// TODO: ideally, it only needs to check if there's a file within the
// compaction output key range. For simplicity, it just check if there's any
// file on the proximal level.
// file on the penultimate level.
if (start_level == immutable_options.num_levels - 1 &&
(immutable_options.compaction_style != kCompactionStyleUniversal ||
!vstorage->LevelFiles(proximal_level).empty())) {
!vstorage->LevelFiles(penultimate_level).empty())) {
return kInvalidLevel;
}
bool supports_per_key_placement =
mutable_cf_options.preclude_last_level_data_seconds > 0;
immutable_options.preclude_last_level_data_seconds > 0;
// it could be overridden by unittest
TEST_SYNC_POINT_CALLBACK("Compaction::SupportsPerKeyPlacement:Enabled",
@@ -1060,86 +982,7 @@ int Compaction::EvaluateProximalLevel(
return kInvalidLevel;
}
return proximal_level;
}
void Compaction::FilterInputsForCompactionIterator() {
assert(earliest_snapshot_.has_value());
// cfd_ is not populated at Compaction construction time, get it from
// VersionStorageInfo instead.
assert(input_vstorage_);
const auto* ucmp = input_vstorage_->user_comparator();
assert(ucmp);
// Simply comparing file boundaries when user-defined timestamp is defined
// is not as safe because we need to also compare timestamp to know for
// sure. Although entries with higher timestamp is also supposed to have
// higher sequence number for the same user key (without timestamp).
assert(ucmp->timestamp_size() == 0);
size_t num_input_levels = inputs_.size();
// TODO(yuzhangyu): filtering of older L0 file by new L0 file is not
// supported yet.
FileMetaData* rangedel_candidate = inputs_[0].level == 0
? inputs_[0].files.back()
: inputs_[0].files.front();
assert(rangedel_candidate);
if (!rangedel_candidate->FileIsStandAloneRangeTombstone() ||
!DataIsDefinitelyInSnapshot(rangedel_candidate->fd.smallest_seqno,
earliest_snapshot_.value(),
snapshot_checker_)) {
for (size_t level = 0; level < num_input_levels; level++) {
DoGenerateLevelFilesBrief(&input_levels_[level], inputs_[level].files,
&arena_);
}
return;
}
Slice rangedel_start_ukey = rangedel_candidate->smallest.user_key();
Slice rangedel_end_ukey = rangedel_candidate->largest.user_key();
SequenceNumber rangedel_seqno = rangedel_candidate->fd.smallest_seqno;
std::vector<std::vector<FileMetaData*>> non_start_level_input_files;
non_start_level_input_files.reserve(num_input_levels - 1);
non_start_level_input_files_filtered_.reserve(num_input_levels - 1);
for (size_t level = 1; level < num_input_levels; level++) {
non_start_level_input_files.emplace_back();
non_start_level_input_files_filtered_.emplace_back();
for (FileMetaData* file : inputs_[level].files) {
non_start_level_input_files_filtered_.back().push_back(false);
// When range data and point data has the same sequence number, point
// data wins. Range deletion end key is exclusive, so check it's bigger
// than file right boundary user key.
if (rangedel_seqno > file->fd.largest_seqno &&
ucmp->CompareWithoutTimestamp(rangedel_start_ukey,
file->smallest.user_key()) <= 0 &&
ucmp->CompareWithoutTimestamp(rangedel_end_ukey,
file->largest.user_key()) > 0) {
non_start_level_input_files_filtered_.back().back() = true;
filtered_input_levels_[level].push_back(file);
} else {
non_start_level_input_files.back().push_back(file);
}
}
}
DoGenerateLevelFilesBrief(&input_levels_[0], inputs_[0].files, &arena_);
assert(non_start_level_input_files.size() == num_input_levels - 1);
for (size_t level = 1; level < num_input_levels; level++) {
DoGenerateLevelFilesBrief(&input_levels_[level],
non_start_level_input_files[level - 1], &arena_);
}
}
Temperature Compaction::GetOutputTemperature(bool is_proximal_level) const {
if (output_temperature_override_ != Temperature::kUnknown) {
return output_temperature_override_;
}
if (is_last_level() && !is_proximal_level &&
mutable_cf_options_.last_level_temperature != Temperature::kUnknown) {
return mutable_cf_options_.last_level_temperature;
}
return mutable_cf_options_.default_write_temperature;
return penultimate_level;
}
} // namespace ROCKSDB_NAMESPACE
+70 -146
View File
@@ -8,8 +8,6 @@
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#pragma once
#include "db/snapshot_checker.h"
#include "db/version_set.h"
#include "memory/arena.h"
#include "options/cf_options.h"
@@ -90,25 +88,23 @@ class Compaction {
uint64_t target_file_size, uint64_t max_compaction_bytes,
uint32_t output_path_id, CompressionType compression,
CompressionOptions compression_opts,
Temperature output_temperature_override,
uint32_t max_subcompactions,
Temperature output_temperature, uint32_t max_subcompactions,
std::vector<FileMetaData*> grandparents,
std::optional<SequenceNumber> earliest_snapshot,
const SnapshotChecker* snapshot_checker,
CompactionReason compaction_reason,
const std::string& trim_ts = "", double score = -1,
bool manual_compaction = false, const std::string& trim_ts = "",
double score = -1, bool deletion_compaction = false,
bool l0_files_might_overlap = true,
CompactionReason compaction_reason = CompactionReason::kUnknown,
BlobGarbageCollectionPolicy blob_garbage_collection_policy =
BlobGarbageCollectionPolicy::kUseDefault,
double blob_garbage_collection_age_cutoff = -1);
// The type of the proximal level output range
enum class ProximalOutputRangeType : int {
kNotSupported, // it cannot output to the proximal level
kFullRange, // any data could be output to the proximal level
// The type of the penultimate level output range
enum class PenultimateOutputRangeType : int {
kNotSupported, // it cannot output to the penultimate level
kFullRange, // any data could be output to the penultimate level
kNonLastRange, // only the keys within non_last_level compaction inputs can
// be outputted to the proximal level
kDisabled, // no data can be outputted to the proximal level
// be outputted to the penultimate level
kDisabled, // no data can be outputted to the penultimate level
};
// No copying allowed
@@ -180,24 +176,10 @@ class Compaction {
const std::vector<CompactionInputFiles>* inputs() { return &inputs_; }
// Returns the LevelFilesBrief of the specified compaction input level.
// Note that if the compaction includes standalone range deletion file,
// this function returns the result after filtering out input files covered
// by the range deletion file.
// Use inputs() if you want to get the original input files.
const LevelFilesBrief* input_levels(size_t compaction_input_level) const {
return &input_levels_[compaction_input_level];
}
// Returns the filtered input files of the specified compaction input level.
// For now, only non start level is filtered.
const std::vector<FileMetaData*>& filtered_input_levels(
size_t compaction_input_level) const {
const std::vector<FileMetaData*>& filtered_input_level =
filtered_input_levels_[compaction_input_level];
assert(compaction_input_level != 0 || filtered_input_level.size() == 0);
return filtered_input_level;
}
// Maximum size of files to build during this compaction.
uint64_t max_output_file_size() const { return max_output_file_size_; }
@@ -248,7 +230,7 @@ class Compaction {
// Delete this compaction from the list of running compactions.
//
// Requirement: DB mutex held
void ReleaseCompactionFiles(const Status& status);
void ReleaseCompactionFiles(Status status);
// Returns the summary of the compaction in "output" with maximum "len"
// in bytes. The caller is responsible for the memory management of
@@ -287,26 +269,19 @@ class Compaction {
// are non-overlapping and can be trivially moved.
bool is_trivial_move() const { return is_trivial_move_; }
bool is_trivial_copy_compaction() const {
return immutable_options_.compaction_style == kCompactionStyleFIFO &&
compaction_reason_ == CompactionReason::kChangeTemperature &&
mutable_cf_options_.compaction_options_fifo
.allow_trivial_copy_when_change_temperature;
}
// How many total levels are there?
int number_levels() const { return number_levels_; }
// Return the ImmutableOptions that should be used throughout the compaction
// procedure
const ImmutableOptions& immutable_options() const {
return immutable_options_;
const ImmutableOptions* immutable_options() const {
return &immutable_options_;
}
// Return the MutableCFOptions that should be used throughout the compaction
// procedure
const MutableCFOptions& mutable_cf_options() const {
return mutable_cf_options_;
const MutableCFOptions* mutable_cf_options() const {
return &mutable_cf_options_;
}
// Returns the size in bytes that the output file should be preallocated to.
@@ -314,7 +289,14 @@ class Compaction {
// is the sum of all input file sizes.
uint64_t OutputFilePreallocationSize() const;
void FinalizeInputInfo(Version* input_version);
// TODO(hx235): eventually we should consider `InitInputTableProperties()`'s
// status and fail the compaction if needed
// TODO(hx235): consider making this function part of the construction so we
// don't forget to call it
void FinalizeInputInfo(Version* input_version) {
SetInputVersion(input_version);
InitInputTableProperties().PermitUncheckedError();
}
struct InputLevelSummaryBuffer {
char buffer[128];
@@ -351,16 +333,6 @@ class Compaction {
int output_level, VersionStorageInfo* vstorage,
const std::vector<CompactionInputFiles>& inputs);
// TODO(hx235): eventually we should consider `InitInputTableProperties()`'s
// status and fail the compaction if needed
//
// May open and read table files for table property.
// Should not be called while holding mutex_.
const TablePropertiesCollection& GetOrInitInputTableProperties() {
InitInputTableProperties().PermitUncheckedError();
return input_table_properties_;
}
const TablePropertiesCollection& GetInputTableProperties() const {
return input_table_properties_;
}
@@ -381,30 +353,30 @@ class Compaction {
Slice GetLargestUserKey() const { return largest_user_key_; }
ProximalOutputRangeType GetProximalOutputRangeType() const {
return proximal_output_range_type_;
PenultimateOutputRangeType GetPenultimateOutputRangeType() const {
return penultimate_output_range_type_;
}
// Return true if the compaction supports per_key_placement
bool SupportsPerKeyPlacement() const;
// Get per_key_placement proximal output level, which is `last_level - 1`
// Get per_key_placement penultimate output level, which is `last_level - 1`
// if per_key_placement feature is supported. Otherwise, return -1.
int GetProximalLevel() const;
int GetPenultimateLevel() const;
// Return true if the given range is overlap with proximal level output
// Return true if the given range is overlap with penultimate level output
// range.
// Both smallest_key and largest_key include timestamps if user-defined
// timestamp is enabled.
bool OverlapProximalLevelOutputRange(const Slice& smallest_key,
const Slice& largest_key) const;
bool OverlapPenultimateLevelOutputRange(const Slice& smallest_key,
const Slice& largest_key) const;
// For testing purposes, check that a key is within proximal level
// output range for per_key_placement feature, which is safe to place the key
// to the proximal level. Different compaction strategies have different
// rules. `user_key` includes timestamp if user-defined timestamp is enabled.
void TEST_AssertWithinProximalLevelOutputRange(
const Slice& user_key, bool expect_failure = false) const;
// Return true if the key is within penultimate level output range for
// per_key_placement feature, which is safe to place the key to the
// penultimate level. different compaction strategy has different rules.
// If per_key_placement is not supported, always return false.
// key includes timestamp if user-defined timestamp is enabled.
bool WithinPenultimateLevelOutputRange(const ParsedInternalKey& ikey) const;
CompactionReason compaction_reason() const { return compaction_reason_; }
@@ -414,11 +386,7 @@ class Compaction {
uint64_t max_compaction_bytes() const { return max_compaction_bytes_; }
// Order of precedence for temperature:
// 1. Override temp if not kUnknown
// 2. Temperature of the last level files if applicable
// 3. Default write temperature
Temperature GetOutputTemperature(bool is_proximal_level = false) const;
Temperature output_temperature() const { return output_temperature_; }
uint32_t max_subcompactions() const { return max_subcompactions_; }
@@ -430,12 +398,6 @@ class Compaction {
return blob_garbage_collection_age_cutoff_;
}
// start and end are sub compact range. Null if no boundary.
// This is used to calculate the newest_key_time table property after
// compaction.
uint64_t MaxInputFileNewestKeyTime(const InternalKey* start,
const InternalKey* end) const;
// start and end are sub compact range. Null if no boundary.
// This is used to filter out some input files' ancester's time range.
uint64_t MinInputFileOldestAncesterTime(const InternalKey* start,
@@ -456,39 +418,28 @@ class Compaction {
static constexpr int kInvalidLevel = -1;
// Evaluate proximal output level. If the compaction supports
// per_key_placement feature, it returns the proximal level number.
// Evaluate penultimate output level. If the compaction supports
// per_key_placement feature, it returns the penultimate level number.
// Otherwise, it's set to kInvalidLevel (-1), which means
// output_to_proximal_level is not supported.
// Note: even the proximal level output is supported (ProximalLevel !=
// output_to_penultimate_level is not supported.
// Note: even the penultimate level output is supported (PenultimateLevel !=
// kInvalidLevel), some key range maybe unsafe to be outputted to the
// proximal level. The safe key range is populated by
// `PopulateProximalLevelOutputRange()`.
// Which could potentially disable all proximal level output.
static int EvaluateProximalLevel(const VersionStorageInfo* vstorage,
const MutableCFOptions& mutable_cf_options,
const ImmutableOptions& immutable_options,
const int start_level,
const int output_level);
static bool OutputToNonZeroMaxOutputLevel(int output_level,
int max_output_level) {
return output_level > 0 && output_level == max_output_level;
}
// If some data cannot be safely migrated "up" the LSM tree due to a change
// in the preclude_last_level_data_seconds setting, this indicates a sequence
// number for the newest data that must be kept in the last level.
SequenceNumber GetKeepInLastLevelThroughSeqno() const {
return keep_in_last_level_through_seqno_;
}
// mark (or clear) all files that are being compacted
void MarkFilesBeingCompacted(bool being_compacted) const;
// penultimate level. The safe key range is populated by
// `PopulatePenultimateLevelOutputRange()`.
// Which could potentially disable all penultimate level output.
static int EvaluatePenultimateLevel(const VersionStorageInfo* vstorage,
const ImmutableOptions& immutable_options,
const int start_level,
const int output_level);
private:
void SetInputVersion(Version* input_version);
Status InitInputTableProperties();
// mark (or clear) all files that are being compacted
void MarkFilesBeingCompacted(bool mark_as_compacted);
// get the smallest and largest key present in files to be compacted
static void GetBoundaryKeys(VersionStorageInfo* vstorage,
const std::vector<CompactionInputFiles>& inputs,
@@ -502,17 +453,10 @@ class Compaction {
InternalKey* smallest_key, InternalKey* largest_key,
int exclude_level = -1);
// populate proximal level output range, which will be used to determine if
// a key is safe to output to the proximal level (details see
// `Compaction::WithinProximalLevelOutputRange()`.
void PopulateProximalLevelOutputRange();
// If oldest snapshot is specified at Compaction construction time, we have
// an opportunity to optimize inputs for compaction iterator for this case:
// When a standalone range deletion file on the start level is recognized and
// can be determined to completely shadow some input files on non-start level.
// These files will be filtered out and later not feed to compaction iterator.
void FilterInputsForCompactionIterator();
// populate penultimate level output range, which will be used to determine if
// a key is safe to output to the penultimate level (details see
// `Compaction::WithinPenultimateLevelOutputRange()`.
void PopulatePenultimateLevelOutputRange();
// Get the atomic file boundaries for all files in the compaction. Necessary
// in order to avoid the scenario described in
@@ -550,7 +494,7 @@ class Compaction {
const uint32_t output_path_id_;
CompressionType output_compression_;
CompressionOptions output_compression_opts_;
Temperature output_temperature_override_;
Temperature output_temperature_;
// If true, then the compaction can be done by simply deleting input files.
const bool deletion_compaction_;
// should it split the output file using the compact cursor?
@@ -564,30 +508,12 @@ class Compaction {
// Compaction input files organized by level. Constant after construction
const std::vector<CompactionInputFiles> inputs_;
// All files from inputs_ that are not filtered and will be fed to compaction
// iterator, organized more closely in memory.
// A copy of inputs_, organized more closely in memory
autovector<LevelFilesBrief, 2> input_levels_;
// State used to check for number of overlapping grandparent files
// (grandparent == "output_level_ + 1")
std::vector<FileMetaData*> grandparents_;
// The earliest snapshot and snapshot checker at compaction picking time.
// These fields are only set for deletion triggered compactions picked in
// universal compaction. And when user-defined timestamp is not enabled.
// It will be used to possibly filter out some non start level input files.
std::optional<SequenceNumber> earliest_snapshot_;
const SnapshotChecker* snapshot_checker_;
// Markers for which non start level input files are filtered out if
// applicable. Only applicable if earliest_snapshot_ is provided and input
// start level has a standalone range deletion file. Filtered files are
// tracked in `filtered_input_levels_`.
std::vector<std::vector<bool>> non_start_level_input_files_filtered_;
// All files from inputs_ that are filtered.
std::vector<std::vector<FileMetaData*>> filtered_input_levels_;
const double score_; // score that was used to pick this compaction.
// Is this compaction creating a file in the bottom most level?
@@ -632,24 +558,22 @@ class Compaction {
// Blob garbage collection age cutoff.
double blob_garbage_collection_age_cutoff_;
SequenceNumber keep_in_last_level_through_seqno_ = kMaxSequenceNumber;
// only set when per_key_placement feature is enabled, -1 (kInvalidLevel)
// means not supported.
const int proximal_level_;
const int penultimate_level_;
// Key range for proximal level output
// Key range for penultimate level output
// includes timestamp if user-defined timestamp is enabled.
// proximal_output_range_type_ shows the range type
InternalKey proximal_level_smallest_;
InternalKey proximal_level_largest_;
ProximalOutputRangeType proximal_output_range_type_ =
ProximalOutputRangeType::kNotSupported;
// penultimate_output_range_type_ shows the range type
InternalKey penultimate_level_smallest_;
InternalKey penultimate_level_largest_;
PenultimateOutputRangeType penultimate_output_range_type_ =
PenultimateOutputRangeType::kNotSupported;
};
#ifndef NDEBUG
// Helper struct only for tests, which contains the data to decide if a key
// should be output to the proximal level.
// should be output to the penultimate level.
// TODO: remove this when the public feature knob is available
struct PerKeyPlacementContext {
const int level;
@@ -657,16 +581,16 @@ struct PerKeyPlacementContext {
const Slice value;
const SequenceNumber seq_num;
bool& output_to_proximal_level;
bool& output_to_penultimate_level;
PerKeyPlacementContext(int _level, Slice _key, Slice _value,
SequenceNumber _seq_num,
bool& _output_to_proximal_level)
bool& _output_to_penultimate_level)
: level(_level),
key(_key),
value(_value),
seq_num(_seq_num),
output_to_proximal_level(_output_to_proximal_level) {}
output_to_penultimate_level(_output_to_penultimate_level) {}
};
#endif /* !NDEBUG */
+130 -64
View File
@@ -25,10 +25,9 @@ namespace ROCKSDB_NAMESPACE {
CompactionIterator::CompactionIterator(
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
SequenceNumber earliest_snapshot,
SequenceNumber earliest_write_conflict_snapshot,
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
Env* env, bool report_detailed_time,
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
CompactionRangeDelAggregator* range_del_agg,
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
bool enforce_single_del_contracts,
@@ -38,24 +37,26 @@ CompactionIterator::CompactionIterator(
const std::atomic<bool>* shutting_down,
const std::shared_ptr<Logger> info_log,
const std::string* full_history_ts_low,
std::optional<SequenceNumber> preserve_seqno_min)
const SequenceNumber preserve_time_min_seqno,
const SequenceNumber preclude_last_level_min_seqno)
: CompactionIterator(
input, cmp, merge_helper, last_sequence, snapshots, earliest_snapshot,
input, cmp, merge_helper, last_sequence, snapshots,
earliest_write_conflict_snapshot, job_snapshot, snapshot_checker, env,
report_detailed_time, range_del_agg, blob_file_builder,
allow_data_in_errors, enforce_single_del_contracts,
report_detailed_time, expect_valid_internal_key, range_del_agg,
blob_file_builder, allow_data_in_errors, enforce_single_del_contracts,
manual_compaction_canceled,
compaction ? std::make_unique<RealCompaction>(compaction) : nullptr,
std::unique_ptr<CompactionProxy>(
compaction ? new RealCompaction(compaction) : nullptr),
must_count_input_entries, compaction_filter, shutting_down, info_log,
full_history_ts_low, preserve_seqno_min) {}
full_history_ts_low, preserve_time_min_seqno,
preclude_last_level_min_seqno) {}
CompactionIterator::CompactionIterator(
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
SequenceNumber /*last_sequence*/, std::vector<SequenceNumber>* snapshots,
SequenceNumber earliest_snapshot,
SequenceNumber earliest_write_conflict_snapshot,
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
Env* env, bool report_detailed_time,
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
CompactionRangeDelAggregator* range_del_agg,
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
bool enforce_single_del_contracts,
@@ -65,7 +66,8 @@ CompactionIterator::CompactionIterator(
const std::atomic<bool>* shutting_down,
const std::shared_ptr<Logger> info_log,
const std::string* full_history_ts_low,
std::optional<SequenceNumber> preserve_seqno_min)
const SequenceNumber preserve_time_min_seqno,
const SequenceNumber preclude_last_level_min_seqno)
: input_(input, cmp, must_count_input_entries),
cmp_(cmp),
merge_helper_(merge_helper),
@@ -76,18 +78,22 @@ CompactionIterator::CompactionIterator(
env_(env),
clock_(env_->GetSystemClock().get()),
report_detailed_time_(report_detailed_time),
expect_valid_internal_key_(expect_valid_internal_key),
range_del_agg_(range_del_agg),
blob_file_builder_(blob_file_builder),
compaction_(std::move(compaction)),
compaction_filter_(compaction_filter),
shutting_down_(shutting_down),
manual_compaction_canceled_(manual_compaction_canceled),
bottommost_level_(compaction_ && compaction_->bottommost_level() &&
!compaction_->allow_ingest_behind()),
bottommost_level_(!compaction_ ? false
: compaction_->bottommost_level() &&
!compaction_->allow_ingest_behind()),
// snapshots_ cannot be nullptr, but we will assert later in the body of
// the constructor.
visible_at_tip_(snapshots_ ? snapshots_->empty() : false),
earliest_snapshot_(earliest_snapshot),
earliest_snapshot_(!snapshots_ || snapshots_->empty()
? kMaxSequenceNumber
: snapshots_->at(0)),
info_log_(info_log),
allow_data_in_errors_(allow_data_in_errors),
enforce_single_del_contracts_(enforce_single_del_contracts),
@@ -104,9 +110,10 @@ CompactionIterator::CompactionIterator(
current_key_committed_(false),
cmp_with_history_ts_low_(0),
level_(compaction_ == nullptr ? 0 : compaction_->level()),
preserve_seqno_after_(preserve_seqno_min.value_or(earliest_snapshot)) {
preserve_time_min_seqno_(preserve_time_min_seqno),
preclude_last_level_min_seqno_(preclude_last_level_min_seqno) {
assert(snapshots_ != nullptr);
assert(preserve_seqno_after_ <= earliest_snapshot_);
assert(preserve_time_min_seqno_ <= preclude_last_level_min_seqno_);
if (compaction_ != nullptr) {
level_ptrs_ = std::vector<size_t>(compaction_->number_levels(), 0);
@@ -159,7 +166,6 @@ void CompactionIterator::Next() {
// MergeUntil stops when it encounters a corrupt key and does not
// include them in the result, so we expect the keys here to be valid.
if (!s.ok()) {
// FIXME: should fail compaction after this fatal logging.
ROCKS_LOG_FATAL(
info_log_, "Invalid ikey %s in compaction. %s",
allow_data_in_errors_ ? key_.ToString(true).c_str() : "hidden",
@@ -463,9 +469,18 @@ void CompactionIterator::NextFromInput() {
if (!pik_status.ok()) {
iter_stats_.num_input_corrupt_records++;
// Always fail compaction when encountering corrupted internal keys
status_ = pik_status;
return;
// If `expect_valid_internal_key_` is false, return the corrupted key
// and let the caller decide what to do with it.
if (expect_valid_internal_key_) {
status_ = pik_status;
return;
}
key_ = current_key_.SetInternalKey(key_);
has_current_user_key_ = false;
current_user_key_sequence_ = kMaxSequenceNumber;
current_user_key_snapshot_ = 0;
validity_info_.SetValid(ValidContext::kParseKeyError);
break;
}
TEST_SYNC_POINT_CALLBACK("CompactionIterator:ProcessKV", &ikey_);
if (is_range_del_) {
@@ -632,8 +647,7 @@ void CompactionIterator::NextFromInput() {
} else if (ikey_.type == kTypeSingleDeletion) {
// We can compact out a SingleDelete if:
// 1) We encounter the corresponding PUT -OR- we know that this key
// doesn't appear past this output level and we are not in
// ingest_behind mode.
// doesn't appear past this output level
// =AND=
// 2) We've already returned a record in this snapshot -OR-
// there are no earlier earliest_write_conflict_snapshot.
@@ -722,8 +736,6 @@ void CompactionIterator::NextFromInput() {
"CompactionIterator::NextFromInput:SingleDelete:1",
const_cast<Compaction*>(c));
if (last_key_seq_zeroed_) {
// Drop SD and the next key since they are both in the last
// snapshot (since last key has seqno zeroed).
++iter_stats_.num_record_drop_hidden;
++iter_stats_.num_record_drop_obsolete;
assert(bottommost_level_);
@@ -834,7 +846,7 @@ void CompactionIterator::NextFromInput() {
// iteration. If the next key is corrupt, we return before the
// comparison, so the value of has_current_user_key does not matter.
has_current_user_key_ = false;
if (compaction_ != nullptr && !compaction_->allow_ingest_behind() &&
if (compaction_ != nullptr &&
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
&level_ptrs_) &&
@@ -847,9 +859,6 @@ void CompactionIterator::NextFromInput() {
++iter_stats_.num_optimized_del_drop_obsolete;
}
} else if (last_key_seq_zeroed_) {
// Sequence number zeroing requires bottommost_level_, which is
// false with ingest_behind.
assert(!compaction_->allow_ingest_behind());
// Skip.
++iter_stats_.num_record_drop_hidden;
++iter_stats_.num_record_drop_obsolete;
@@ -863,10 +872,9 @@ void CompactionIterator::NextFromInput() {
if (Valid()) {
at_next_ = true;
}
} else if (last_sequence != kMaxSequenceNumber &&
(last_snapshot == current_user_key_snapshot_ ||
} else if (last_snapshot == current_user_key_snapshot_ ||
(last_snapshot > 0 &&
last_snapshot < current_user_key_snapshot_)) {
// rule (A):
// If the earliest snapshot is which this key is visible in
// is the same as the visibility of a previous instance of the
// same key, then this kv is not visible in any snapshot.
@@ -875,15 +883,6 @@ void CompactionIterator::NextFromInput() {
// Note: Dropping this key will not affect TransactionDB write-conflict
// checking since there has already been a record returned for this key
// in this snapshot.
// When ingest_behind is enabled, it's ok that we drop an overwritten
// Delete here. The overwritting key still covers whatever that will be
// ingested. Note that we will not drop SingleDelete here as SingleDelte
// is handled entirely in its own if clause. This is important, see
// example: from new to old: SingleDelete_1, PUT_1, SingleDelete_2, PUT_2,
// where all operations are on the same key and PUT_2 is ingested with
// ingest_behind=true. If SingleDelete_2 is dropped due to being compacted
// together with PUT_1, and then PUT_1 is compacted away together with
// SingleDelete_1, PUT_2 can incorrectly becomes visible.
if (last_sequence < current_user_key_sequence_) {
ROCKS_LOG_FATAL(info_log_,
"key %s, last_sequence (%" PRIu64
@@ -893,13 +892,12 @@ void CompactionIterator::NextFromInput() {
assert(false);
}
++iter_stats_.num_record_drop_hidden;
++iter_stats_.num_record_drop_hidden; // rule (A)
AdvanceInputIter();
} else if (compaction_ != nullptr &&
(ikey_.type == kTypeDeletion ||
(ikey_.type == kTypeDeletionWithTimestamp &&
cmp_with_history_ts_low_ < 0)) &&
!compaction_->allow_ingest_behind() &&
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
&level_ptrs_)) {
@@ -935,13 +933,11 @@ void CompactionIterator::NextFromInput() {
(ikey_.type == kTypeDeletionWithTimestamp &&
cmp_with_history_ts_low_ < 0)) &&
bottommost_level_) {
assert(compaction_);
assert(!compaction_->allow_ingest_behind()); // bottommost_level_ is true
// Handle the case where we have a delete key at the bottom most level
// We can skip outputting the key iff there are no subsequent puts for
// this key
assert(compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
&level_ptrs_));
assert(!compaction_ || compaction_->KeyNotExistsBeyondOutputLevel(
ikey_.user_key, &level_ptrs_));
ParsedInternalKey next_ikey;
AdvanceInputIter();
#ifndef NDEBUG
@@ -983,12 +979,6 @@ void CompactionIterator::NextFromInput() {
(compaction_ != nullptr &&
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
&level_ptrs_)))) {
// FIXME: it's possible that we are setting sequence number to 0 as
// preferred sequence number here. If cf_ingest_behind is enabled, this
// may fail ingestions since they expect all keys above the last level
// to have non-zero sequence number. We should probably not allow seqno
// zeroing here.
//
// This section that attempts to swap preferred sequence number will not
// be invoked if this is a CompactionIterator created for flush, since
// `compaction_` will be nullptr and it's not bottommost either.
@@ -1028,6 +1018,7 @@ void CompactionIterator::NextFromInput() {
} else {
if (ikey_.sequence != 0) {
iter_stats_.num_timed_put_swap_preferred_seqno++;
saved_seq_for_penul_check_ = ikey_.sequence;
ikey_.sequence = preferred_seqno;
}
ikey_.type = kTypeValue;
@@ -1120,15 +1111,17 @@ void CompactionIterator::NextFromInput() {
}
}
if (status_.ok()) {
if (!Valid() && IsShuttingDown()) {
status_ = Status::ShutdownInProgress();
} else if (IsPausingManualCompaction()) {
status_ = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
} else if (!input_.Valid() && input_.status().IsCorruption()) {
// Propagate corruption status from memtable iterator
status_ = input_.status();
}
if (!Valid() && IsShuttingDown()) {
status_ = Status::ShutdownInProgress();
}
if (IsPausingManualCompaction()) {
status_ = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
}
// Propagate corruption status from memtable itereator
if (!input_.Valid() && input_.status().IsCorruption()) {
status_ = input_.status();
}
}
@@ -1266,6 +1259,71 @@ void CompactionIterator::GarbageCollectBlobIfNeeded() {
}
}
void CompactionIterator::DecideOutputLevel() {
assert(compaction_->SupportsPerKeyPlacement());
output_to_penultimate_level_ = false;
// if the key is newer than the cutoff sequence or within the earliest
// snapshot, it should output to the penultimate level.
if (ikey_.sequence > preclude_last_level_min_seqno_ ||
ikey_.sequence > earliest_snapshot_) {
output_to_penultimate_level_ = true;
}
#ifndef NDEBUG
// Could be overridden by unittest
PerKeyPlacementContext context(level_, ikey_.user_key, value_, ikey_.sequence,
output_to_penultimate_level_);
TEST_SYNC_POINT_CALLBACK("CompactionIterator::PrepareOutput.context",
&context);
if (ikey_.sequence > earliest_snapshot_) {
output_to_penultimate_level_ = true;
}
#endif // NDEBUG
// saved_seq_for_penul_check_ is populated in `NextFromInput` when the
// entry's sequence number is non zero and validity context for output this
// entry is kSwapPreferredSeqno for use in `DecideOutputLevel`. It should be
// cleared out here unconditionally. Otherwise, it may end up getting consumed
// incorrectly by a different entry.
SequenceNumber seq_for_range_check =
(saved_seq_for_penul_check_.has_value() &&
saved_seq_for_penul_check_.value() != kMaxSequenceNumber)
? saved_seq_for_penul_check_.value()
: ikey_.sequence;
saved_seq_for_penul_check_ = std::nullopt;
ParsedInternalKey ikey_for_range_check = ikey_;
if (seq_for_range_check != ikey_.sequence) {
ikey_for_range_check.sequence = seq_for_range_check;
}
if (output_to_penultimate_level_) {
// If it's decided to output to the penultimate level, but unsafe to do so,
// still output to the last level. For example, moving the data from a lower
// level to a higher level outside of the higher-level input key range is
// considered unsafe, because the key may conflict with higher-level SSTs
// not from this compaction.
// TODO: add statistic for declined output_to_penultimate_level
bool safe_to_penultimate_level =
compaction_->WithinPenultimateLevelOutputRange(ikey_for_range_check);
if (!safe_to_penultimate_level) {
output_to_penultimate_level_ = false;
// It could happen when disable/enable `last_level_temperature` while
// holding a snapshot. When `last_level_temperature` is not set
// (==kUnknown), the data newer than any snapshot is pushed to the last
// level, but when the per_key_placement feature is enabled on the fly,
// the data later than the snapshot has to be moved to the penultimate
// level, which may or may not be safe. So the user needs to make sure all
// snapshot is released before enabling `last_level_temperature` feature
// We will migrate the feature to `last_level_temperature` and maybe make
// it not dynamically changeable.
if (seq_for_range_check > earliest_snapshot_) {
status_ = Status::Corruption(
"Unsafe to store Seq later than snapshot in the last level if "
"per_key_placement is enabled");
}
}
}
}
void CompactionIterator::PrepareOutput() {
if (Valid()) {
if (LIKELY(!is_range_del_)) {
@@ -1274,6 +1332,13 @@ void CompactionIterator::PrepareOutput() {
} else if (ikey_.type == kTypeBlobIndex) {
GarbageCollectBlobIfNeeded();
}
// For range del sentinel, we don't use it to cut files for bottommost
// compaction. So it should not make a difference which output level we
// decide.
if (compaction_ != nullptr && compaction_->SupportsPerKeyPlacement()) {
DecideOutputLevel();
}
}
// Zeroing out the sequence number leads to better compression.
@@ -1287,11 +1352,12 @@ void CompactionIterator::PrepareOutput() {
//
// Can we do the same for levels above bottom level as long as
// KeyNotExistsBeyondOutputLevel() return true?
if (Valid() && bottommost_level_ &&
if (Valid() && compaction_ != nullptr &&
!compaction_->allow_ingest_behind() && bottommost_level_ &&
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
ikey_.type != kTypeMerge && current_key_committed_ &&
ikey_.sequence <= preserve_seqno_after_ && !is_range_del_) {
assert(compaction_ != nullptr && !compaction_->allow_ingest_behind());
!output_to_penultimate_level_ &&
ikey_.sequence < preserve_time_min_seqno_ && !is_range_del_) {
if (ikey_.type == kTypeDeletion ||
(ikey_.type == kTypeSingleDeletion && timestamp_size_ == 0)) {
ROCKS_LOG_FATAL(
+84 -65
View File
@@ -117,6 +117,10 @@ class CompactionIterator {
virtual const Compaction* real_compaction() const = 0;
virtual bool SupportsPerKeyPlacement() const = 0;
// `key` includes timestamp if user-defined timestamp is enabled.
virtual bool WithinPenultimateLevelOutputRange(
const ParsedInternalKey&) const = 0;
};
class RealCompaction : public CompactionProxy {
@@ -124,6 +128,8 @@ class CompactionIterator {
explicit RealCompaction(const Compaction* compaction)
: compaction_(compaction) {
assert(compaction_);
assert(compaction_->immutable_options());
assert(compaction_->mutable_cf_options());
}
int level() const override { return compaction_->level(); }
@@ -145,12 +151,11 @@ class CompactionIterator {
}
bool allow_ingest_behind() const override {
return compaction_->immutable_options().cf_allow_ingest_behind ||
compaction_->immutable_options().allow_ingest_behind;
return compaction_->immutable_options()->allow_ingest_behind;
}
bool allow_mmap_reads() const override {
return compaction_->immutable_options().allow_mmap_reads;
return compaction_->immutable_options()->allow_mmap_reads;
}
bool enable_blob_garbage_collection() const override {
@@ -162,7 +167,7 @@ class CompactionIterator {
}
uint64_t blob_compaction_readahead_size() const override {
return compaction_->mutable_cf_options().blob_compaction_readahead_size;
return compaction_->mutable_cf_options()->blob_compaction_readahead_size;
}
const Version* input_version() const override {
@@ -179,31 +184,28 @@ class CompactionIterator {
return compaction_->SupportsPerKeyPlacement();
}
// Check if key is within penultimate level output range, to see if it's
// safe to output to the penultimate level for per_key_placement feature.
// `key` includes timestamp if user-defined timestamp is enabled.
bool WithinPenultimateLevelOutputRange(
const ParsedInternalKey& ikey) const override {
return compaction_->WithinPenultimateLevelOutputRange(ikey);
}
private:
const Compaction* compaction_;
};
// @param must_count_input_entries Controls input entry counting accuracy vs
// performance:
// - If true: `NumInputEntryScanned()` always returns the exact count of
// input keys
// scanned. The iterator will use sequential `Next()` calls instead of
// `Seek()` to maintain count accuracy as `Seek()` will not count the
// skipped input entries, which is slower but guarantees correctness.
// - If false: `NumInputEntryScanned()` returns the count only if no
// `Seek()` operations
// were performed on the input iterator. When compaction filters request
// skipping ranges of keys or other optimizations trigger seek operations,
// the count becomes unreliable. Always call `HasNumInputEntryScanned()`
// first to verify if the count is accurate before using
// `NumInputEntryScanned()`.
// @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,
SequenceNumber earliest_snapshot,
SequenceNumber earliest_write_conflict_snapshot,
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
Env* env, bool report_detailed_time,
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
CompactionRangeDelAggregator* range_del_agg,
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
bool enforce_single_del_contracts,
@@ -213,29 +215,28 @@ class CompactionIterator {
const std::atomic<bool>* shutting_down = nullptr,
const std::shared_ptr<Logger> info_log = nullptr,
const std::string* full_history_ts_low = nullptr,
std::optional<SequenceNumber> preserve_seqno_min = {});
const SequenceNumber preserve_time_min_seqno = kMaxSequenceNumber,
const SequenceNumber preclude_last_level_min_seqno = kMaxSequenceNumber);
// Constructor with custom CompactionProxy, used for tests.
CompactionIterator(InternalIterator* input, const Comparator* cmp,
MergeHelper* merge_helper, SequenceNumber last_sequence,
std::vector<SequenceNumber>* snapshots,
SequenceNumber earliest_snapshot,
SequenceNumber earliest_write_conflict_snapshot,
SequenceNumber job_snapshot,
const SnapshotChecker* snapshot_checker, Env* env,
bool report_detailed_time,
CompactionRangeDelAggregator* range_del_agg,
BlobFileBuilder* blob_file_builder,
bool allow_data_in_errors,
bool enforce_single_del_contracts,
const std::atomic<bool>& manual_compaction_canceled,
std::unique_ptr<CompactionProxy> compaction,
bool must_count_input_entries,
const CompactionFilter* compaction_filter = nullptr,
const std::atomic<bool>* shutting_down = nullptr,
const std::shared_ptr<Logger> info_log = nullptr,
const std::string* full_history_ts_low = nullptr,
std::optional<SequenceNumber> preserve_seqno_min = {});
CompactionIterator(
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
SequenceNumber earliest_write_conflict_snapshot,
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
CompactionRangeDelAggregator* range_del_agg,
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
bool enforce_single_del_contracts,
const std::atomic<bool>& manual_compaction_canceled,
std::unique_ptr<CompactionProxy> compaction,
bool must_count_input_entries,
const CompactionFilter* compaction_filter = nullptr,
const std::atomic<bool>* shutting_down = nullptr,
const std::shared_ptr<Logger> info_log = nullptr,
const std::string* full_history_ts_low = nullptr,
const SequenceNumber preserve_time_min_seqno = kMaxSequenceNumber,
const SequenceNumber preclude_last_level_min_seqno = kMaxSequenceNumber);
~CompactionIterator();
@@ -265,21 +266,12 @@ class CompactionIterator {
}
const CompactionIterationStats& iter_stats() const { return iter_stats_; }
bool HasNumInputEntryScanned() const { return input_.HasNumItered(); }
// This method should only be used when `HasNumInputEntryScanned()` returns
// true, unless `must_count_input_entries=true` was specified during iterator
// creation (which ensures the count is always accurate).
uint64_t NumInputEntryScanned() const { return input_.NumItered(); }
// Returns true if the current valid key was already scanned/counted during
// a lookahead operation in a previous iteration.
//
// REQUIRED: Valid() must be true
bool IsCurrentKeyAlreadyScanned() const {
assert(Valid());
return at_next_ || merge_out_iter_.Valid();
// If the current key should be placed on penultimate level, only valid if
// per_key_placement is supported
bool output_to_penultimate_level() const {
return output_to_penultimate_level_;
}
Status InputStatus() const { return input_.status(); }
bool IsDeleteRangeSentinelKey() const { return is_range_del_; }
@@ -291,6 +283,10 @@ class CompactionIterator {
// Do final preparations before presenting the output to the callee.
void PrepareOutput();
// Decide the current key should be output to the last level or penultimate
// level, only call for compaction supports per key placement
void DecideOutputLevel();
// Passes the output value to the blob file builder (if any), and replaces it
// with the corresponding blob reference if it has been actually written to a
// blob file (i.e. if it passed the value size check). Returns true if the
@@ -372,6 +368,7 @@ class CompactionIterator {
Env* env_;
SystemClock* clock_;
const bool report_detailed_time_;
const bool expect_valid_internal_key_;
CompactionRangeDelAggregator* range_del_agg_;
BlobFileBuilder* blob_file_builder_;
std::unique_ptr<CompactionProxy> compaction_;
@@ -441,15 +438,23 @@ class CompactionIterator {
// NextFromInput()).
ParsedInternalKey ikey_;
// Stores whether current_user_key_ is valid. If so, current_user_key_
// stores the user key of the last key seen by the iterator.
// If false, treat the next key to read as a new user key.
// When a kTypeValuePreferredSeqno entry's preferred seqno is safely swapped
// in in this compaction, this field saves its original sequence number for
// range checking whether it's safe to be placed on the penultimate level.
// This is to ensure when such an entry happens to be the right boundary of
// penultimate safe range, it won't get excluded because with the preferred
// seqno swapped in, it's now larger than the right boundary (itself before
// the swap). This is safe to do, because preferred seqno is swapped in only
// when no entries with the same user key exist on lower levels and this entry
// is already visible in the earliest snapshot.
std::optional<SequenceNumber> saved_seq_for_penul_check_ = kMaxSequenceNumber;
// Stores whether ikey_.user_key is valid. If set to false, the user key is
// not compared against the current key in the underlying iterator.
bool has_current_user_key_ = false;
// If false, the iterator holds a copy of the current compaction iterator
// output (or current key in the underlying iterator during NextFromInput()).
bool at_next_ = false;
// A copy of the current internal key.
IterKey current_key_;
Slice current_user_key_;
std::string curr_ts_;
@@ -459,9 +464,8 @@ class CompactionIterator {
// True if the iterator has already returned a record for the current key.
bool has_outputted_key_ = false;
// Truncate the value of the next key and output it without applying any
// compaction rules. This is an optimization for outputting a put after
// a single delete. See more in `NextFromInput()` under Optimization 3.
// truncated the value of the next key and output it without applying any
// compaction rules. This is used for outputting a put after a single delete.
bool clear_and_output_next_key_ = false;
MergeOutputIterator merge_out_iter_;
@@ -501,8 +505,17 @@ class CompactionIterator {
// just been zeroed out during bottommost compaction.
bool last_key_seq_zeroed_{false};
// Max seqno that can be zeroed out at last level (various reasons)
const SequenceNumber preserve_seqno_after_ = kMaxSequenceNumber;
// True if the current key should be output to the penultimate level if
// possible, compaction logic makes the final decision on which level to
// output to.
bool output_to_penultimate_level_{false};
// min seqno for preserving the time information.
const SequenceNumber preserve_time_min_seqno_ = kMaxSequenceNumber;
// min seqno to preclude the data from the last level, if the key seqno larger
// than this, it will be output to penultimate level
const SequenceNumber preclude_last_level_min_seqno_ = kMaxSequenceNumber;
void AdvanceInputIter() { input_.Next(); }
@@ -525,12 +538,18 @@ class CompactionIterator {
inline bool CompactionIterator::DefinitelyInSnapshot(SequenceNumber seq,
SequenceNumber snapshot) {
return DataIsDefinitelyInSnapshot(seq, snapshot, snapshot_checker_);
return ((seq) <= (snapshot) &&
(snapshot_checker_ == nullptr ||
LIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) ==
SnapshotCheckerResult::kInSnapshot)));
}
inline bool CompactionIterator::DefinitelyNotInSnapshot(
SequenceNumber seq, SequenceNumber snapshot) {
return DataIsDefinitelyNotInSnapshot(seq, snapshot, snapshot_checker_);
return ((seq) > (snapshot) ||
(snapshot_checker_ != nullptr &&
UNLIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) ==
SnapshotCheckerResult::kNotInSnapshot)));
}
} // namespace ROCKSDB_NAMESPACE
+131 -29
View File
@@ -192,6 +192,11 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
return supports_per_key_placement;
}
bool WithinPenultimateLevelOutputRange(
const ParsedInternalKey& key) const override {
return (!key.user_key.starts_with("unsafe_pb"));
}
bool key_not_exists_beyond_output_level = false;
bool is_bottommost_level = false;
@@ -291,10 +296,9 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
iter_->SeekToFirst();
c_iter_.reset(new CompactionIterator(
iter_.get(), cmp_, merge_helper_.get(), last_sequence, &snapshots_,
snapshots_.empty() ? kMaxSequenceNumber : snapshots_.at(0),
earliest_write_conflict_snapshot, kMaxSequenceNumber,
snapshot_checker_.get(), Env::Default(),
false /* report_detailed_time */, range_del_agg_.get(),
false /* report_detailed_time */, false, range_del_agg_.get(),
nullptr /* blob_file_builder */, true /*allow_data_in_errors*/,
true /*enforce_single_del_contracts*/,
/*manual_compaction_canceled=*/kManualCompactionCanceledFalse_,
@@ -374,7 +378,8 @@ TEST_P(CompactionIteratorTest, EmptyResult) {
ASSERT_FALSE(c_iter_->Valid());
}
// If there is a corruption after a single deletion, the compaction should fail.
// If there is a corruption after a single deletion, the corrupted key should
// be preserved.
TEST_P(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
test::KeyStr("a", 3, kTypeValue, true),
@@ -385,10 +390,14 @@ TEST_P(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
ASSERT_EQ(test::KeyStr("a", 5, kTypeSingleDeletion),
c_iter_->key().ToString());
c_iter_->Next();
// The iterator should now fail when encountering the corrupted key
ASSERT_TRUE(c_iter_->Valid());
ASSERT_EQ(test::KeyStr("a", 3, kTypeValue, true), c_iter_->key().ToString());
c_iter_->Next();
ASSERT_TRUE(c_iter_->Valid());
ASSERT_EQ(test::KeyStr("b", 10, kTypeValue), c_iter_->key().ToString());
c_iter_->Next();
ASSERT_OK(c_iter_->status());
ASSERT_FALSE(c_iter_->Valid());
ASSERT_FALSE(c_iter_->status().ok());
ASSERT_TRUE(c_iter_->status().IsCorruption());
}
// Tests compatibility of TimedPut and SingleDelete. TimedPut should act as if
@@ -823,15 +832,124 @@ TEST_P(CompactionIteratorTest, ConvertToPutAtBottom) {
true /*bottomost_level*/);
}
TEST_P(CompactionIteratorTest, ZeroSeqOfKeyAndSnapshot) {
AddSnapshot(0);
const std::vector<std::string> input_keys = {
test::KeyStr("a", 0, kTypeValue), test::KeyStr("b", 0, kTypeValue)};
const std::vector<std::string> input_values = {"a1", "b1"};
RunTest(input_keys, input_values, input_keys, input_values);
INSTANTIATE_TEST_CASE_P(CompactionIteratorTestInstance, CompactionIteratorTest,
testing::Values(true, false));
class PerKeyPlacementCompIteratorTest : public CompactionIteratorTest {
public:
bool SupportsPerKeyPlacement() const override { return true; }
};
TEST_P(PerKeyPlacementCompIteratorTest, SplitLastLevelData) {
std::atomic_uint64_t latest_cold_seq = 0;
SyncPoint::GetInstance()->SetCallBack(
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
auto context = static_cast<PerKeyPlacementContext*>(arg);
context->output_to_penultimate_level =
context->seq_num > latest_cold_seq;
});
SyncPoint::GetInstance()->EnableProcessing();
latest_cold_seq = 5;
InitIterators(
{test::KeyStr("a", 7, kTypeValue), test::KeyStr("b", 6, kTypeValue),
test::KeyStr("c", 5, kTypeValue)},
{"vala", "valb", "valc"}, {}, {}, kMaxSequenceNumber, kMaxSequenceNumber,
nullptr, nullptr, true);
c_iter_->SeekToFirst();
ASSERT_TRUE(c_iter_->Valid());
// the first 2 keys are hot, which should has
// `output_to_penultimate_level()==true` and seq num not zeroed out
ASSERT_EQ(test::KeyStr("a", 7, kTypeValue), c_iter_->key().ToString());
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
c_iter_->Next();
ASSERT_TRUE(c_iter_->Valid());
ASSERT_EQ(test::KeyStr("b", 6, kTypeValue), c_iter_->key().ToString());
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
c_iter_->Next();
ASSERT_TRUE(c_iter_->Valid());
// `a` is cold data, which should be output to bottommost
ASSERT_EQ(test::KeyStr("c", 0, kTypeValue), c_iter_->key().ToString());
ASSERT_FALSE(c_iter_->output_to_penultimate_level());
c_iter_->Next();
ASSERT_OK(c_iter_->status());
ASSERT_FALSE(c_iter_->Valid());
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
INSTANTIATE_TEST_CASE_P(CompactionIteratorTestInstance, CompactionIteratorTest,
TEST_P(PerKeyPlacementCompIteratorTest, SnapshotData) {
AddSnapshot(5);
InitIterators(
{test::KeyStr("a", 7, kTypeValue), test::KeyStr("b", 6, kTypeDeletion),
test::KeyStr("b", 5, kTypeValue)},
{"vala", "", "valb"}, {}, {}, kMaxSequenceNumber, kMaxSequenceNumber,
nullptr, nullptr, true);
c_iter_->SeekToFirst();
ASSERT_TRUE(c_iter_->Valid());
// The first key and the tombstone are within snapshot, which should output
// to the penultimate level (and seq num cannot be zeroed out).
ASSERT_EQ(test::KeyStr("a", 7, kTypeValue), c_iter_->key().ToString());
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
c_iter_->Next();
ASSERT_TRUE(c_iter_->Valid());
ASSERT_EQ(test::KeyStr("b", 6, kTypeDeletion), c_iter_->key().ToString());
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
c_iter_->Next();
ASSERT_TRUE(c_iter_->Valid());
// `a` is not protected by the snapshot, the sequence number is zero out and
// should output bottommost
ASSERT_EQ(test::KeyStr("b", 0, kTypeValue), c_iter_->key().ToString());
ASSERT_FALSE(c_iter_->output_to_penultimate_level());
c_iter_->Next();
ASSERT_OK(c_iter_->status());
ASSERT_FALSE(c_iter_->Valid());
}
TEST_P(PerKeyPlacementCompIteratorTest, ConflictWithSnapshot) {
std::atomic_uint64_t latest_cold_seq = 0;
SyncPoint::GetInstance()->SetCallBack(
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
auto context = static_cast<PerKeyPlacementContext*>(arg);
context->output_to_penultimate_level =
context->seq_num > latest_cold_seq;
});
SyncPoint::GetInstance()->EnableProcessing();
latest_cold_seq = 6;
AddSnapshot(5);
InitIterators({test::KeyStr("a", 7, kTypeValue),
test::KeyStr("unsafe_pb", 6, kTypeValue),
test::KeyStr("c", 5, kTypeValue)},
{"vala", "valb", "valc"}, {}, {}, kMaxSequenceNumber,
kMaxSequenceNumber, nullptr, nullptr, true);
c_iter_->SeekToFirst();
ASSERT_TRUE(c_iter_->Valid());
ASSERT_EQ(test::KeyStr("a", 7, kTypeValue), c_iter_->key().ToString());
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
// the 2nd key is unsafe to output_to_penultimate_level, but it's within
// snapshot so for per_key_placement feature it has to be outputted to the
// penultimate level. which is a corruption. We should never see
// such case as the data with seq num (within snapshot) should always come
// from higher compaction input level, which makes it safe to
// output_to_penultimate_level.
c_iter_->Next();
ASSERT_TRUE(c_iter_->status().IsCorruption());
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
INSTANTIATE_TEST_CASE_P(PerKeyPlacementCompIteratorTest,
PerKeyPlacementCompIteratorTest,
testing::Values(true, false));
// Tests how CompactionIterator work together with SnapshotChecker.
@@ -1727,22 +1845,6 @@ TEST_P(CompactionIteratorTsGcTest, SingleDeleteAllKeysOlderThanThreshold) {
}
}
TEST_P(CompactionIteratorTsGcTest, ZeroSeqOfKeyAndSnapshot) {
AddSnapshot(0);
std::string full_history_ts_low;
PutFixed64(&full_history_ts_low, std::numeric_limits<uint64_t>::max());
const std::vector<std::string> input_keys = {
test::KeyStr(101, "a", 0, kTypeValue),
test::KeyStr(102, "b", 0, kTypeValue)};
const std::vector<std::string> input_values = {"a1", "b1"};
RunTest(input_keys, input_values, input_keys, input_values,
/*last_committed_seq=*/kMaxSequenceNumber,
/*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
/*bottommost_level=*/false,
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
/*key_not_exists_beyond_output_level=*/false, &full_history_ts_low);
}
INSTANTIATE_TEST_CASE_P(CompactionIteratorTsGcTestInstance,
CompactionIteratorTsGcTest,
testing::Values(true, false));
File diff suppressed because it is too large. Load diff
+111 -329
View File
@@ -67,7 +67,7 @@ class SubcompactionState;
// if needed.
//
// CompactionJob has 2 main stats:
// 1. CompactionJobStats job_stats_
// 1. CompactionJobStats compaction_job_stats_
// CompactionJobStats is a public data structure which is part of Compaction
// event listener that rocksdb share the job stats with the user.
// Internally it's an aggregation of all the compaction_job_stats from each
@@ -81,7 +81,7 @@ class SubcompactionState;
// +------------------------+ |
// | CompactionJob | | +------------------------+
// | | | | SubcompactionState |
// | job_stats +-----+ | |
// | compaction_job_stats +-----+ | |
// | | +--------->| compaction_job_stats |
// | | | | |
// +------------------------+ | +------------------------+
@@ -98,13 +98,16 @@ class SubcompactionState;
// +--------->+ |
// +------------------------+
//
// 2. CompactionStatsFull internal_stats_
// 2. CompactionStatsFull compaction_stats_
// `CompactionStatsFull` is an internal stats about the compaction, which
// is eventually sent to `ColumnFamilyData::internal_stats_` and used for
// logging and public metrics.
// Internally, it's an aggregation of stats_ from each `SubcompactionState`.
// It has 2 parts, ordinary output level stats and the proximal level output
// stats.
// It has 2 parts, normal stats about the main compaction information and
// the penultimate level output stats.
// `SubcompactionState` maintains the CompactionOutputs for normal output and
// the penultimate level output if exists, the per_level stats is
// stored with the outputs.
// +---------------------------+
// | SubcompactionState |
// | |
@@ -116,15 +119,15 @@ class SubcompactionState;
// | | |
// | | +----------------------+ |
// +--------------------------------+ | | | CompactionOutputs | |
// | CompactionJob | | | | (proximal_level) | |
// | CompactionJob | | | | (penultimate_level) | |
// | | +--------->| stats_ | |
// | internal_stats_ | | | | +----------------------+ |
// | compaction_stats_ | | | | +----------------------+ |
// | +-------------------------+ | | | | |
// | |output_level_stats |------|----+ +---------------------------+
// | |stats (normal) |------|----+ +---------------------------+
// | +-------------------------+ | | |
// | | | |
// | +-------------------------+ | | | +---------------------------+
// | |proximal_level_stats |------+ | | SubcompactionState |
// | |penultimate_level_stats +------+ | | SubcompactionState |
// | +-------------------------+ | | | | |
// | | | | | +----------------------+ |
// | | | | | | CompactionOutputs | |
@@ -134,7 +137,7 @@ class SubcompactionState;
// | | |
// | | +----------------------+ |
// | | | CompactionOutputs | |
// | | | (proximal_level) | |
// | | | (penultimate_level) | |
// +--------->| stats_ | |
// | +----------------------+ |
// | |
@@ -142,27 +145,27 @@ class SubcompactionState;
class CompactionJob {
public:
CompactionJob(int job_id, Compaction* compaction,
const ImmutableDBOptions& db_options,
const MutableDBOptions& mutable_db_options,
const FileOptions& file_options, VersionSet* versions,
const std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
FSDirectory* db_directory, FSDirectory* output_directory,
FSDirectory* blob_output_directory, Statistics* stats,
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
JobContext* job_context, std::shared_ptr<Cache> table_cache,
EventLogger* event_logger, bool paranoid_file_checks,
bool measure_io_stats, const std::string& dbname,
CompactionJobStats* compaction_job_stats,
Env::Priority thread_pri,
const std::shared_ptr<IOTracer>& io_tracer,
const std::atomic<bool>& manual_compaction_canceled,
const std::string& db_id = "",
const std::string& db_session_id = "",
std::string full_history_ts_low = "", std::string trim_ts = "",
BlobFileCompletionCallback* blob_callback = nullptr,
int* bg_compaction_scheduled = nullptr,
int* bg_bottom_compaction_scheduled = nullptr);
CompactionJob(
int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
const MutableDBOptions& mutable_db_options,
const FileOptions& file_options, VersionSet* versions,
const std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
FSDirectory* db_directory, FSDirectory* output_directory,
FSDirectory* blob_output_directory, Statistics* stats,
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
std::vector<SequenceNumber> existing_snapshots,
SequenceNumber earliest_write_conflict_snapshot,
const SnapshotChecker* snapshot_checker, JobContext* job_context,
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
bool paranoid_file_checks, bool measure_io_stats,
const std::string& dbname, CompactionJobStats* compaction_job_stats,
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
const std::atomic<bool>& manual_compaction_canceled,
const std::string& db_id = "", const std::string& db_session_id = "",
std::string full_history_ts_low = "", std::string trim_ts = "",
BlobFileCompletionCallback* blob_callback = nullptr,
int* bg_compaction_scheduled = nullptr,
int* bg_bottom_compaction_scheduled = nullptr);
virtual ~CompactionJob();
@@ -173,24 +176,7 @@ class CompactionJob {
// REQUIRED: mutex held
// Prepare for the compaction by setting up boundaries for each subcompaction
// and organizing seqno <-> time info. `known_single_subcompact` is non-null
// if we already have a known single subcompaction, with optional key bounds
// (currently for executing a remote compaction).
//
// @param compaction_progress Previously saved compaction progress
// to resume from. If empty, compaction starts fresh from the
// beginning.
//
// @param compaction_progress_writer Writer for persisting
// subcompaction progress periodically during compaction
// execution. If nullptr, progress tracking is disabled and compaction
// cannot be resumed later.
void Prepare(
std::optional<std::pair<std::optional<Slice>, std::optional<Slice>>>
known_single_subcompact,
const CompactionProgress& compaction_progress = CompactionProgress{},
log::Writer* compaction_progress_writer = nullptr);
void Prepare();
// REQUIRED mutex not held
// Launch threads for each subcompaction and wait for them to finish. After
// that, verify table is usable and finally do bookkeeping to unify
@@ -201,25 +187,38 @@ class CompactionJob {
// Add compaction input/output to the current version
// Releases compaction file through Compaction::ReleaseCompactionFiles().
// Sets *compaction_released to true if compaction is released.
Status Install(bool* compaction_released);
Status Install(const MutableCFOptions& mutable_cf_options,
bool* compaction_released);
// Return the IO status
IOStatus io_status() const { return io_status_; }
protected:
void UpdateCompactionJobOutputStatsFromInternalStats(
const Status& status,
const InternalStats::CompactionStatsFull& internal_stats) const;
// Update the following stats in compaction_stats_.stats
// - num_input_files_in_non_output_levels
// - num_input_files_in_output_level
// - bytes_read_non_output_levels
// - bytes_read_output_level
// - num_input_records
// - bytes_read_blob
// - num_dropped_records
//
// @param num_input_range_del if non-null, will be set to the number of range
// deletion entries in this compaction input.
//
// Returns true iff compaction_stats_.stats.num_input_records and
// num_input_range_del are calculated successfully.
bool UpdateCompactionStats(uint64_t* num_input_range_del = nullptr);
void LogCompaction();
virtual void RecordCompactionIOStats();
void CleanupCompaction();
// Iterate through input and compact the kv-pairs.
// Call compaction filter. Then iterate through input and compact the
// kv-pairs
void ProcessKeyValueCompaction(SubcompactionState* sub_compact);
CompactionState* compact_;
InternalStats::CompactionStatsFull internal_stats_;
InternalStats::CompactionStatsFull compaction_stats_;
const ImmutableDBOptions& db_options_;
const MutableDBOptions mutable_db_options_copy_;
LogBuffer* log_buffer_;
@@ -232,38 +231,11 @@ class CompactionJob {
IOStatus io_status_;
CompactionJobStats* job_stats_;
CompactionJobStats* compaction_job_stats_;
private:
friend class CompactionJobTestBase;
// Collect the following stats from input files and table properties
// - num_input_files_in_non_output_levels
// - num_input_files_in_output_level
// - bytes_read_non_output_levels
// - bytes_read_output_level
// - num_input_records
// - bytes_read_blob
// - num_dropped_records
// and set them in internal_stats_.output_level_stats
//
// @param num_input_range_del if non-null, will be set to the number of range
// deletion entries in this compaction input.
//
// Returns true iff internal_stats_.output_level_stats.num_input_records and
// num_input_range_del are calculated successfully.
//
// This should be called only once for compactions (not per subcompaction)
bool UpdateInternalStatsFromInputFiles(
uint64_t* num_input_range_del = nullptr);
void UpdateCompactionJobInputStatsFromInternalStats(
const InternalStats::CompactionStatsFull& internal_stats,
uint64_t num_input_range_del) const;
Status VerifyInputRecordCount(uint64_t num_input_range_del) const;
Status VerifyOutputRecordCount() const;
// Generates a histogram representing potential divisions of key ranges from
// the input. It adds the starting and/or ending keys of certain input files
// to the working set and then finds the approximate size of data in between
@@ -271,10 +243,6 @@ class CompactionJob {
// consecutive groups such that each group has a similar size.
void GenSubcompactionBoundaries();
void MaybeAssignCompactionProgressAndWriter(
const CompactionProgress& compaction_progress,
log::Writer* compaction_progress_writer);
// Get the number of planned subcompactions based on max_subcompactions and
// extra reserved resources
uint64_t GetSubcompactionsLimit();
@@ -295,149 +263,24 @@ class CompactionJob {
// Release all reserved threads and update the compaction limits.
void ReleaseSubcompactionResources();
void InitializeCompactionRun();
void RunSubcompactions();
void UpdateTimingStats(uint64_t start_micros);
void RemoveEmptyOutputs();
bool HasNewBlobFiles() const;
Status CollectSubcompactionErrors();
Status SyncOutputDirectories();
Status VerifyOutputFiles();
void SetOutputTableProperties();
// Aggregates subcompaction output stats to internal stat, and aggregates
// subcompaction's compaction job stats to the whole entire surrounding
// compaction job stats.
void AggregateSubcompactionOutputAndJobStats();
Status VerifyCompactionRecordCounts(bool stats_built_from_input_table_prop,
uint64_t num_input_range_del);
void FinalizeCompactionRun(const Status& status,
bool stats_built_from_input_table_prop,
uint64_t num_input_range_del);
CompactionServiceJobStatus ProcessKeyValueCompactionWithCompactionService(
SubcompactionState* sub_compact);
struct CompactionIOStatsSnapshot {
PerfLevel prev_perf_level = PerfLevel::kEnableTime;
uint64_t prev_write_nanos = 0;
uint64_t prev_fsync_nanos = 0;
uint64_t prev_range_sync_nanos = 0;
uint64_t prev_prepare_write_nanos = 0;
uint64_t prev_cpu_write_nanos = 0;
uint64_t prev_cpu_read_nanos = 0;
};
struct SubcompactionKeyBoundaries {
const std::optional<const Slice> start;
const std::optional<const Slice> end;
// Boundaries without timestamps for read options
std::optional<Slice> start_without_ts;
std::optional<Slice> end_without_ts;
// Timestamp management
static constexpr char kMaxTs[] =
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
std::string max_ts;
Slice ts_slice;
// Internal key boundaries
IterKey start_ikey;
IterKey end_ikey;
Slice start_internal_key;
Slice end_internal_key;
// User key boundaries
Slice start_user_key;
Slice end_user_key;
SubcompactionKeyBoundaries(std::optional<const Slice> start_boundary,
std::optional<const Slice> end_boundary)
: start(start_boundary), end(end_boundary) {}
};
struct SubcompactionInternalIterators {
std::unique_ptr<InternalIterator> raw_input;
std::unique_ptr<InternalIterator> clip;
std::unique_ptr<InternalIterator> blob_counter;
std::unique_ptr<InternalIterator> trim_history_iter;
};
struct BlobFileResources {
std::vector<std::string> blob_file_paths;
std::unique_ptr<BlobFileBuilder> blob_file_builder;
};
bool ShouldUseLocalCompaction(SubcompactionState* sub_compact);
CompactionIOStatsSnapshot InitializeIOStats();
Status SetupAndValidateCompactionFilter(
SubcompactionState* sub_compact,
const CompactionFilter* configured_compaction_filter,
const CompactionFilter*& compaction_filter,
std::unique_ptr<CompactionFilter>& compaction_filter_from_factory);
void InitializeReadOptionsAndBoundaries(
size_t ts_sz, ReadOptions& read_options,
SubcompactionKeyBoundaries& boundaries);
InternalIterator* CreateInputIterator(
SubcompactionState* sub_compact, ColumnFamilyData* cfd,
SubcompactionInternalIterators& iterators,
SubcompactionKeyBoundaries& boundaries, ReadOptions& read_options);
void CreateBlobFileBuilder(SubcompactionState* sub_compact,
ColumnFamilyData* cfd,
BlobFileResources& blob_resources,
const WriteOptions& write_options);
std::unique_ptr<CompactionIterator> CreateCompactionIterator(
SubcompactionState* sub_compact, ColumnFamilyData* cfd,
InternalIterator* input_iter, const CompactionFilter* compaction_filter,
MergeHelper& merge, BlobFileResources& blob_resources,
const WriteOptions& write_options);
std::pair<CompactionFileOpenFunc, CompactionFileCloseFunc> CreateFileHandlers(
SubcompactionState* sub_compact, SubcompactionKeyBoundaries& boundaries);
Status ProcessKeyValue(SubcompactionState* sub_compact, ColumnFamilyData* cfd,
CompactionIterator* c_iter,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func,
uint64_t& prev_cpu_micros);
void UpdateSubcompactionJobStatsIncrementally(
CompactionIterator* c_iter, CompactionJobStats* compaction_job_stats,
uint64_t cur_cpu_micros, uint64_t& prev_cpu_micros);
void FinalizeSubcompactionJobStats(SubcompactionState* sub_compact,
CompactionIterator* c_iter,
uint64_t start_cpu_micros,
uint64_t prev_cpu_micros,
const CompactionIOStatsSnapshot& io_stats);
Status FinalizeProcessKeyValueStatus(ColumnFamilyData* cfd,
InternalIterator* input_iter,
CompactionIterator* c_iter,
Status status);
Status CleanupCompactionFiles(SubcompactionState* sub_compact, Status status,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func);
Status FinalizeBlobFiles(SubcompactionState* sub_compact,
BlobFileBuilder* blob_file_builder, Status status);
void FinalizeSubcompaction(SubcompactionState* sub_compact, Status status,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func,
BlobFileBuilder* blob_file_builder,
CompactionIterator* c_iter,
InternalIterator* input_iter,
uint64_t start_cpu_micros,
uint64_t prev_cpu_micros,
const CompactionIOStatsSnapshot& io_stats);
// update the thread status for starting a compaction.
void ReportStartedCompaction(Compaction* compaction);
Status FinishCompactionOutputFile(
const Status& input_status,
const ParsedInternalKey& prev_iter_output_internal_key,
const Slice& next_table_min_key, const Slice* comp_start_user_key,
const Slice* comp_end_user_key, const CompactionIterator* c_iter,
SubcompactionState* sub_compact, CompactionOutputs& outputs);
Status InstallCompactionResults(bool* compaction_released);
Status FinishCompactionOutputFile(const Status& input_status,
SubcompactionState* sub_compact,
CompactionOutputs& outputs,
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 OpenCompactionOutputFile(SubcompactionState* sub_compact,
CompactionOutputs& outputs);
void UpdateCompactionJobStats(
const InternalStats::CompactionStats& stats) const;
void RecordDroppedKeys(const CompactionIterationStats& c_iter_stats,
CompactionJobStats* compaction_job_stats = nullptr);
@@ -465,8 +308,19 @@ class CompactionJob {
FSDirectory* blob_output_directory_;
InstrumentedMutex* db_mutex_;
ErrorHandler* db_error_handler_;
// If there were two snapshots with seq numbers s1 and
// s2 and s1 < s2, and if we find two instances of a key k1 then lies
// entirely within s1 and s2, then the earlier version of k1 can be safely
// deleted because that version is not visible in any snapshot.
std::vector<SequenceNumber> existing_snapshots_;
// This is the earliest snapshot that could be used for write-conflict
// checking by a transaction. For any user-key newer than this snapshot, we
// should make sure not to remove evidence that a write occurred.
SequenceNumber earliest_write_conflict_snapshot_;
const SnapshotChecker* const snapshot_checker_;
SequenceNumber earliest_snapshot_;
JobContext* job_context_;
std::shared_ptr<Cache> table_cache_;
@@ -497,21 +351,15 @@ class CompactionJob {
// it also collects the smallest_seqno -> oldest_ancester_time from the SST.
SeqnoToTimeMapping seqno_to_time_mapping_;
// Max seqno that can be zeroed out in last level, including for preserving
// write times.
SequenceNumber preserve_seqno_after_ = kMaxSequenceNumber;
// Minimal sequence number for preserving the time information. The time info
// older than this sequence number won't be preserved after the compaction and
// if it's bottommost compaction, the seq num will be zeroed out.
SequenceNumber preserve_time_min_seqno_ = kMaxSequenceNumber;
// Minimal sequence number to preclude the data from the last level. If the
// key has bigger (newer) sequence number than this, it will be precluded from
// the last level (output to proximal level).
SequenceNumber proximal_after_seqno_ = kMaxSequenceNumber;
// Options File Number used for Remote Compaction
// Setting this requires DBMutex.
uint64_t options_file_number_ = 0;
// Writer for persisting compaction progress during compaction
log::Writer* compaction_progress_writer_ = nullptr;
// the last level (output to penultimate level).
SequenceNumber preclude_last_level_min_seqno_ = kMaxSequenceNumber;
// Get table file name in where it's outputting to, which should also be in
// `output_directory_`.
@@ -520,43 +368,6 @@ class CompactionJob {
// The Compaction Read and Write priorities are the same for different
// scenarios, such as write stalled.
Env::IOPriority GetRateLimiterPriority();
Status MaybeResumeSubcompactionProgressOnInputIterator(
SubcompactionState* sub_compact, InternalIterator* input_iter);
Status ReadOutputFilesTableProperties(
const autovector<FileMetaData>& temporary_output_file_allocation,
const ReadOptions& read_options,
std::vector<std::shared_ptr<const TableProperties>>&
output_files_table_properties,
bool is_proximal_level = false);
Status ReadTablePropertiesDirectly(
const ImmutableOptions& ioptions, const MutableCFOptions& moptions,
const FileMetaData* file_meta, const ReadOptions& read_options,
std::shared_ptr<const TableProperties>* tp);
void RestoreCompactionOutputs(
const ColumnFamilyData* cfd,
const std::vector<std::shared_ptr<const TableProperties>>&
output_files_table_properties,
SubcompactionProgressPerLevel& subcompaction_progress_per_level,
CompactionOutputs* outputs_to_restore);
bool ShouldUpdateSubcompactionProgress(
const SubcompactionState* sub_compact, const CompactionIterator* c_iter,
const ParsedInternalKey& prev_iter_output_internal_key,
const Slice& next_table_min_internal_key, const FileMetaData* meta) const;
void UpdateSubcompactionProgress(const CompactionIterator* c_iter,
const Slice next_table_min_key,
SubcompactionState* sub_compact);
Status PersistSubcompactionProgress(SubcompactionState* sub_compact);
void UpdateSubcompactionProgressPerLevel(
SubcompactionState* sub_compact, bool is_proximal_level,
SubcompactionProgress& subcompaction_progress);
};
// CompactionServiceInput is used the pass compaction information between two
@@ -564,7 +375,9 @@ class CompactionJob {
// doesn't contain the LSM tree information, which is passed though MANIFEST
// file.
struct CompactionServiceInput {
std::string cf_name;
ColumnFamilyDescriptor column_family;
DBOptions db_options;
std::vector<SequenceNumber> snapshots;
@@ -572,7 +385,7 @@ struct CompactionServiceInput {
// files needed for this compaction, for both input level files and output
// level files.
std::vector<std::string> input_files;
int output_level = 0;
int output_level;
// db_id is used to generate unique id of sst on the remote compactor
std::string db_id;
@@ -583,12 +396,13 @@ struct CompactionServiceInput {
bool has_end = false;
std::string end;
uint64_t options_file_number = 0;
// serialization interface to read and write the object
static Status Read(const std::string& data_str, CompactionServiceInput* obj);
Status Write(std::string* output);
// Initialize a dummy ColumnFamilyDescriptor
CompactionServiceInput() : column_family("", ColumnFamilyOptions()) {}
#ifndef NDEBUG
bool TEST_Equals(CompactionServiceInput* other);
bool TEST_Equals(CompactionServiceInput* other, std::string* mismatch);
@@ -598,36 +412,25 @@ struct CompactionServiceInput {
// CompactionServiceOutputFile is the metadata for the output SST file
struct CompactionServiceOutputFile {
std::string file_name;
uint64_t file_size{};
SequenceNumber smallest_seqno{};
SequenceNumber largest_seqno{};
SequenceNumber smallest_seqno;
SequenceNumber largest_seqno;
std::string smallest_internal_key;
std::string largest_internal_key;
uint64_t oldest_ancester_time = kUnknownOldestAncesterTime;
uint64_t file_creation_time = kUnknownFileCreationTime;
uint64_t epoch_number = kUnknownEpochNumber;
std::string file_checksum = kUnknownFileChecksum;
std::string file_checksum_func_name = kUnknownFileChecksumFuncName;
uint64_t paranoid_hash{};
uint64_t oldest_ancester_time;
uint64_t file_creation_time;
uint64_t epoch_number;
uint64_t paranoid_hash;
bool marked_for_compaction;
UniqueId64x2 unique_id{};
TableProperties table_properties;
bool is_proximal_level_output;
Temperature file_temperature = Temperature::kUnknown;
UniqueId64x2 unique_id;
CompactionServiceOutputFile() = default;
CompactionServiceOutputFile(
const std::string& name, uint64_t size, SequenceNumber smallest,
SequenceNumber largest, std::string _smallest_internal_key,
std::string _largest_internal_key, uint64_t _oldest_ancester_time,
uint64_t _file_creation_time, uint64_t _epoch_number,
const std::string& _file_checksum,
const std::string& _file_checksum_func_name, uint64_t _paranoid_hash,
bool _marked_for_compaction, UniqueId64x2 _unique_id,
const TableProperties& _table_properties, bool _is_proximal_level_output,
Temperature _file_temperature)
const std::string& name, SequenceNumber smallest, SequenceNumber largest,
std::string _smallest_internal_key, std::string _largest_internal_key,
uint64_t _oldest_ancester_time, uint64_t _file_creation_time,
uint64_t _epoch_number, uint64_t _paranoid_hash,
bool _marked_for_compaction, UniqueId64x2 _unique_id)
: file_name(name),
file_size(size),
smallest_seqno(smallest),
largest_seqno(largest),
smallest_internal_key(std::move(_smallest_internal_key)),
@@ -635,14 +438,9 @@ struct CompactionServiceOutputFile {
oldest_ancester_time(_oldest_ancester_time),
file_creation_time(_file_creation_time),
epoch_number(_epoch_number),
file_checksum(_file_checksum),
file_checksum_func_name(_file_checksum_func_name),
paranoid_hash(_paranoid_hash),
marked_for_compaction(_marked_for_compaction),
unique_id(std::move(_unique_id)),
table_properties(_table_properties),
is_proximal_level_output(_is_proximal_level_output),
file_temperature(_file_temperature) {}
unique_id(std::move(_unique_id)) {}
};
// CompactionServiceResult contains the compaction result from a different db
@@ -651,28 +449,18 @@ struct CompactionServiceOutputFile {
struct CompactionServiceResult {
Status status;
std::vector<CompactionServiceOutputFile> output_files;
int output_level = 0;
int output_level;
// location of the output files
std::string output_path;
// some statistics about the compaction
uint64_t num_output_records = 0;
uint64_t total_bytes = 0;
uint64_t bytes_read = 0;
uint64_t bytes_written = 0;
// Job-level Compaction Stats.
//
// NOTE: Job level stats cannot be rebuilt from scratch by simply aggregating
// per-level stats due to some fields populated directly during compaction
// (e.g. RecordDroppedKeys()). This is why we need both job-level stats and
// per-level in the serialized result. If rebuilding job-level stats from
// per-level stats become possible in the future, consider deprecating this
// field.
CompactionJobStats stats;
// Per-level Compaction Stats for both output_level_stats and
// proximal_level_stats
InternalStats::CompactionStatsFull internal_stats;
// serialization interface to read and write the object
static Status Read(const std::string& data_str, CompactionServiceResult* obj);
Status Write(std::string* output);
@@ -695,21 +483,15 @@ class CompactionServiceCompactionJob : private CompactionJob {
const std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
FSDirectory* output_directory, Statistics* stats,
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
JobContext* job_context, std::shared_ptr<Cache> table_cache,
EventLogger* event_logger, const std::string& dbname,
const std::shared_ptr<IOTracer>& io_tracer,
std::vector<SequenceNumber> existing_snapshots,
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
const std::string& dbname, const std::shared_ptr<IOTracer>& io_tracer,
const std::atomic<bool>& manual_compaction_canceled,
const std::string& db_id, const std::string& db_session_id,
std::string output_path,
const CompactionServiceInput& compaction_service_input,
CompactionServiceResult* compaction_service_result);
// REQUIRED: mutex held
// Like CompactionJob::Prepare()
void Prepare(
const CompactionProgress& compaction_progress = CompactionProgress{},
log::Writer* compaction_progress_writer = nullptr);
// Run the compaction in current thread and return the result
Status Run();
@@ -959,6 +959,7 @@ int main(int argc, char** argv) {
return RUN_ALL_TESTS();
}
#else
int main(int /*argc*/, char** /*argv*/) { return 0; }
+75 -562
View File
@@ -3,6 +3,7 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#include "db/compaction/compaction_job.h"
#include <algorithm>
@@ -17,7 +18,6 @@
#include "db/db_impl/db_impl.h"
#include "db/error_handler.h"
#include "db/version_set.h"
#include "file/filename.h"
#include "file/random_access_file_reader.h"
#include "file/writable_file_writer.h"
#include "options/options_helper.h"
@@ -44,14 +44,15 @@ void VerifyInitializationOfCompactionJobStats(
ASSERT_EQ(compaction_job_stats.elapsed_micros, 0U);
ASSERT_EQ(compaction_job_stats.num_input_records, 0U);
ASSERT_EQ(compaction_job_stats.num_input_files, 0U);
ASSERT_EQ(compaction_job_stats.num_input_files_at_output_level, 0U);
ASSERT_EQ(compaction_job_stats.num_output_records, 0U);
ASSERT_EQ(compaction_job_stats.num_output_files, 0U);
ASSERT_TRUE(compaction_job_stats.is_manual_compaction);
ASSERT_FALSE(compaction_job_stats.is_remote_compaction);
ASSERT_EQ(compaction_job_stats.is_manual_compaction, true);
ASSERT_EQ(compaction_job_stats.total_input_bytes, 0U);
ASSERT_EQ(compaction_job_stats.total_output_bytes, 0U);
ASSERT_EQ(compaction_job_stats.total_input_raw_key_bytes, 0U);
@@ -211,12 +212,12 @@ class CompactionJobTestBase : public testing::Test {
table_cache_(NewLRUCache(50000, 16)),
write_buffer_manager_(db_options_.db_write_buffer_size),
versions_(new VersionSet(
dbname_, &db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
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, /*unchanging=*/false)),
/*error_handler=*/nullptr, /*read_only=*/false)),
shutting_down_(false),
mock_table_factory_(new mock::MockTableFactory()),
error_handler_(nullptr, db_options_, &mutex_),
@@ -248,7 +249,6 @@ class CompactionJobTestBase : public testing::Test {
} else {
assert(false);
}
mutable_cf_options_.table_factory = cf_options_.table_factory;
}
std::string GenerateFileName(uint64_t file_number) {
@@ -299,13 +299,13 @@ class CompactionJobTestBase : public testing::Test {
const WriteOptions write_options;
std::unique_ptr<TableBuilder> table_builder(
cf_options_.table_factory->NewTableBuilder(
TableBuilderOptions(
cfd_->ioptions(), mutable_cf_options_, read_options,
write_options, cfd_->internal_comparator(),
cfd_->internal_tbl_prop_coll_factories(),
CompressionType::kNoCompression, CompressionOptions(),
0 /* column_family_id */, kDefaultColumnFamilyName,
-1 /* level */, kUnknownNewestKeyTime),
TableBuilderOptions(*cfd_->ioptions(), mutable_cf_options_,
read_options, write_options,
cfd_->internal_comparator(),
cfd_->internal_tbl_prop_coll_factories(),
CompressionType::kNoCompression,
CompressionOptions(), 0 /* column_family_id */,
kDefaultColumnFamilyName, -1 /* level */),
file_writer.get()));
// Build table.
for (const auto& kv : contents) {
@@ -396,8 +396,8 @@ class CompactionJobTestBase : public testing::Test {
mutex_.Lock();
EXPECT_OK(versions_->LogAndApply(
versions_->GetColumnFamilySet()->GetDefault(), read_options_,
write_options_, &edit, &mutex_, nullptr));
versions_->GetColumnFamilySet()->GetDefault(), mutable_cf_options_,
read_options_, write_options_, &edit, &mutex_, nullptr));
mutex_.Unlock();
}
@@ -459,10 +459,9 @@ class CompactionJobTestBase : public testing::Test {
ReadOptions read_opts;
Status s = cf_options_.table_factory->NewTableReader(
read_opts,
TableReaderOptions(cfd->ioptions(), /*prefix_extractor=*/nullptr,
/*compression_manager=*/nullptr, FileOptions(),
TableReaderOptions(*cfd->ioptions(), nullptr, FileOptions(),
cfd_->internal_comparator(),
/*block_protection_bytes_per_key=*/0),
0 /* block_protection_bytes_per_key */),
std::move(freader), file_size, &table_reader, false);
ASSERT_OK(s);
assert(table_reader);
@@ -547,13 +546,13 @@ class CompactionJobTestBase : public testing::Test {
db_options_.info_log = info_log;
versions_.reset(new VersionSet(
dbname_, &db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
test::kUnitTestDbId, /*db_session_id=*/"",
/*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*unchanging=*/false));
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*read_only=*/false));
compaction_job_stats_.Reset();
ASSERT_OK(SetIdentityFile(WriteOptions(), env_, dbname_));
VersionEdit new_db;
new_db.SetLogNumber(0);
@@ -576,8 +575,7 @@ class CompactionJobTestBase : public testing::Test {
}
ASSERT_OK(s);
// Make "CURRENT" file that points to the new manifest file.
s = SetCurrentFile(WriteOptions(), fs_.get(), dbname_, 1,
Temperature::kUnknown, nullptr);
s = SetCurrentFile(WriteOptions(), fs_.get(), dbname_, 1, nullptr);
ASSERT_OK(s);
@@ -595,11 +593,11 @@ class CompactionJobTestBase : public testing::Test {
const std::vector<std::vector<FileMetaData*>>& input_files,
const std::vector<int> input_levels,
std::function<void(Compaction& comp)>&& verify_func,
std::vector<SequenceNumber>&& snapshots = {}) {
const std::vector<SequenceNumber>& snapshots = {}) {
const int kLastLevel = cf_options_.num_levels - 1;
verify_per_key_placement_ = std::move(verify_func);
mock::KVVector empty_map;
RunCompaction(input_files, input_levels, {empty_map}, std::move(snapshots),
RunCompaction(input_files, input_levels, {empty_map}, snapshots,
kMaxSequenceNumber, kLastLevel, false);
}
@@ -608,7 +606,7 @@ class CompactionJobTestBase : public testing::Test {
const std::vector<std::vector<FileMetaData*>>& input_files,
const std::vector<int>& input_levels,
const std::vector<mock::KVVector>& expected_results,
std::vector<SequenceNumber>&& snapshots = {},
const std::vector<SequenceNumber>& snapshots = {},
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber,
int output_level = 1, bool verify = true,
std::vector<uint64_t> expected_oldest_blob_file_numbers = {},
@@ -645,15 +643,13 @@ class CompactionJobTestBase : public testing::Test {
}
Compaction compaction(
cfd->current()->storage_info(), cfd->ioptions(),
cfd->GetLatestMutableCFOptions(), mutable_db_options_,
cfd->current()->storage_info(), *cfd->ioptions(),
*cfd->GetLatestMutableCFOptions(), mutable_db_options_,
compaction_input_files, output_level,
mutable_cf_options_.target_file_size_base,
mutable_cf_options_.max_compaction_bytes, 0, kNoCompression,
cfd->GetLatestMutableCFOptions().compression_opts,
Temperature::kUnknown, max_subcompactions, grandparents,
/*earliest_snapshot*/ std::nullopt, /*snapshot_checker*/ nullptr,
CompactionReason::kManualCompaction);
cfd->GetLatestMutableCFOptions()->compression_opts,
Temperature::kUnknown, max_subcompactions, grandparents, true);
compaction.FinalizeInputInfo(cfd->current());
assert(db_options_.info_log);
@@ -666,28 +662,27 @@ class CompactionJobTestBase : public testing::Test {
ucmp_->timestamp_size() == full_history_ts_low_.size());
const std::atomic<bool> kManualCompactionCanceledFalse{false};
JobContext job_context(1, false /* create_superversion */);
job_context.InitSnapshotContext(snapshot_checker, nullptr,
earliest_write_conflict_snapshot,
std::move(snapshots));
CompactionJob compaction_job(
0, &compaction, db_options_, mutable_db_options_, env_options_,
versions_.get(), &shutting_down_, &log_buffer, nullptr, nullptr,
nullptr, nullptr, &mutex_, &error_handler_, &job_context, table_cache_,
&event_logger, false, false, dbname_, &compaction_job_stats_,
Env::Priority::USER, nullptr /* IOTracer */,
nullptr, nullptr, &mutex_, &error_handler_, snapshots,
earliest_write_conflict_snapshot, snapshot_checker, &job_context,
table_cache_, &event_logger, false, false, dbname_,
&compaction_job_stats_, Env::Priority::USER, nullptr /* IOTracer */,
/*manual_compaction_canceled=*/kManualCompactionCanceledFalse,
env_->GenerateUniqueId(), DBImpl::GenerateDbSessionId(nullptr),
full_history_ts_low_);
VerifyInitializationOfCompactionJobStats(compaction_job_stats_);
compaction_job.Prepare(std::nullopt /*subcompact to be computed*/);
compaction_job.Prepare();
mutex_.Unlock();
Status s = compaction_job.Run();
ASSERT_OK(s);
ASSERT_OK(compaction_job.io_status());
mutex_.Lock();
bool compaction_released = false;
ASSERT_OK(compaction_job.Install(&compaction_released));
ASSERT_OK(compaction_job.Install(*cfd->GetLatestMutableCFOptions(),
&compaction_released));
ASSERT_OK(compaction_job.io_status());
mutex_.Unlock();
log_buffer.FlushBufferToLog();
@@ -1477,7 +1472,7 @@ TEST_F(CompactionJobTest, OldestBlobFileNumber) {
/* expected_oldest_blob_file_numbers */ {19});
}
TEST_F(CompactionJobTest, VerifyProximalLevelOutput) {
TEST_F(CompactionJobTest, VerifyPenultimateLevelOutput) {
cf_options_.last_level_temperature = Temperature::kCold;
SyncPoint::GetInstance()->SetCallBack(
"Compaction::SupportsPerKeyPlacement:Enabled", [&](void* arg) {
@@ -1490,7 +1485,8 @@ TEST_F(CompactionJobTest, VerifyProximalLevelOutput) {
SyncPoint::GetInstance()->SetCallBack(
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
auto context = static_cast<PerKeyPlacementContext*>(arg);
context->output_to_proximal_level = context->seq_num > latest_cold_seq;
context->output_to_penultimate_level =
context->seq_num > latest_cold_seq;
});
SyncPoint::GetInstance()->EnableProcessing();
@@ -1536,11 +1532,14 @@ TEST_F(CompactionJobTest, VerifyProximalLevelOutput) {
/*verify_func=*/[&](Compaction& comp) {
for (char c = 'a'; c <= 'z'; c++) {
if (c == 'a') {
comp.TEST_AssertWithinProximalLevelOutputRange(
"a", true /*expect_failure*/);
ParsedInternalKey pik("a", 0U, kTypeValue);
ASSERT_FALSE(comp.WithinPenultimateLevelOutputRange(pik));
} else {
std::string c_str{c};
comp.TEST_AssertWithinProximalLevelOutputRange(c_str);
// WithinPenultimateLevelOutputRange checks internal key range.
// 'z' is the last key, so set seqno properly.
ParsedInternalKey pik(c_str, c == 'z' ? 12U : 0U, kTypeValue);
ASSERT_TRUE(comp.WithinPenultimateLevelOutputRange(pik));
}
}
});
@@ -1568,7 +1567,17 @@ TEST_F(CompactionJobTest, InputSerialization) {
const int kStrMaxLen = 1000;
Random rnd(static_cast<uint32_t>(time(nullptr)));
Random64 rnd64(time(nullptr));
input.cf_name = rnd.RandomString(rnd.Uniform(kStrMaxLen));
input.column_family.name = rnd.RandomString(rnd.Uniform(kStrMaxLen));
input.column_family.options.comparator = ReverseBytewiseComparator();
input.column_family.options.max_bytes_for_level_base =
rnd64.Uniform(UINT64_MAX);
input.column_family.options.disable_auto_compactions = rnd.OneIn(2);
input.column_family.options.compression = kZSTD;
input.column_family.options.compression_opts.level = 4;
input.db_options.max_background_flushes = 10;
input.db_options.paranoid_checks = rnd.OneIn(2);
input.db_options.statistics = CreateDBStatistics();
input.db_options.env = env_;
while (!rnd.OneIn(10)) {
input.snapshots.emplace_back(rnd64.Uniform(UINT64_MAX));
}
@@ -1596,10 +1605,10 @@ TEST_F(CompactionJobTest, InputSerialization) {
ASSERT_TRUE(deserialized1.TEST_Equals(&input));
// Test mismatch
deserialized1.output_level += 10;
deserialized1.db_options.max_background_flushes += 10;
std::string mismatch;
ASSERT_FALSE(deserialized1.TEST_Equals(&input, &mismatch));
ASSERT_EQ(mismatch, "output_level");
ASSERT_EQ(mismatch, "db_options.max_background_flushes");
// Test unknown field
CompactionServiceInput deserialized2;
@@ -1655,42 +1664,20 @@ TEST_F(CompactionJobTest, ResultSerialization) {
};
result.status =
status_list.at(rnd.Uniform(static_cast<int>(status_list.size())));
std::string file_checksum = rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen));
std::string file_checksum_func_name = "MyAwesomeChecksumGenerator";
while (!rnd.OneIn(10)) {
TableProperties tp;
tp.user_collected_properties.emplace(
"UCP_Key1", rnd.RandomString(rnd.Uniform(kStrMaxLen)));
tp.user_collected_properties.emplace(
"UCP_Key2", rnd.RandomString(rnd.Uniform(kStrMaxLen)));
tp.readable_properties.emplace("RP_Key1",
rnd.RandomString(rnd.Uniform(kStrMaxLen)));
tp.readable_properties.emplace("RP_K2y2",
rnd.RandomString(rnd.Uniform(kStrMaxLen)));
UniqueId64x2 id{rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX)};
result.output_files.emplace_back(
rnd.RandomString(rnd.Uniform(kStrMaxLen)) /* file_name */,
rnd64.Uniform(UINT64_MAX) /* file_size */,
rnd64.Uniform(UINT64_MAX) /* smallest_seqno */,
rnd64.Uniform(UINT64_MAX) /* largest_seqno */,
rnd.RandomBinaryString(
rnd.Uniform(kStrMaxLen)) /* smallest_internal_key */,
rnd.RandomBinaryString(
rnd.Uniform(kStrMaxLen)) /* largest_internal_key */,
rnd64.Uniform(UINT64_MAX) /* oldest_ancester_time */,
rnd64.Uniform(UINT64_MAX) /* file_creation_time */,
rnd64.Uniform(UINT64_MAX) /* epoch_number */,
file_checksum /* file_checksum */,
file_checksum_func_name /* file_checksum_func_name */,
rnd64.Uniform(UINT64_MAX) /* paranoid_hash */,
rnd.OneIn(2) /* marked_for_compaction */, id /* unique_id */, tp,
false /* is_proximal_level_output */, Temperature::kHot);
rnd.RandomString(rnd.Uniform(kStrMaxLen)), rnd64.Uniform(UINT64_MAX),
rnd64.Uniform(UINT64_MAX),
rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen)),
rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen)),
rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX),
rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX), rnd.OneIn(2), id);
}
result.output_level = rnd.Uniform(10);
result.output_path = rnd.RandomString(rnd.Uniform(kStrMaxLen));
result.stats.num_output_records = rnd64.Uniform(UINT64_MAX);
result.num_output_records = rnd64.Uniform(UINT64_MAX);
result.total_bytes = rnd64.Uniform(UINT64_MAX);
result.bytes_read = 123;
result.bytes_written = rnd64.Uniform(UINT64_MAX);
result.stats.elapsed_micros = rnd64.Uniform(UINT64_MAX);
@@ -1707,21 +1694,6 @@ TEST_F(CompactionJobTest, ResultSerialization) {
ASSERT_OK(CompactionServiceResult::Read(output, &deserialized1));
ASSERT_TRUE(deserialized1.TEST_Equals(&result));
for (size_t i = 0; i < result.output_files.size(); i++) {
for (const auto& prop :
result.output_files[i].table_properties.user_collected_properties) {
ASSERT_EQ(deserialized1.output_files[i]
.table_properties.user_collected_properties[prop.first],
prop.second);
}
for (const auto& prop :
result.output_files[i].table_properties.readable_properties) {
ASSERT_EQ(deserialized1.output_files[i]
.table_properties.readable_properties[prop.first],
prop.second);
}
}
// Test mismatch
deserialized1.stats.num_input_files += 10;
std::string mismatch;
@@ -1736,12 +1708,6 @@ TEST_F(CompactionJobTest, ResultSerialization) {
ASSERT_FALSE(deserialized_tmp.TEST_Equals(&result, &mismatch));
ASSERT_EQ(mismatch, "output_files.unique_id");
deserialized_tmp.status.PermitUncheckedError();
ASSERT_EQ(deserialized_tmp.output_files[0].file_checksum, file_checksum);
ASSERT_EQ(deserialized_tmp.output_files[0].file_checksum_func_name,
file_checksum_func_name);
ASSERT_EQ(deserialized_tmp.output_files[0].file_temperature,
Temperature::kHot);
}
// Test unknown field
@@ -1901,8 +1867,8 @@ TEST_F(CompactionJobTest, CutToSkipGrandparentFile) {
const std::vector<int> input_levels = {0, 1};
auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
auto lvl1_files = cfd_->current()->storage_info()->LevelFiles(1);
RunCompaction({lvl0_files, lvl1_files}, input_levels,
{expected_file1, expected_file2});
RunCompaction({lvl0_files, lvl1_files}, input_levels,
{expected_file1, expected_file2});
}
TEST_F(CompactionJobTest, CutToAlignGrandparentBoundary) {
@@ -1976,8 +1942,8 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundary) {
const std::vector<int> input_levels = {0, 1};
auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
auto lvl1_files = cfd_->current()->storage_info()->LevelFiles(1);
RunCompaction({lvl0_files, lvl1_files}, input_levels,
{expected_file1, expected_file2});
RunCompaction({lvl0_files, lvl1_files}, input_levels,
{expected_file1, expected_file2});
}
TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
@@ -2038,8 +2004,8 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
for (int i = 80; i <= 100; i++) {
snapshots.emplace_back(i);
}
RunCompaction({lvl0_files, lvl1_files}, input_levels,
{expected_file1, expected_file2}, std::move(snapshots));
RunCompaction({lvl0_files, lvl1_files}, input_levels,
{expected_file1, expected_file2}, snapshots);
}
TEST_F(CompactionJobTest, CutForMaxCompactionBytesSameKey) {
@@ -2098,8 +2064,7 @@ TEST_F(CompactionJobTest, CutForMaxCompactionBytesSameKey) {
snapshots.emplace_back(i);
}
RunCompaction({lvl0_files, lvl1_files}, input_levels,
{expected_file1, expected_file2, expected_file3},
std::move(snapshots));
{expected_file1, expected_file2, expected_file3}, snapshots);
}
class CompactionJobTimestampTest : public CompactionJobTestBase {
@@ -2410,458 +2375,6 @@ TEST_F(CompactionJobIOPriorityTest, GetRateLimiterPriority) {
Env::IO_LOW, Env::IO_LOW);
}
class ResumableCompactionJobTest : public CompactionJobTestBase {
public:
ResumableCompactionJobTest()
: CompactionJobTestBase(
test::PerThreadDBPath("allow_resumption_job_test"),
BytewiseComparator(), [](uint64_t /*ts*/) { return ""; },
/*test_io_priority=*/false, TableTypeForTest::kBlockBasedTable) {}
protected:
static constexpr const char* kCancelBeforeThisKey = "cancel_before_this_key";
std::string progress_dir_;
bool enable_cancel_ = false;
std::atomic<int> stop_count_{0};
std::atomic<bool> cancel_{false};
SequenceNumber cancel_before_seqno = kMaxSequenceNumber;
void SetUp() override {
CompactionJobTestBase::SetUp();
SyncPoint::GetInstance()->SetCallBack(
"CompactionOutputs::ShouldStopBefore::manual_decision",
[this](void* p) {
auto* pair = static_cast<std::pair<bool*, const Slice>*>(p);
*(pair->first) = true;
// Cancel after outputting a specific key
if (enable_cancel_) {
ParsedInternalKey parsed_key;
if (ParseInternalKey(pair->second, &parsed_key, true).ok()) {
if (parsed_key.user_key == kCancelBeforeThisKey &&
(cancel_before_seqno == kMaxSequenceNumber ||
parsed_key.sequence == cancel_before_seqno)) {
cancel_.store(true);
}
}
}
});
SyncPoint::GetInstance()->EnableProcessing();
}
void TearDown() override {
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
if (env_->FileExists(progress_dir_).ok()) {
std::vector<std::string> files;
EXPECT_OK(env_->GetChildren(progress_dir_, &files));
for (const auto& file : files) {
if (file != "." && file != "..") {
EXPECT_OK(env_->DeleteFile(progress_dir_ + "/" + file));
}
}
EXPECT_OK(env_->DeleteDir(progress_dir_));
}
CompactionJobTestBase::TearDown();
}
void NewDB() {
if (env_->FileExists(progress_dir_).ok()) {
std::vector<std::string> files;
EXPECT_OK(env_->GetChildren(progress_dir_, &files));
for (const auto& file : files) {
if (file != "." && file != "..") {
EXPECT_OK(env_->DeleteFile(progress_dir_ + "/" + file));
}
}
EXPECT_OK(env_->DeleteDir(progress_dir_));
}
CompactionJobTestBase::NewDB();
progress_dir_ = test::PerThreadDBPath("compaction_progress");
ASSERT_OK(env_->CreateDirIfMissing(progress_dir_));
}
void EnableCompactionCancel() { enable_cancel_ = true; }
void DisableCompactionCancel() {
enable_cancel_ = false;
cancel_.store(false);
}
std::unique_ptr<log::Writer> CreateCompactionProgressWriter(
const std::string& compaction_progress_file) {
std::unique_ptr<FSWritableFile> file;
EXPECT_OK(fs_->NewWritableFile(compaction_progress_file, FileOptions(),
&file, nullptr));
auto file_writer = std::make_unique<WritableFileWriter>(
std::move(file), compaction_progress_file, FileOptions());
auto compaction_progress_writer =
std::make_unique<log::Writer>(std::move(file_writer), 0, false);
return compaction_progress_writer;
}
Status RunCompactionWithProgressTracking(
const CompactionProgress& compaction_progress,
log::Writer* compaction_progress_writer,
std::vector<SequenceNumber> snapshots = {},
std::shared_ptr<Statistics> stats = nullptr) {
mutex_.Lock();
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
auto files = cfd->current()->storage_info()->LevelFiles(0);
db_options_.statistics = stats;
db_options_.stats = db_options_.statistics.get();
std::vector<CompactionInputFiles> compaction_input_files;
CompactionInputFiles level;
level.level = 0;
level.files = files;
compaction_input_files.push_back(level);
Compaction compaction(
cfd->current()->storage_info(), cfd->ioptions(),
cfd->GetLatestMutableCFOptions(), mutable_db_options_,
compaction_input_files, 1, mutable_cf_options_.target_file_size_base,
mutable_cf_options_.max_compaction_bytes, 0, kNoCompression,
cfd->GetLatestMutableCFOptions().compression_opts,
Temperature::kUnknown, 0, {}, std::nullopt, nullptr,
CompactionReason::kManualCompaction);
compaction.FinalizeInputInfo(cfd->current());
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
EventLogger event_logger(db_options_.info_log.get());
JobContext job_context(1, false);
job_context.InitSnapshotContext(nullptr, nullptr, kMaxSequenceNumber,
std::move(snapshots));
CompactionJobStats job_stats;
CompactionJob compaction_job(
0, &compaction, db_options_, mutable_db_options_, env_options_,
versions_.get(), &shutting_down_, &log_buffer, nullptr, nullptr,
nullptr, stats.get(), &mutex_, &error_handler_, &job_context,
table_cache_, &event_logger, false, false, dbname_, &job_stats,
Env::Priority::USER, nullptr, cancel_, env_->GenerateUniqueId(),
DBImpl::GenerateDbSessionId(nullptr), "");
compaction_job.Prepare(std::nullopt, compaction_progress,
compaction_progress_writer);
mutex_.Unlock();
compaction_job.Run().PermitUncheckedError();
EXPECT_OK(compaction_job.io_status());
mutex_.Lock();
bool compaction_released = false;
Status s = compaction_job.Install(&compaction_released);
mutex_.Unlock();
if (!compaction_released) {
compaction.ReleaseCompactionFiles(s);
}
return s;
}
SubcompactionProgress ReadAndParseProgress(
const std::string& compaction_progress_file) {
std::unique_ptr<FSSequentialFile> seq_file;
EXPECT_OK(fs_->NewSequentialFile(compaction_progress_file, FileOptions(),
&seq_file, nullptr));
auto file_reader = std::make_unique<SequentialFileReader>(
std::move(seq_file), compaction_progress_file, 0, nullptr);
log::Reader reader(nullptr, std::move(file_reader), nullptr, true, 0);
SubcompactionProgressBuilder builder;
std::string record;
Slice slice;
while (reader.ReadRecord(&slice, &record)) {
VersionEdit edit;
if (!edit.DecodeFrom(slice).ok()) {
continue;
}
builder.ProcessVersionEdit(edit);
}
EXPECT_TRUE(builder.HasAccumulatedSubcompactionProgress());
return builder.GetAccumulatedSubcompactionProgress();
}
// Test utility function to verify that compaction progress was correctly
// persisted to the progress file after compaction interruption.
//
// VERIFIES:
// - Progress file exists and has expected size (empty if no progress
// expected)
// - Next internal key to compact matches expected user key with proper format
// - Number of processed input records matches position in ordered input keys
// - Number of processed output records equals number of processed input
// records (by test design to simplify verification)
// - Each output file contains exactly one user key (by test design to
// simplify verification)
void VerifyCompactionProgressPersisted(
const std::string& compaction_progress_file,
const std::string& next_user_key_to_compact,
const std::vector<std::string>& ordered_intput_keys) {
ASSERT_OK(env_->FileExists(compaction_progress_file));
uint64_t file_size;
ASSERT_OK(env_->GetFileSize(compaction_progress_file, &file_size));
if (next_user_key_to_compact.empty()) {
ASSERT_EQ(file_size, 0);
return;
}
const auto& subcompaction_progress =
ReadAndParseProgress(compaction_progress_file);
ASSERT_FALSE(subcompaction_progress.next_internal_key_to_compact.empty());
ParsedInternalKey parsed_next_key;
ASSERT_OK(
ParseInternalKey(subcompaction_progress.next_internal_key_to_compact,
&parsed_next_key, true /* log_err_key */));
ASSERT_EQ(parsed_next_key.user_key, next_user_key_to_compact);
ASSERT_EQ(parsed_next_key.sequence, kMaxSequenceNumber);
ASSERT_EQ(parsed_next_key.type, kValueTypeForSeek);
auto it = std::find(ordered_intput_keys.begin(), ordered_intput_keys.end(),
next_user_key_to_compact);
ASSERT_TRUE(it != ordered_intput_keys.end());
auto next_key_index = std::distance(ordered_intput_keys.begin(), it);
ASSERT_EQ(subcompaction_progress.num_processed_input_records,
next_key_index);
ASSERT_EQ(subcompaction_progress.output_level_progress
.GetNumProcessedOutputRecords(),
next_key_index);
ASSERT_EQ(
subcompaction_progress.output_level_progress.GetOutputFiles().size(),
next_key_index);
for (size_t i = 0;
i <
subcompaction_progress.output_level_progress.GetOutputFiles().size();
++i) {
const auto& output_file =
subcompaction_progress.output_level_progress.GetOutputFiles()[i];
ASSERT_EQ(output_file.smallest.user_key().ToString(),
output_file.largest.user_key().ToString());
ASSERT_EQ(output_file.largest.user_key().ToString(),
ordered_intput_keys[i]);
}
}
void RunCancelAndResumeTest(
const std::initializer_list<mock::KVPair>& input_file_1,
const std::initializer_list<mock::KVPair>& input_file_2,
uint64_t last_sequence, const std::vector<uint64_t>& snapshots,
const std::string& expected_next_key_to_compact,
const std::vector<std::string>& expected_input_keys,
bool cancelled_past_mid_point = false) {
std::shared_ptr<Statistics> stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
auto file1 = mock::MakeMockFile(input_file_1);
AddMockFile(file1);
auto file2 = mock::MakeMockFile(input_file_2);
AddMockFile(file2);
SetLastSequence(last_sequence);
// First compaction (will be cancelled)
std::string compaction_progress_file =
CompactionProgressFileName(progress_dir_, 123);
std::unique_ptr<log::Writer> compaction_progress_writer =
CreateCompactionProgressWriter(compaction_progress_file);
ASSERT_OK(stats->Reset());
EnableCompactionCancel();
Status status = RunCompactionWithProgressTracking(
CompactionProgress{}, compaction_progress_writer.get(), snapshots,
stats);
ASSERT_TRUE(status.IsManualCompactionPaused());
DisableCompactionCancel();
HistogramData cancelled_compaction_stats;
stats->histogramData(FILE_WRITE_COMPACTION_MICROS,
&cancelled_compaction_stats);
VerifyCompactionProgressPersisted(compaction_progress_file,
expected_next_key_to_compact,
expected_input_keys);
// Resume compaction
CompactionProgress compaction_progress;
if (expected_next_key_to_compact != "") {
compaction_progress.push_back(
ReadAndParseProgress(compaction_progress_file));
}
std::string compaction_progress_file_2 =
CompactionProgressFileName(progress_dir_, 234);
std::unique_ptr<log::Writer> compaction_progress_writer_2 =
CreateCompactionProgressWriter(compaction_progress_file_2);
ASSERT_OK(stats->Reset());
status = RunCompactionWithProgressTracking(
compaction_progress, compaction_progress_writer_2.get(),
{} /* snapshots */, stats);
ASSERT_OK(status);
if (cancelled_past_mid_point) {
HistogramData resumed_compaction_stats;
stats->histogramData(FILE_WRITE_COMPACTION_MICROS,
&resumed_compaction_stats);
ASSERT_GT(cancelled_compaction_stats.count,
resumed_compaction_stats.count);
}
}
};
TEST_F(ResumableCompactionJobTest, BasicProgressPersistence) {
NewDB();
auto file1 = mock::MakeMockFile({
{KeyStr("a", 1U, kTypeValue), "val1"},
{KeyStr("b", 2U, kTypeValue), "val2"},
});
AddMockFile(file1);
auto file2 = mock::MakeMockFile({
{KeyStr("c", 3U, kTypeValue), "val3"},
{KeyStr("d", 4U, kTypeValue), "val4"},
});
AddMockFile(file2);
SetLastSequence(4U);
std::string compaction_progress_file =
CompactionProgressFileName(progress_dir_, 123);
std::unique_ptr<log::Writer> compaction_progress_writer =
CreateCompactionProgressWriter(compaction_progress_file);
Status status = RunCompactionWithProgressTracking(
CompactionProgress(), compaction_progress_writer.get());
ASSERT_OK(status);
VerifyCompactionProgressPersisted(
compaction_progress_file, "d" /* next_user_key_to_compact */,
{"a", "b", "c", "d"} /* ordered_intput_keys */);
}
TEST_F(ResumableCompactionJobTest, BasicProgressResume) {
NewDB();
RunCancelAndResumeTest(
{{KeyStr("a", 1U, kTypeValue), "val1"},
{KeyStr("b", 2U, kTypeValue), "val2"}} /* input_file_1 */,
{{KeyStr("bb", 3U, kTypeValue), "val3"},
{KeyStr(kCancelBeforeThisKey, 4U, kTypeValue),
"val4"}} /* input_file_2 */,
4U /* last_sequence */, {} /* snapshots */,
kCancelBeforeThisKey /* expected_next_key_to_compact */,
{"a", "b", "bb", kCancelBeforeThisKey} /* expected_input_keys */,
true /* cancelled_past_mid_point */);
}
TEST_F(ResumableCompactionJobTest, NoProgressResumeOnSameKey) {
NewDB();
// `cancel_before_seqno` is set to 0U to force cancellation after
// `kCancelBeforeThisKey@1` instead of `kCancelBeforeThisKey@2`.
// The seqno is 0 because `kCancelBeforeThisKey@1` will have its sequence
// number zeroed during compaction while `kCancelBeforeThisKey@2` won't be
cancel_before_seqno = 0U;
RunCancelAndResumeTest(
{{KeyStr(kCancelBeforeThisKey, 1U, kTypeValue),
"val1"}} /* input_file_1 */,
{{KeyStr(kCancelBeforeThisKey, 2U, kTypeValue), "val11"},
{KeyStr("d", 3U, kTypeValue), "val2"}} /* input_file_2 */,
3U /* last_sequence */, {1U} /* snapshots */,
"" /* expected_next_key_to_compact */,
{kCancelBeforeThisKey, kCancelBeforeThisKey,
"d"} /* expected_input_keys */);
}
TEST_F(ResumableCompactionJobTest, NoProgressResumeOnDeleteRange) {
NewDB();
RunCancelAndResumeTest(
{{KeyStr("a", 1U, kTypeValue), "val1"},
{KeyStr("b", 2U, kTypeValue), "val2"},
{KeyStr(kCancelBeforeThisKey, 3U, kTypeValue),
"val3"}} /* input_file_1 */,
{{KeyStr(kCancelBeforeThisKey, 4U, kTypeRangeDeletion),
"range_deletion_end_key"},
{KeyStr("d", 5U, kTypeValue), "val4"}} /* input_file_2 */,
5U /* last_sequence */, {3U} /* snapshots */,
"b" /* expected_next_key_to_compact */,
{"a", "b", kCancelBeforeThisKey, kCancelBeforeThisKey,
"d"} /* expected_input_keys */);
}
TEST_F(ResumableCompactionJobTest, NoProgressResumeOnMerge) {
merge_op_ = MergeOperators::CreateStringAppendOperator();
NewDB();
RunCancelAndResumeTest(
{{KeyStr("a", 1U, kTypeValue), "val1"},
{KeyStr("b", 2U, kTypeValue), "val2"}} /* input_file_1 */,
{{KeyStr("bb", 3U, kTypeValue), "val3"},
{KeyStr(kCancelBeforeThisKey, 4U, kTypeMerge),
"val4"}} /* input_file_2 */,
4U /* last_sequence */, {} /* snapshots */,
"bb" /* expected_next_key_to_compact */,
{"a", "b", "bb", kCancelBeforeThisKey} /* expected_input_keys */);
}
TEST_F(ResumableCompactionJobTest, NoProgressResumeOnSingleDelete) {
NewDB();
RunCancelAndResumeTest(
{{KeyStr("a", 1U, kTypeValue), "val1"},
{KeyStr("b", 2U, kTypeValue), "val2"},
{KeyStr(kCancelBeforeThisKey, 3U, kTypeValue),
"val3"}} /* input_file_1 */,
{{KeyStr(kCancelBeforeThisKey, 4U, kTypeSingleDeletion), ""},
{KeyStr("d", 5U, kTypeValue), "val4"}} /* input_file_2 */,
5U /* last_sequence */, {3U} /* snapshots */,
"b" /* expected_next_key_to_compact */,
{"a", "b", kCancelBeforeThisKey, kCancelBeforeThisKey,
"d"} /* expected_input_keys */);
}
TEST_F(ResumableCompactionJobTest, NoProgressResumeOnDeletionAtBottom) {
NewDB();
RunCancelAndResumeTest(
{{KeyStr("a", 1U, kTypeValue), "val1"},
{KeyStr("b", 2U, kTypeValue), "val2"},
{KeyStr(kCancelBeforeThisKey, 3U, kTypeValue),
"val3"}} /* input_file_1 */,
{{KeyStr(kCancelBeforeThisKey, 4U, kTypeDeletion), ""},
{KeyStr("d", 5U, kTypeValue), "val4"}} /* input_file_2 */,
5U /* last_sequence */, {3U} /* snapshots */,
"b" /* expected_next_key_to_compact */,
{"a", "b", kCancelBeforeThisKey, kCancelBeforeThisKey,
"d"} /* expected_input_keys */);
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+20 -34
View File
@@ -54,9 +54,7 @@ Status CompactionOutputs::Finish(
}
current_output().finished = true;
stats_.bytes_written += current_bytes;
stats_.bytes_written_pre_comp += builder_->PreCompressionSize();
stats_.num_output_files = static_cast<int>(outputs_.size());
worker_cpu_micros_ += builder_->GetWorkerCPUMicros();
stats_.num_output_files = outputs_.size();
return s;
}
@@ -278,11 +276,7 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
}
// reach the max file size
uint64_t estimated_file_size = current_output_file_size_;
if (compaction_->mutable_cf_options().target_file_size_is_upper_bound) {
estimated_file_size += builder_->EstimatedTailSize();
}
if (estimated_file_size >= compaction_->max_output_file_size()) {
if (current_output_file_size_ >= compaction_->max_output_file_size()) {
return true;
}
@@ -326,7 +320,7 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
// More details, check PR #1963
const size_t num_skippable_boundaries_crossed =
being_grandparent_gap_ ? 2 : 3;
if (compaction_->immutable_options().compaction_style ==
if (compaction_->immutable_options()->compaction_style ==
kCompactionStyleLevel &&
num_grandparent_boundaries_crossed >=
num_skippable_boundaries_crossed &&
@@ -347,7 +341,7 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
// target file size. The test shows it can generate larger files than a
// static threshold like 75% and has a similar write amplification
// improvement.
if (compaction_->immutable_options().compaction_style ==
if (compaction_->immutable_options()->compaction_style ==
kCompactionStyleLevel &&
current_output_file_size_ >=
((compaction_->target_output_file_size() + 99) / 100) *
@@ -363,8 +357,7 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
Status CompactionOutputs::AddToOutput(
const CompactionIterator& c_iter,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func,
const ParsedInternalKey& prev_iter_output_internal_key) {
const CompactionFileCloseFunc& close_file_func) {
Status s;
bool is_range_del = c_iter.IsDeleteRangeSentinelKey();
if (is_range_del && compaction_->bottommost_level()) {
@@ -375,8 +368,7 @@ Status CompactionOutputs::AddToOutput(
}
const Slice& key = c_iter.key();
if (ShouldStopBefore(c_iter) && HasBuilder()) {
s = close_file_func(c_iter.InputStatus(), prev_iter_output_internal_key,
key, &c_iter, *this);
s = close_file_func(*this, c_iter.InputStatus(), key);
if (!s.ok()) {
return s;
}
@@ -463,11 +455,9 @@ void SetMaxSeqAndTs(InternalKey& internal_key, const Slice& user_key,
} // namespace
Status CompactionOutputs::AddRangeDels(
CompactionRangeDelAggregator& range_del_agg,
const Slice* comp_start_user_key, const Slice* comp_end_user_key,
CompactionIterationStats& range_del_out_stats, bool bottommost_level,
const InternalKeyComparator& icmp, SequenceNumber earliest_snapshot,
std::pair<SequenceNumber, SequenceNumber> keep_seqno_range,
const Slice& next_table_min_key, const std::string& full_history_ts_low) {
// The following example does not happen since
// CompactionOutput::ShouldStopBefore() always return false for the first
@@ -480,7 +470,7 @@ Status CompactionOutputs::AddRangeDels(
// Then meta.smallest will be set to comp_start_user_key@seqno
// and meta.largest will be set to comp_start_user_key@kMaxSequenceNumber
// which violates the assumption that meta.smallest should be <= meta.largest.
assert(!range_del_agg.IsEmpty());
assert(HasRangeDel());
FileMetaData& meta = current_output().meta;
const Comparator* ucmp = icmp.user_comparator();
InternalKey lower_bound_buf, upper_bound_buf;
@@ -589,19 +579,13 @@ Status CompactionOutputs::AddRangeDels(
assert(comp_end_user_key == nullptr || upper_bound == nullptr ||
ucmp->CompareWithoutTimestamp(ExtractUserKey(*upper_bound),
*comp_end_user_key) <= 0);
auto it = range_del_agg.NewIterator(lower_bound, upper_bound);
auto it = range_del_agg_->NewIterator(lower_bound, upper_bound);
Slice last_tombstone_start_user_key{};
bool reached_lower_bound = false;
const ReadOptions read_options(Env::IOActivity::kCompaction);
for (it->SeekToFirst(); it->Valid(); it->Next()) {
auto tombstone = it->Tombstone();
auto kv = tombstone.Serialize();
// Filter out by seqno for per-key placement
if (tombstone.seq_ < keep_seqno_range.first ||
tombstone.seq_ >= keep_seqno_range.second) {
continue;
}
InternalKey tombstone_end = tombstone.SerializeEndKey();
// TODO: the underlying iterator should support clamping the bounds.
// tombstone_end.Encode is of form user_key@kMaxSeqno
@@ -760,10 +744,11 @@ Status CompactionOutputs::AddRangeDels(
}
void CompactionOutputs::FillFilesToCutForTtl() {
if (compaction_->immutable_options().compaction_style !=
if (compaction_->immutable_options()->compaction_style !=
kCompactionStyleLevel ||
compaction_->immutable_options().compaction_pri != kMinOverlappingRatio ||
compaction_->mutable_cf_options().ttl == 0 ||
compaction_->immutable_options()->compaction_pri !=
kMinOverlappingRatio ||
compaction_->mutable_cf_options()->ttl == 0 ||
compaction_->num_input_levels() < 2 || compaction_->bottommost_level()) {
return;
}
@@ -771,19 +756,20 @@ void CompactionOutputs::FillFilesToCutForTtl() {
// We define new file with the oldest ancestor time to be younger than 1/4
// TTL, and an old one to be older than 1/2 TTL time.
int64_t temp_current_time;
auto get_time_status = compaction_->immutable_options().clock->GetCurrentTime(
&temp_current_time);
auto get_time_status =
compaction_->immutable_options()->clock->GetCurrentTime(
&temp_current_time);
if (!get_time_status.ok()) {
return;
}
auto current_time = static_cast<uint64_t>(temp_current_time);
if (current_time < compaction_->mutable_cf_options().ttl) {
if (current_time < compaction_->mutable_cf_options()->ttl) {
return;
}
uint64_t old_age_thres =
current_time - compaction_->mutable_cf_options().ttl / 2;
current_time - compaction_->mutable_cf_options()->ttl / 2;
const std::vector<FileMetaData*>& olevel =
*(compaction_->inputs(compaction_->num_input_levels() - 1));
for (FileMetaData* file : olevel) {
@@ -793,15 +779,15 @@ void CompactionOutputs::FillFilesToCutForTtl() {
// of small files.
if (oldest_ancester_time < old_age_thres &&
file->fd.GetFileSize() >
compaction_->mutable_cf_options().target_file_size_base / 2) {
compaction_->mutable_cf_options()->target_file_size_base / 2) {
files_to_cut_for_ttl_.push_back(file);
}
}
}
CompactionOutputs::CompactionOutputs(const Compaction* compaction,
const bool is_proximal_level)
: compaction_(compaction), is_proximal_level_(is_proximal_level) {
const bool is_penultimate_level)
: compaction_(compaction), is_penultimate_level_(is_penultimate_level) {
partitioner_ = compaction->output_level() == 0
? nullptr
: compaction->CreateSstPartitioner();
+45 -59
View File
@@ -21,8 +21,7 @@ namespace ROCKSDB_NAMESPACE {
class CompactionOutputs;
using CompactionFileOpenFunc = std::function<Status(CompactionOutputs&)>;
using CompactionFileCloseFunc =
std::function<Status(const Status&, const ParsedInternalKey&, const Slice&,
const CompactionIterator*, CompactionOutputs&)>;
std::function<Status(CompactionOutputs&, const Status&, const Slice&)>;
// Files produced by subcompaction, most of the functions are used by
// compaction_job Open/Close compaction file functions.
@@ -31,36 +30,29 @@ class CompactionOutputs {
// compaction output file
struct Output {
Output(FileMetaData&& _meta, const InternalKeyComparator& _icmp,
bool _enable_hash, bool _finished, uint64_t precalculated_hash,
bool _is_proximal_level)
bool _enable_hash, bool _finished, uint64_t precalculated_hash)
: meta(std::move(_meta)),
validator(_icmp, _enable_hash, precalculated_hash),
finished(_finished),
is_proximal_level(_is_proximal_level) {}
finished(_finished) {}
FileMetaData meta;
OutputValidator validator;
bool finished;
bool is_proximal_level;
std::shared_ptr<const TableProperties> table_properties;
};
CompactionOutputs() = delete;
explicit CompactionOutputs(const Compaction* compaction,
const bool is_proximal_level);
bool IsProximalLevel() const { return is_proximal_level_; }
const bool is_penultimate_level);
// Add generated output to the list
void AddOutput(FileMetaData&& meta, const InternalKeyComparator& icmp,
bool enable_hash, bool finished = false,
uint64_t precalculated_hash = 0) {
outputs_.emplace_back(std::move(meta), icmp, enable_hash, finished,
precalculated_hash, is_proximal_level_);
precalculated_hash);
}
const std::vector<Output>& GetOutputs() const { return outputs_; }
// Set new table builder for the current output
void NewBuilder(const TableBuilderOptions& tboptions);
@@ -69,29 +61,33 @@ class CompactionOutputs {
file_writer_.reset(writer);
}
// TODO: Remove it when remote compaction support tiered compaction
void SetTotalBytes(uint64_t bytes) { stats_.bytes_written += bytes; }
void SetNumOutputRecords(uint64_t num) { stats_.num_output_records = num; }
// TODO: Move the BlobDB builder into CompactionOutputs
const std::vector<BlobFileAddition>& GetBlobFileAdditions() const {
if (is_proximal_level_) {
if (is_penultimate_level_) {
assert(blob_file_additions_.empty());
}
return blob_file_additions_;
}
std::vector<BlobFileAddition>* GetBlobFileAdditionsPtr() {
assert(!is_proximal_level_);
assert(!is_penultimate_level_);
return &blob_file_additions_;
}
bool HasBlobFileAdditions() const { return !blob_file_additions_.empty(); }
BlobGarbageMeter* CreateBlobGarbageMeter() {
assert(!is_proximal_level_);
assert(!is_penultimate_level_);
blob_garbage_meter_ = std::make_unique<BlobGarbageMeter>();
return blob_garbage_meter_.get();
}
BlobGarbageMeter* GetBlobGarbageMeter() const {
if (is_proximal_level_) {
if (is_penultimate_level_) {
// blobdb doesn't support per_key_placement yet
assert(blob_garbage_meter_ == nullptr);
return nullptr;
@@ -100,9 +96,8 @@ class CompactionOutputs {
}
void UpdateBlobStats() {
assert(!is_proximal_level_);
stats_.num_output_files_blob =
static_cast<int>(blob_file_additions_.size());
assert(!is_penultimate_level_);
stats_.num_output_files_blob = blob_file_additions_.size();
for (const auto& blob : blob_file_additions_) {
stats_.bytes_written_blob += blob.GetTotalBlobBytes();
}
@@ -112,12 +107,6 @@ class CompactionOutputs {
Status Finish(const Status& intput_status,
const SeqnoToTimeMapping& seqno_to_time_mapping);
// Update output table properties from already populated TableProperties.
// Used for remote compaction
void UpdateTableProperties(const TableProperties& table_properties) {
current_output().table_properties =
std::make_shared<TableProperties>(table_properties);
}
// Update output table properties from table builder
void UpdateTableProperties() {
current_output().table_properties =
@@ -171,10 +160,6 @@ class CompactionOutputs {
uint64_t NumEntries() const { return builder_->NumEntries(); }
uint64_t GetWorkerCPUMicros() const {
return worker_cpu_micros_ + (builder_ ? builder_->GetWorkerCPUMicros() : 0);
}
void ResetBuilder() {
builder_.reset();
current_output_file_size_ = 0;
@@ -190,16 +175,17 @@ class CompactionOutputs {
// @param next_table_min_key internal key lower bound for the next compaction
// output.
// @param full_history_ts_low used for range tombstone garbage collection.
Status AddRangeDels(
CompactionRangeDelAggregator& range_del_agg,
const Slice* comp_start_user_key, const Slice* comp_end_user_key,
CompactionIterationStats& range_del_out_stats, bool bottommost_level,
const InternalKeyComparator& icmp, SequenceNumber earliest_snapshot,
std::pair<SequenceNumber, SequenceNumber> keep_seqno_range,
const Slice& next_table_min_key, const std::string& full_history_ts_low);
Status AddRangeDels(const Slice* comp_start_user_key,
const Slice* comp_end_user_key,
CompactionIterationStats& range_del_out_stats,
bool bottommost_level, const InternalKeyComparator& icmp,
SequenceNumber earliest_snapshot,
const Slice& next_table_min_key,
const std::string& full_history_ts_low);
void SetNumOutputRecords(uint64_t num_output_records) {
stats_.num_output_records = num_output_records;
// if the outputs have range delete, range delete is also data
bool HasRangeDel() const {
return range_del_agg_ && !range_del_agg_->IsEmpty();
}
private:
@@ -261,29 +247,21 @@ class CompactionOutputs {
// close and open new compaction output with the functions provided.
Status AddToOutput(const CompactionIterator& c_iter,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func,
const ParsedInternalKey& prev_iter_output_internal_key);
const CompactionFileCloseFunc& close_file_func);
// Close the current output. `open_file_func` is needed for creating new file
// for range-dels only output file.
Status CloseOutput(const Status& curr_status,
CompactionRangeDelAggregator* range_del_agg,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func) {
Status status = curr_status;
// Handle subcompaction containing only range deletions. They could
// be dropped or sent to another output level, so this is only an
// over-approximate check for whether opening is needed.
if (status.ok() && !HasBuilder() && !HasOutput() && range_del_agg &&
!range_del_agg->IsEmpty()) {
// handle subcompaction containing only range deletions
if (status.ok() && !HasBuilder() && !HasOutput() && HasRangeDel()) {
status = open_file_func(*this);
}
if (HasBuilder()) {
const ParsedInternalKey empty_internal_key{};
const Slice empty_key{};
Status s = close_file_func(status, empty_internal_key, empty_key,
nullptr /* c_iter */, *this);
Status s = close_file_func(*this, status, empty_key);
if (!s.ok() && status.ok()) {
status = s;
}
@@ -303,6 +281,16 @@ class CompactionOutputs {
return outputs_.back();
}
// Assign the range_del_agg to the target output level. There's only one
// range-del-aggregator per compaction outputs, for
// output_to_penultimate_level compaction it is only assigned to the
// penultimate level.
void AssignRangeDelAggregator(
std::unique_ptr<CompactionRangeDelAggregator>&& range_del_agg) {
assert(range_del_agg_ == nullptr);
range_del_agg_ = std::move(range_del_agg);
}
const Compaction* compaction_;
// current output builder and writer
@@ -311,9 +299,6 @@ class CompactionOutputs {
uint64_t current_output_file_size_ = 0;
SequenceNumber smallest_preferred_seqno_ = kMaxSequenceNumber;
// Sum of all the GetWorkerCPUMicros() for all the closed builders so far.
uint64_t worker_cpu_micros_ = 0;
// all the compaction outputs so far
std::vector<Output> outputs_;
@@ -321,12 +306,13 @@ class CompactionOutputs {
std::vector<BlobFileAddition> blob_file_additions_;
std::unique_ptr<BlobGarbageMeter> blob_garbage_meter_;
// Per level's output stat
InternalStats::CompactionStats stats_;
// Basic compaction output stats for this level's outputs
InternalStats::CompactionOutputsStats stats_;
// indicate if this CompactionOutputs obj for proximal_level, should always
// indicate if this CompactionOutputs obj for penultimate_level, should always
// be false if per_key_placement feature is not enabled.
const bool is_proximal_level_;
const bool is_penultimate_level_;
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg_ = nullptr;
// partitioner information
std::string last_key_for_partitioner_;
@@ -380,7 +366,7 @@ class CompactionOutputs {
std::vector<size_t> level_ptrs_;
};
// helper struct to concatenate the last level and proximal level outputs
// helper struct to concatenate the last level and penultimate level outputs
// which could be replaced by std::ranges::join_view() in c++20
struct OutputIterator {
public:
+64 -84
View File
@@ -133,8 +133,7 @@ CompactionPicker::CompactionPicker(const ImmutableOptions& ioptions,
CompactionPicker::~CompactionPicker() = default;
// Delete this compaction from the list of running compactions.
void CompactionPicker::ReleaseCompactionFiles(Compaction* c,
const Status& status) {
void CompactionPicker::ReleaseCompactionFiles(Compaction* c, Status status) {
UnregisterCompaction(c);
if (!status.ok()) {
c->ResetNextCompactionIndex();
@@ -242,7 +241,7 @@ bool CompactionPicker::ExpandInputsToCleanCut(const std::string& /*cf_name*/,
GetRange(*inputs, &smallest, &largest);
inputs->clear();
vstorage->GetOverlappingInputs(level, &smallest, &largest, &inputs->files,
hint_index, &hint_index, true, nullptr,
hint_index, &hint_index, true,
next_smallest);
} while (inputs->size() > old_size);
@@ -272,8 +271,8 @@ bool CompactionPicker::RangeOverlapWithCompaction(
return true;
}
if (c->SupportsPerKeyPlacement()) {
if (c->OverlapProximalLevelOutputRange(smallest_user_key,
largest_user_key)) {
if (c->OverlapPenultimateLevelOutputRange(smallest_user_key,
largest_user_key)) {
return true;
}
}
@@ -284,7 +283,7 @@ bool CompactionPicker::RangeOverlapWithCompaction(
bool CompactionPicker::FilesRangeOverlapWithCompaction(
const std::vector<CompactionInputFiles>& inputs, int level,
int proximal_level) const {
int penultimate_level) const {
bool is_empty = true;
for (auto& in : inputs) {
if (!in.empty()) {
@@ -301,18 +300,18 @@ bool CompactionPicker::FilesRangeOverlapWithCompaction(
// files cannot be overlapped in the order of L0 files.
InternalKey smallest, largest;
GetRange(inputs, &smallest, &largest, Compaction::kInvalidLevel);
if (proximal_level != Compaction::kInvalidLevel) {
if (penultimate_level != Compaction::kInvalidLevel) {
if (ioptions_.compaction_style == kCompactionStyleUniversal) {
if (RangeOverlapWithCompaction(smallest.user_key(), largest.user_key(),
proximal_level)) {
penultimate_level)) {
return true;
}
} else {
InternalKey proximal_smallest, proximal_largest;
GetRange(inputs, &proximal_smallest, &proximal_largest, level);
if (RangeOverlapWithCompaction(proximal_smallest.user_key(),
proximal_largest.user_key(),
proximal_level)) {
InternalKey penultimate_smallest, penultimate_largest;
GetRange(inputs, &penultimate_smallest, &penultimate_largest, level);
if (RangeOverlapWithCompaction(penultimate_smallest.user_key(),
penultimate_largest.user_key(),
penultimate_level)) {
return true;
}
}
@@ -333,13 +332,11 @@ bool CompactionPicker::AreFilesInCompaction(
return false;
}
Compaction* CompactionPicker::PickCompactionForCompactFiles(
Compaction* CompactionPicker::CompactFiles(
const CompactionOptions& compact_options,
const std::vector<CompactionInputFiles>& input_files, int output_level,
VersionStorageInfo* vstorage, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options, uint32_t output_path_id,
std::optional<SequenceNumber> earliest_snapshot,
const SnapshotChecker* snapshot_checker) {
const MutableDBOptions& mutable_db_options, uint32_t output_path_id) {
#ifndef NDEBUG
assert(input_files.size());
// This compaction output should not overlap with a running compaction as
@@ -353,11 +350,11 @@ Compaction* CompactionPicker::PickCompactionForCompactFiles(
break;
}
}
assert(output_level == 0 || !FilesRangeOverlapWithCompaction(
input_files, output_level,
Compaction::EvaluateProximalLevel(
vstorage, mutable_cf_options, ioptions_,
start_level, output_level)));
assert(output_level == 0 ||
!FilesRangeOverlapWithCompaction(
input_files, output_level,
Compaction::EvaluatePenultimateLevel(vstorage, ioptions_,
start_level, output_level)));
#endif /* !NDEBUG */
CompressionType compression_type;
@@ -375,16 +372,14 @@ Compaction* CompactionPicker::PickCompactionForCompactFiles(
// without configurable `CompressionOptions`, which is inconsistent.
compression_type = compact_options.compression;
}
auto c = new Compaction(
vstorage, ioptions_, mutable_cf_options, mutable_db_options, input_files,
output_level, compact_options.output_file_size_limit,
mutable_cf_options.max_compaction_bytes, output_path_id, compression_type,
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
compact_options.output_temperature_override,
mutable_cf_options.default_write_temperature,
compact_options.max_subcompactions,
/* grandparents */ {}, earliest_snapshot, snapshot_checker,
CompactionReason::kManualCompaction);
/* grandparents */ {}, true);
RegisterCompaction(c);
return c;
}
@@ -465,8 +460,7 @@ bool CompactionPicker::SetupOtherInputs(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
VersionStorageInfo* vstorage, CompactionInputFiles* inputs,
CompactionInputFiles* output_level_inputs, int* parent_index,
int base_index, bool only_expand_towards_right,
const FileMetaData* starting_l0_file) {
int base_index, bool only_expand_towards_right) {
assert(!inputs->empty());
assert(output_level_inputs->empty());
const int input_level = inputs->level;
@@ -522,11 +516,11 @@ bool CompactionPicker::SetupOtherInputs(
// Round-robin compaction only allows expansion towards the larger side.
vstorage->GetOverlappingInputs(input_level, &smallest, &all_limit,
&expanded_inputs.files, base_index,
nullptr, true, starting_l0_file);
nullptr);
} else {
vstorage->GetOverlappingInputs(input_level, &all_start, &all_limit,
&expanded_inputs.files, base_index,
nullptr, true, starting_l0_file);
nullptr);
}
uint64_t expanded_inputs_size = TotalFileSize(expanded_inputs.files);
if (!ExpandInputsToCleanCut(cf_name, vstorage, &expanded_inputs)) {
@@ -605,7 +599,7 @@ void CompactionPicker::GetGrandparents(
}
}
Compaction* CompactionPicker::PickCompactionForCompactRange(
Compaction* CompactionPicker::CompactRange(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
int input_level, int output_level,
@@ -621,8 +615,8 @@ Compaction* CompactionPicker::PickCompactionForCompactRange(
// Universal compaction with more than one level always compacts all the
// files together to the last level.
assert(vstorage->num_levels() > 1);
int max_output_level = vstorage->MaxOutputLevel(
ioptions_.cf_allow_ingest_behind || ioptions_.allow_ingest_behind);
int max_output_level =
vstorage->MaxOutputLevel(ioptions_.allow_ingest_behind);
// DBImpl::CompactRange() set output level to be the last level
assert(output_level == max_output_level);
// DBImpl::RunManualCompaction will make full range for universal compaction
@@ -663,9 +657,8 @@ Compaction* CompactionPicker::PickCompactionForCompactRange(
// overlaping outputs in the same level.
if (FilesRangeOverlapWithCompaction(
inputs, output_level,
Compaction::EvaluateProximalLevel(vstorage, mutable_cf_options,
ioptions_, start_level,
output_level))) {
Compaction::EvaluatePenultimateLevel(vstorage, ioptions_,
start_level, output_level))) {
// This compaction output could potentially conflict with the output
// of a currently running compaction, we cannot run it.
*manual_conflict = true;
@@ -681,11 +674,11 @@ Compaction* CompactionPicker::PickCompactionForCompactRange(
compact_range_options.target_path_id,
GetCompressionType(vstorage, mutable_cf_options, output_level, 1),
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
Temperature::kUnknown, compact_range_options.max_subcompactions,
/* grandparents */ {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, CompactionReason::kManualCompaction,
trim_ts, /* score */ -1,
/* l0_files_might_overlap */ true,
mutable_cf_options.default_write_temperature,
compact_range_options.max_subcompactions,
/* grandparents */ {}, /* is manual */ true, trim_ts, /* score */ -1,
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
CompactionReason::kUnknown,
compact_range_options.blob_garbage_collection_policy,
compact_range_options.blob_garbage_collection_age_cutoff);
@@ -723,14 +716,14 @@ Compaction* CompactionPicker::PickCompactionForCompactRange(
// two files overlap.
if (input_level > 0) {
const uint64_t limit = mutable_cf_options.max_compaction_bytes;
uint64_t input_level_total = 0;
int hint_index = -1;
assert(!inputs.empty());
// Always include first file for progress.
uint64_t input_level_total = inputs[0]->fd.GetFileSize();
InternalKey* smallest = &(inputs[0]->smallest);
InternalKey* smallest = nullptr;
InternalKey* largest = nullptr;
for (size_t i = 1; i < inputs.size(); ++i) {
// Consider whether to include inputs[i]
for (size_t i = 0; i + 1 < inputs.size(); ++i) {
if (!smallest) {
smallest = &inputs[i]->smallest;
}
largest = &inputs[i]->largest;
uint64_t input_file_size = inputs[i]->fd.GetFileSize();
@@ -746,11 +739,13 @@ Compaction* CompactionPicker::PickCompactionForCompactRange(
input_level_total += input_file_size;
if (input_level_total + output_level_total > limit) {
// To ensure compaction size is <= limit, leave out inputs from
// index i onwards.
if (input_level_total + output_level_total >= limit) {
covering_the_whole_range = false;
inputs.files.resize(i);
// still include the current file, so the compaction could be larger
// than max_compaction_bytes, which is also to make sure the compaction
// can make progress even `max_compaction_bytes` is small (e.g. smaller
// than an SST file).
inputs.files.resize(i + 1);
break;
}
}
@@ -819,9 +814,6 @@ Compaction* CompactionPicker::PickCompactionForCompactRange(
output_level = vstorage->base_level();
assert(output_level > 0);
}
for (int i = input_level + 1; i < output_level; i++) {
assert(vstorage->NumLevelFiles(i) == 0);
}
output_level_inputs.level = output_level;
if (input_level != output_level) {
int parent_index = -1;
@@ -850,9 +842,8 @@ Compaction* CompactionPicker::PickCompactionForCompactRange(
// overlaping outputs in the same level.
if (FilesRangeOverlapWithCompaction(
compaction_inputs, output_level,
Compaction::EvaluateProximalLevel(vstorage, mutable_cf_options,
ioptions_, input_level,
output_level))) {
Compaction::EvaluatePenultimateLevel(vstorage, ioptions_, input_level,
output_level))) {
// This compaction output could potentially conflict with the output
// of a currently running compaction, we cannot run it.
*manual_conflict = true;
@@ -872,11 +863,11 @@ Compaction* CompactionPicker::PickCompactionForCompactRange(
GetCompressionType(vstorage, mutable_cf_options, output_level,
vstorage->base_level()),
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
Temperature::kUnknown, compact_range_options.max_subcompactions,
std::move(grandparents),
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
CompactionReason::kManualCompaction, trim_ts, /* score */ -1,
/* l0_files_might_overlap */ true,
mutable_cf_options.default_write_temperature,
compact_range_options.max_subcompactions, std::move(grandparents),
/* is manual */ true, trim_ts, /* score */ -1,
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
CompactionReason::kUnknown,
compact_range_options.blob_garbage_collection_policy,
compact_range_options.blob_garbage_collection_age_cutoff);
@@ -1053,12 +1044,10 @@ Status CompactionPicker::SanitizeCompactionInputFilesForAllLevels(
}
Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
std::unordered_set<uint64_t>* input_files, const int output_level,
Version* version,
std::unordered_set<uint64_t>* input_files,
const ColumnFamilyMetaData& cf_meta, const int output_level,
const VersionStorageInfo* vstorage,
std::vector<CompactionInputFiles>* converted_input_files) const {
ColumnFamilyMetaData cf_meta;
version->GetColumnFamilyMetaData(&cf_meta);
assert(static_cast<int>(cf_meta.levels.size()) - 1 ==
cf_meta.levels[cf_meta.levels.size() - 1].level);
assert(converted_input_files);
@@ -1129,8 +1118,7 @@ Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
}
s = GetCompactionInputsFromFileNumbers(converted_input_files, input_files,
version->storage_info(),
CompactionOptions());
vstorage, CompactionOptions());
if (!s.ok()) {
return s;
}
@@ -1138,9 +1126,9 @@ Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
if (output_level != 0 &&
FilesRangeOverlapWithCompaction(
*converted_input_files, output_level,
Compaction::EvaluateProximalLevel(
version->storage_info(), version->GetMutableCFOptions(),
ioptions_, (*converted_input_files)[0].level, output_level))) {
Compaction::EvaluatePenultimateLevel(
vstorage, ioptions_, (*converted_input_files)[0].level,
output_level))) {
return Status::Aborted(
"A running compaction is writing to the same output level(s) in an "
"overlapping key range");
@@ -1155,7 +1143,7 @@ void CompactionPicker::RegisterCompaction(Compaction* c) {
assert(ioptions_.compaction_style != kCompactionStyleLevel ||
c->output_level() == 0 ||
!FilesRangeOverlapWithCompaction(*c->inputs(), c->output_level(),
c->GetProximalLevel()));
c->GetPenultimateLevel()));
// CompactionReason::kExternalSstIngestion's start level is just a placeholder
// number without actual meaning as file ingestion technically does not have
// an input level like other compactions
@@ -1182,8 +1170,7 @@ void CompactionPicker::UnregisterCompaction(Compaction* c) {
void CompactionPicker::PickFilesMarkedForCompaction(
const std::string& cf_name, VersionStorageInfo* vstorage, int* start_level,
int* output_level, CompactionInputFiles* start_level_inputs,
std::function<bool(const FileMetaData*)> skip_marked_file) {
int* output_level, CompactionInputFiles* start_level_inputs) {
if (vstorage->FilesMarkedForCompaction().empty()) {
return;
}
@@ -1193,9 +1180,6 @@ void CompactionPicker::PickFilesMarkedForCompaction(
// If this assert() fails that means that some function marked some
// files as being_compacted, but didn't call ComputeCompactionScore()
assert(!level_file.second->being_compacted);
if (skip_marked_file(level_file.second)) {
return false;
}
*start_level = level_file.first;
*output_level =
(*start_level == 0) ? vstorage->base_level() : *start_level + 1;
@@ -1232,7 +1216,7 @@ void CompactionPicker::PickFilesMarkedForCompaction(
bool CompactionPicker::GetOverlappingL0Files(
VersionStorageInfo* vstorage, CompactionInputFiles* start_level_inputs,
int output_level, int* parent_index, const FileMetaData* starting_l0_file) {
int output_level, int* parent_index) {
// Two level 0 compaction won't run at the same time, so don't need to worry
// about files on level 0 being compacted.
assert(level0_compactions_in_progress()->empty());
@@ -1243,11 +1227,7 @@ bool CompactionPicker::GetOverlappingL0Files(
// which will include the picked file.
start_level_inputs->files.clear();
vstorage->GetOverlappingInputs(0, &smallest, &largest,
&(start_level_inputs->files),
/*hint_index=*/-1,
/*file_index=*/nullptr,
/*expand_range=*/true,
/*starting_l0_file=*/starting_l0_file);
&(start_level_inputs->files));
// If we include more L0 files in the same compaction run it can
// cause the 'smallest' and 'largest' key to get extended to a
+45 -67
View File
@@ -16,7 +16,6 @@
#include <vector>
#include "db/compaction/compaction.h"
#include "db/snapshot_checker.h"
#include "db/version_set.h"
#include "options/cf_options.h"
#include "rocksdb/env.h"
@@ -56,26 +55,24 @@ class CompactionPicker {
// Returns nullptr if there is no compaction to be done.
// Otherwise returns a pointer to a heap-allocated object that
// describes the compaction. Caller should delete the result.
// Currently, only universal compaction will query existing snapshots and
// pass it to aid compaction picking. And it's only passed when user-defined
// timestamps is not enabled. The other compaction styles do not pass or use
// `existing_snapshots` or `snapshot_checker`.
virtual Compaction* PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& existing_snapshots,
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
LogBuffer* log_buffer, bool require_max_output_level) = 0;
virtual Compaction* PickCompaction(const std::string& cf_name,
const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
VersionStorageInfo* vstorage,
LogBuffer* log_buffer) = 0;
// Return a compaction object for compacting the range [begin,end] in
// the specified level. Returns nullptr if there is nothing in that
// level that overlaps the specified range. Caller should delete
// the result.
//
// The returned Compaction might not include the whole requested range.
// In that case, compaction_end will be set to the next key that needs
// compacting. In case the compaction will compact the whole range,
// compaction_end will be set to nullptr.
// Client is responsible for compaction_end storage -- when called,
// *compaction_end should point to valid InternalKey!
// REQUIRES: If not compacting all levels (input_level == kCompactAllLevels),
// then levels between input_level and output_level should be empty.
virtual Compaction* PickCompactionForCompactRange(
virtual Compaction* CompactRange(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
int input_level, int output_level,
@@ -99,14 +96,15 @@ class CompactionPicker {
// non-ok status with specific reason.
//
Status SanitizeAndConvertCompactionInputFiles(
std::unordered_set<uint64_t>* input_files, const int output_level,
Version* version,
std::unordered_set<uint64_t>* input_files,
const ColumnFamilyMetaData& cf_meta, const int output_level,
const VersionStorageInfo* vstorage,
std::vector<CompactionInputFiles>* converted_input_files) const;
// Free up the files that participated in a compaction
//
// Requirement: DB mutex held
void ReleaseCompactionFiles(Compaction* c, const Status& status);
void ReleaseCompactionFiles(Compaction* c, Status status);
// Returns true if any one of the specified files are being compacted
bool AreFilesInCompaction(const std::vector<FileMetaData*>& files);
@@ -117,17 +115,12 @@ class CompactionPicker {
// Caller must provide a set of input files that has been passed through
// `SanitizeAndConvertCompactionInputFiles` earlier. The lock should not be
// released between that call and this one.
//
// TODO - Remove default values for earliest_snapshot and snapshot_checker
// and require all callers to pass them in so that DB::CompactFiles() can
// also benefit from Standalone Range Tombstone Optimization
Compaction* PickCompactionForCompactFiles(
const CompactionOptions& compact_options,
const std::vector<CompactionInputFiles>& input_files, int output_level,
VersionStorageInfo* vstorage, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options, uint32_t output_path_id,
std::optional<SequenceNumber> earliest_snapshot = std::nullopt,
const SnapshotChecker* snapshot_checker = nullptr);
Compaction* CompactFiles(const CompactionOptions& compact_options,
const std::vector<CompactionInputFiles>& input_files,
int output_level, VersionStorageInfo* vstorage,
const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
uint32_t output_path_id);
// Converts a set of compaction input file numbers into
// a list of CompactionInputFiles.
@@ -143,12 +136,6 @@ class CompactionPicker {
return !level0_compactions_in_progress_.empty();
}
// Is any compaction in progress
bool IsCompactionInProgress() const {
return !(level0_compactions_in_progress_.empty() &&
compactions_in_progress_.empty());
}
// Return true if the passed key range overlap with a compaction output
// that is currently running.
bool RangeOverlapWithCompaction(const Slice& smallest_user_key,
@@ -201,36 +188,29 @@ class CompactionPicker {
// key range of a currently running compaction.
bool FilesRangeOverlapWithCompaction(
const std::vector<CompactionInputFiles>& inputs, int level,
int proximal_level) const;
int penultimate_level) const;
// @param starting_l0_file If not null, restricts L0 file selection to only
// include files at or older than starting_l0_file.
bool SetupOtherInputs(const std::string& cf_name,
const MutableCFOptions& mutable_cf_options,
VersionStorageInfo* vstorage,
CompactionInputFiles* inputs,
CompactionInputFiles* output_level_inputs,
int* parent_index, int base_index,
bool only_expand_towards_right = false,
const FileMetaData* starting_l0_file = nullptr);
bool only_expand_towards_right = false);
void GetGrandparents(VersionStorageInfo* vstorage,
const CompactionInputFiles& inputs,
const CompactionInputFiles& output_level_inputs,
std::vector<FileMetaData*>* grandparents);
void PickFilesMarkedForCompaction(
const std::string& cf_name, VersionStorageInfo* vstorage,
int* start_level, int* output_level,
CompactionInputFiles* start_level_inputs,
std::function<bool(const FileMetaData*)> skip_marked_file);
void PickFilesMarkedForCompaction(const std::string& cf_name,
VersionStorageInfo* vstorage,
int* start_level, int* output_level,
CompactionInputFiles* start_level_inputs);
// @param starting_l0_file If not null, restricts L0 file selection to only
// include files at or older than starting_l0_file.
bool GetOverlappingL0Files(VersionStorageInfo* vstorage,
CompactionInputFiles* start_level_inputs,
int output_level, int* parent_index,
const FileMetaData* starting_l0_file = nullptr);
int output_level, int* parent_index);
// Register this compaction in the set of running compactions
void RegisterCompaction(Compaction* c);
@@ -277,29 +257,27 @@ class NullCompactionPicker : public CompactionPicker {
virtual ~NullCompactionPicker() {}
// Always return "nullptr"
Compaction* PickCompaction(
const std::string& /*cf_name*/,
const MutableCFOptions& /*mutable_cf_options*/,
const MutableDBOptions& /*mutable_db_options*/,
const std::vector<SequenceNumber>& /*existing_snapshots*/,
const SnapshotChecker* /*snapshot_checker*/,
VersionStorageInfo* /*vstorage*/, LogBuffer* /* log_buffer */,
bool /*require_max_output_level*/ = false) override {
Compaction* PickCompaction(const std::string& /*cf_name*/,
const MutableCFOptions& /*mutable_cf_options*/,
const MutableDBOptions& /*mutable_db_options*/,
VersionStorageInfo* /*vstorage*/,
LogBuffer* /* log_buffer */) override {
return nullptr;
}
// Always return "nullptr"
Compaction* PickCompactionForCompactRange(
const std::string& /*cf_name*/,
const MutableCFOptions& /*mutable_cf_options*/,
const MutableDBOptions& /*mutable_db_options*/,
VersionStorageInfo* /*vstorage*/, int /*input_level*/,
int /*output_level*/,
const CompactRangeOptions& /*compact_range_options*/,
const InternalKey* /*begin*/, const InternalKey* /*end*/,
InternalKey** /*compaction_end*/, bool* /*manual_conflict*/,
uint64_t /*max_file_num_to_ignore*/,
const std::string& /*trim_ts*/) override {
Compaction* CompactRange(const std::string& /*cf_name*/,
const MutableCFOptions& /*mutable_cf_options*/,
const MutableDBOptions& /*mutable_db_options*/,
VersionStorageInfo* /*vstorage*/,
int /*input_level*/, int /*output_level*/,
const CompactRangeOptions& /*compact_range_options*/,
const InternalKey* /*begin*/,
const InternalKey* /*end*/,
InternalKey** /*compaction_end*/,
bool* /*manual_conflict*/,
uint64_t /*max_file_num_to_ignore*/,
const std::string& /*trim_ts*/) override {
return nullptr;
}
+80 -71
View File
@@ -79,14 +79,10 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
FileMetaData* f = *ritr;
assert(f);
if (f->fd.table_reader && f->fd.table_reader->GetTableProperties()) {
uint64_t newest_key_time = f->TryGetNewestKeyTime();
uint64_t creation_time =
f->fd.table_reader->GetTableProperties()->creation_time;
uint64_t est_newest_key_time = newest_key_time == kUnknownNewestKeyTime
? creation_time
: newest_key_time;
if (est_newest_key_time == kUnknownNewestKeyTime ||
est_newest_key_time >= (current_time - mutable_cf_options.ttl)) {
if (creation_time == 0 ||
creation_time >= (current_time - mutable_cf_options.ttl)) {
break;
}
}
@@ -106,29 +102,26 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
}
for (const auto& f : inputs[0].files) {
assert(f);
uint64_t newest_key_time = f->TryGetNewestKeyTime();
uint64_t creation_time = 0;
assert(f);
if (f->fd.table_reader && f->fd.table_reader->GetTableProperties()) {
creation_time = f->fd.table_reader->GetTableProperties()->creation_time;
}
uint64_t est_newest_key_time = newest_key_time == kUnknownNewestKeyTime
? creation_time
: newest_key_time;
ROCKS_LOG_BUFFER(log_buffer,
"[%s] FIFO compaction: picking file %" PRIu64
" with estimated newest key time %" PRIu64 " for deletion",
cf_name.c_str(), f->fd.GetNumber(), est_newest_key_time);
" with creation time %" PRIu64 " for deletion",
cf_name.c_str(), f->fd.GetNumber(), creation_time);
}
Compaction* c = new Compaction(
vstorage, ioptions_, mutable_cf_options, mutable_db_options,
std::move(inputs), 0, 0, 0, 0, kNoCompression,
mutable_cf_options.compression_opts, Temperature::kUnknown,
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, CompactionReason::kFIFOTtl,
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
/* max_subcompactions */ 0, {}, /* is manual */ false,
/* trim_ts */ "", vstorage->CompactionScore(0),
/* l0_files_might_overlap */ true);
/* is deletion compaction */ true, /* l0_files_might_overlap */ true,
CompactionReason::kFIFOTtl);
return c;
}
@@ -193,13 +186,13 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
{comp_inputs}, 0, 16 * 1024 * 1024 /* output file size limit */,
0 /* max compaction bytes, not applicable */,
0 /* output path ID */, mutable_cf_options.compression,
mutable_cf_options.compression_opts, Temperature::kUnknown,
0 /* max_subcompactions */, {},
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
CompactionReason::kFIFOReduceNumFiles,
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
0 /* max_subcompactions */, {}, /* is manual */ false,
/* trim_ts */ "", vstorage->CompactionScore(0),
/* l0_files_might_overlap */ true);
/* is deletion compaction */ false,
/* l0_files_might_overlap */ true,
CompactionReason::kFIFOReduceNumFiles);
return c;
}
}
@@ -256,9 +249,6 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
// better serves a major type of FIFO use cases where smaller keys are
// associated with older data.
for (const auto& f : last_level_files) {
if (f->being_compacted) {
continue;
}
total_size -= f->fd.file_size;
inputs[0].files.push_back(f);
char tmp_fsize[16];
@@ -292,18 +282,19 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
/* target_file_size */ 0,
/* max_compaction_bytes */ 0,
/* output_path_id */ 0, kNoCompression,
mutable_cf_options.compression_opts, Temperature::kUnknown,
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, CompactionReason::kFIFOMaxSize,
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
/* max_subcompactions */ 0, {}, /* is manual */ false,
/* trim_ts */ "", vstorage->CompactionScore(0),
/* l0_files_might_overlap */ true);
/* is deletion compaction */ true,
/* l0_files_might_overlap */ true, CompactionReason::kFIFOMaxSize);
return c;
}
Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
LogBuffer* log_buffer) const {
LogBuffer* log_buffer) {
const std::vector<FileTemperatureAge>& ages =
mutable_cf_options.compaction_options_fifo
.file_temperature_age_thresholds;
@@ -353,49 +344,73 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
Temperature compaction_target_temp = Temperature::kLastTemperature;
if (current_time > min_age) {
uint64_t create_time_threshold = current_time - min_age;
uint64_t compaction_size = 0;
// We will ideally identify a file qualifying for temperature change by
// knowing the timestamp for the youngest entry in the file. However, right
// now we don't have the information. We infer it by looking at timestamp of
// the previous file's (which is just younger) oldest entry's timestamp.
Temperature cur_target_temp;
// avoid index underflow
assert(level_files.size() >= 1);
for (size_t index = level_files.size(); index >= 1; --index) {
for (size_t index = level_files.size() - 1; index >= 1; --index) {
// Try to add cur_file to compaction inputs.
FileMetaData* cur_file = level_files[index - 1];
FileMetaData* prev_file = index < 2 ? nullptr : level_files[index - 2];
FileMetaData* cur_file = level_files[index];
// prev_file is just younger than cur_file
FileMetaData* prev_file = level_files[index - 1];
if (cur_file->being_compacted) {
// Should not happen since we check for
// `level0_compactions_in_progress_` above. Here we simply just don't
// schedule anything.
return nullptr;
}
uint64_t est_newest_key_time = cur_file->TryGetNewestKeyTime(prev_file);
// Newer file could have newest_key_time populated
if (est_newest_key_time == kUnknownNewestKeyTime) {
continue;
}
if (est_newest_key_time > create_time_threshold) {
uint64_t oldest_ancestor_time = prev_file->TryGetOldestAncesterTime();
if (oldest_ancestor_time == kUnknownOldestAncesterTime) {
// Older files might not have enough information. It is possible to
// handle these files by looking at newer files, but maintaining the
// logic isn't worth it.
break;
}
Temperature cur_target_temp = ages[0].temperature;
if (oldest_ancestor_time > create_time_threshold) {
// cur_file is too fresh
break;
}
cur_target_temp = ages[0].temperature;
for (size_t i = 1; i < ages.size(); ++i) {
if (current_time >= ages[i].age &&
est_newest_key_time <= current_time - ages[i].age) {
oldest_ancestor_time <= current_time - ages[i].age) {
cur_target_temp = ages[i].temperature;
}
}
if (cur_file->temperature == cur_target_temp) {
continue;
if (inputs[0].empty()) {
continue;
} else {
break;
}
}
// cur_file needs to change temperature
assert(compaction_target_temp == Temperature::kLastTemperature);
compaction_target_temp = cur_target_temp;
inputs[0].files.push_back(cur_file);
ROCKS_LOG_BUFFER(log_buffer,
"[%s] FIFO compaction: picking file %" PRIu64
" with estimated newest key time %" PRIu64
" and temperature %s for temperature %s.",
cf_name.c_str(), cur_file->fd.GetNumber(),
est_newest_key_time,
temperature_to_string[cur_file->temperature].c_str(),
temperature_to_string[cur_target_temp].c_str());
break;
if (compaction_target_temp == Temperature::kLastTemperature) {
assert(inputs[0].empty());
compaction_target_temp = cur_target_temp;
} else if (cur_target_temp != compaction_target_temp) {
assert(!inputs[0].empty());
break;
}
if (inputs[0].empty() || compaction_size + cur_file->fd.GetFileSize() <=
mutable_cf_options.max_compaction_bytes) {
inputs[0].files.push_back(cur_file);
compaction_size += cur_file->fd.GetFileSize();
ROCKS_LOG_BUFFER(
log_buffer,
"[%s] FIFO compaction: picking file %" PRIu64
" with next file's oldest time %" PRIu64 " for temperature %s.",
cf_name.c_str(), cur_file->fd.GetNumber(), oldest_ancestor_time,
temperature_to_string[cur_target_temp].c_str());
}
if (compaction_size > mutable_cf_options.max_compaction_bytes) {
break;
}
}
}
@@ -403,28 +418,24 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
return nullptr;
}
assert(compaction_target_temp != Temperature::kLastTemperature);
// Only compact one file at a time.
assert(inputs.size() == 1);
assert(inputs[0].size() == 1);
Compaction* c = new Compaction(
vstorage, ioptions_, mutable_cf_options, mutable_db_options,
std::move(inputs), 0, 0 /* output file size limit */,
0 /* max compaction bytes, not applicable */, 0 /* output path ID */,
mutable_cf_options.compression, mutable_cf_options.compression_opts,
compaction_target_temp,
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, CompactionReason::kChangeTemperature,
/* trim_ts */ "", vstorage->CompactionScore(0),
/* l0_files_might_overlap */ true);
/* max_subcompactions */ 0, {}, /* is manual */ false, /* trim_ts */ "",
vstorage->CompactionScore(0),
/* is deletion compaction */ false, /* l0_files_might_overlap */ true,
CompactionReason::kChangeTemperature);
return c;
}
Compaction* FIFOCompactionPicker::PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& /* existing_snapshots */,
const SnapshotChecker* /* snapshot_checker */, VersionStorageInfo* vstorage,
LogBuffer* log_buffer, bool /* require_max_output_level*/) {
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
LogBuffer* log_buffer) {
Compaction* c = nullptr;
if (mutable_cf_options.ttl > 0) {
c = PickTTLCompaction(cf_name, mutable_cf_options, mutable_db_options,
@@ -442,7 +453,7 @@ Compaction* FIFOCompactionPicker::PickCompaction(
return c;
}
Compaction* FIFOCompactionPicker::PickCompactionForCompactRange(
Compaction* FIFOCompactionPicker::CompactRange(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
int input_level, int output_level,
@@ -458,10 +469,8 @@ Compaction* FIFOCompactionPicker::PickCompactionForCompactRange(
assert(output_level == 0);
*compaction_end = nullptr;
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, ioptions_.logger);
Compaction* c =
PickCompaction(cf_name, mutable_cf_options, mutable_db_options,
/*existing_snapshots*/ {}, /*snapshot_checker*/ nullptr,
vstorage, &log_buffer);
Compaction* c = PickCompaction(cf_name, mutable_cf_options,
mutable_db_options, vstorage, &log_buffer);
log_buffer.FlushBufferToLog();
return c;
}
+16 -17
View File
@@ -18,22 +18,22 @@ class FIFOCompactionPicker : public CompactionPicker {
const InternalKeyComparator* icmp)
: CompactionPicker(ioptions, icmp) {}
Compaction* PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& /* existing_snapshots */,
const SnapshotChecker* /* snapshot_checker */,
VersionStorageInfo* version, LogBuffer* log_buffer,
bool /* require_max_output_level*/ = false) override;
Compaction* PickCompaction(const std::string& cf_name,
const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
VersionStorageInfo* version,
LogBuffer* log_buffer) override;
Compaction* PickCompactionForCompactRange(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
int input_level, int output_level,
const CompactRangeOptions& compact_range_options,
const InternalKey* begin, const InternalKey* end,
InternalKey** compaction_end, bool* manual_conflict,
uint64_t max_file_num_to_ignore, const std::string& trim_ts) override;
Compaction* CompactRange(const std::string& cf_name,
const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
VersionStorageInfo* vstorage, int input_level,
int output_level,
const CompactRangeOptions& compact_range_options,
const InternalKey* begin, const InternalKey* end,
InternalKey** compaction_end, bool* manual_conflict,
uint64_t max_file_num_to_ignore,
const std::string& trim_ts) override;
// The maximum allowed output level. Always returns 0.
int MaxOutputLevel() const override { return 0; }
@@ -53,10 +53,9 @@ class FIFOCompactionPicker : public CompactionPicker {
VersionStorageInfo* version,
LogBuffer* log_buffer);
// Will pick one file to compact at a time, starting from the oldest file.
Compaction* PickTemperatureChangeCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
LogBuffer* log_buffer) const;
LogBuffer* log_buffer);
};
} // namespace ROCKSDB_NAMESPACE
+18 -30
View File
@@ -145,6 +145,7 @@ class LevelCompactionBuilder {
int parent_index_ = -1;
int base_index_ = -1;
double start_level_score_ = 0;
bool is_manual_ = false;
bool is_l0_trivial_move_ = false;
CompactionInputFiles start_level_inputs_;
std::vector<CompactionInputFiles> compaction_inputs_;
@@ -261,10 +262,7 @@ void LevelCompactionBuilder::SetupInitialFiles() {
parent_index_ = base_index_ = -1;
compaction_picker_->PickFilesMarkedForCompaction(
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_,
/*skip_marked_file*/ [](const FileMetaData* /* file */) {
return false;
});
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_);
if (!start_level_inputs_.empty()) {
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
return;
@@ -413,9 +411,8 @@ void LevelCompactionBuilder::SetupOtherFilesWithRoundRobinExpansion() {
&tmp_start_level_inputs) ||
compaction_picker_->FilesRangeOverlapWithCompaction(
{tmp_start_level_inputs}, output_level_,
Compaction::EvaluateProximalLevel(vstorage_, mutable_cf_options_,
ioptions_, start_level_,
output_level_))) {
Compaction::EvaluatePenultimateLevel(
vstorage_, ioptions_, start_level_, output_level_))) {
// Constraint 1a
tmp_start_level_inputs.clear();
return;
@@ -489,9 +486,8 @@ bool LevelCompactionBuilder::SetupOtherInputsIfNeeded() {
// We need to disallow this from happening.
if (compaction_picker_->FilesRangeOverlapWithCompaction(
compaction_inputs_, output_level_,
Compaction::EvaluateProximalLevel(vstorage_, mutable_cf_options_,
ioptions_, start_level_,
output_level_))) {
Compaction::EvaluatePenultimateLevel(
vstorage_, ioptions_, start_level_, output_level_))) {
// This compaction output could potentially conflict with the output
// of a currently running compaction, we cannot run it.
return false;
@@ -557,11 +553,10 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
GetCompressionType(vstorage_, mutable_cf_options_, output_level_,
vstorage_->base_level()),
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level_),
Temperature::kUnknown,
/* max_subcompactions */ 0, std::move(grandparents_),
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
compaction_reason_,
/* trim_ts */ "", start_level_score_, l0_files_might_overlap);
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
/* trim_ts */ "", start_level_score_, false /* deletion_compaction */,
l0_files_might_overlap, compaction_reason_);
// If it's level 0 compaction, make sure we don't execute any other level 0
// compactions in parallel
@@ -844,9 +839,8 @@ bool LevelCompactionBuilder::PickFileToCompact() {
&start_level_inputs_) ||
compaction_picker_->FilesRangeOverlapWithCompaction(
{start_level_inputs_}, output_level_,
Compaction::EvaluateProximalLevel(vstorage_, mutable_cf_options_,
ioptions_, start_level_,
output_level_))) {
Compaction::EvaluatePenultimateLevel(
vstorage_, ioptions_, start_level_, output_level_))) {
// A locked (pending compaction) input-level file was pulled in due to
// user-key overlap.
start_level_inputs_.clear();
@@ -931,15 +925,11 @@ bool LevelCompactionBuilder::PickSizeBasedIntraL0Compaction() {
}
uint64_t l0_size = 0;
for (const auto& file : l0_files) {
assert(file->compensated_file_size >= file->fd.GetFileSize());
// Compact down L0s with more deletions.
l0_size += file->compensated_file_size;
l0_size += file->fd.GetFileSize();
}
// Avoid L0->Lbase compactions that are inefficient for write-amp.
const double kMultiplier =
std::max(10.0, mutable_cf_options_.max_bytes_for_level_multiplier) * 2;
const uint64_t min_lbase_size = MultiplyCheckOverflow(l0_size, kMultiplier);
const uint64_t min_lbase_size =
l0_size * static_cast<uint64_t>(std::max(
10.0, mutable_cf_options_.max_bytes_for_level_multiplier));
assert(min_lbase_size >= l0_size);
const std::vector<FileMetaData*>& lbase_files =
vstorage_->LevelFiles(/*level=*/base_level);
@@ -973,10 +963,8 @@ bool LevelCompactionBuilder::PickSizeBasedIntraL0Compaction() {
Compaction* LevelCompactionPicker::PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& /*existing_snapshots */,
const SnapshotChecker* /*snapshot_checker*/, VersionStorageInfo* vstorage,
LogBuffer* log_buffer, bool /* require_max_output_level*/) {
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
LogBuffer* log_buffer) {
LevelCompactionBuilder builder(cf_name, vstorage, this, log_buffer,
mutable_cf_options, ioptions_,
mutable_db_options);
+5 -7
View File
@@ -20,13 +20,11 @@ class LevelCompactionPicker : public CompactionPicker {
LevelCompactionPicker(const ImmutableOptions& ioptions,
const InternalKeyComparator* icmp)
: CompactionPicker(ioptions, icmp) {}
Compaction* PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& /* existing_snapshots */,
const SnapshotChecker* /* snapshot_checker */,
VersionStorageInfo* vstorage, LogBuffer* log_buffer,
bool /*require_max_output_level*/ = false) override;
Compaction* PickCompaction(const std::string& cf_name,
const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
VersionStorageInfo* vstorage,
LogBuffer* log_buffer) override;
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
};
File diff suppressed because it is too large. Load diff
+191 -452
View File
@@ -35,11 +35,8 @@ class UniversalCompactionBuilder {
UniversalCompactionBuilder(
const ImmutableOptions& ioptions, const InternalKeyComparator* icmp,
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& existing_snapshots,
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
UniversalCompactionPicker* picker, LogBuffer* log_buffer,
bool require_max_output_level)
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
UniversalCompactionPicker* picker, LogBuffer* log_buffer)
: ioptions_(ioptions),
icmp_(icmp),
cf_name_(cf_name),
@@ -47,22 +44,7 @@ class UniversalCompactionBuilder {
mutable_db_options_(mutable_db_options),
vstorage_(vstorage),
picker_(picker),
log_buffer_(log_buffer),
require_max_output_level_(require_max_output_level),
allow_ingest_behind_(ioptions.cf_allow_ingest_behind ||
ioptions.allow_ingest_behind) {
assert(icmp_);
const auto* ucmp = icmp_->user_comparator();
assert(ucmp);
// These parameters are only passed when user-defined timestamp is not
// enabled.
if (ucmp->timestamp_size() == 0) {
earliest_snapshot_ = existing_snapshots.empty()
? kMaxSequenceNumber
: existing_snapshots.at(0);
snapshot_checker_ = snapshot_checker;
}
}
log_buffer_(log_buffer) {}
// Form and return the compaction object. The caller owns return object.
Compaction* PickCompaction();
@@ -70,15 +52,12 @@ class UniversalCompactionBuilder {
private:
struct SortedRun {
SortedRun(int _level, FileMetaData* _file, uint64_t _size,
uint64_t _compensated_file_size, bool _being_compacted,
bool _level_has_marked_standalone_rangedel)
uint64_t _compensated_file_size, bool _being_compacted)
: level(_level),
file(_file),
size(_size),
compensated_file_size(_compensated_file_size),
being_compacted(_being_compacted),
level_has_marked_standalone_rangedel(
_level_has_marked_standalone_rangedel) {
being_compacted(_being_compacted) {
assert(compensated_file_size > 0);
assert(level != 0 || file != nullptr);
}
@@ -100,180 +79,8 @@ class UniversalCompactionBuilder {
uint64_t size;
uint64_t compensated_file_size;
bool being_compacted;
// True if this level has any file that is a standalone range deletion file
// marked for compaction. Best effort is made to make only deletion
// triggered compaction pick this type of file.
bool level_has_marked_standalone_rangedel;
};
unsigned int GetMaxNumFilesToCompactBasedOnMaxReadAmp(
const int file_num_compaction_trigger, const unsigned int ratio,
int* num_sr_not_compacted_output, int* max_num_runs_output) const {
assert(num_sr_not_compacted_output);
assert(max_num_runs_output);
int max_num_runs =
mutable_cf_options_.compaction_options_universal.max_read_amp;
if (max_num_runs < 0) {
// any value < -1 is not valid
assert(max_num_runs == -1);
// By default, fall back to `level0_file_num_compaction_trigger`
max_num_runs = file_num_compaction_trigger;
} else if (max_num_runs == 0) {
if (mutable_cf_options_.compaction_options_universal.stop_style ==
kCompactionStopStyleTotalSize) {
// 0 means auto-tuning by RocksDB. We estimate max num run based on
// max_run_size, size_ratio and write buffer size:
// Assume the size of the lowest level size is equal to
// write_buffer_size. Each subsequent level is the max size without
// triggering size_ratio compaction. `max_num_runs` is the minimum
// number of levels required such that the target size of the
// largest level is at least `max_run_size_`.
max_num_runs = 1;
double cur_level_max_size =
static_cast<double>(mutable_cf_options_.write_buffer_size);
double total_run_size = 0;
while (cur_level_max_size < static_cast<double>(max_run_size_)) {
// This loop should not take too many iterations since
// cur_level_max_size at least doubles each iteration.
total_run_size += cur_level_max_size;
cur_level_max_size = (100.0 + ratio) / 100.0 * total_run_size;
++max_num_runs;
}
} else {
// TODO: implement the auto-tune logic for this stop style
max_num_runs = file_num_compaction_trigger;
}
} else {
// max_num_runs > 0, it's the limit on the number of sorted run
}
// Get the total number of sorted runs that are not being compacted
int num_sr_not_compacted = 0;
for (size_t i = 0; i < sorted_runs_.size(); i++) {
if (sorted_runs_[i].being_compacted == false &&
!sorted_runs_[i].level_has_marked_standalone_rangedel) {
num_sr_not_compacted++;
}
}
*num_sr_not_compacted_output = num_sr_not_compacted;
*max_num_runs_output = max_num_runs;
if (num_sr_not_compacted > max_num_runs) {
return num_sr_not_compacted - max_num_runs + 1;
} else {
return 0;
}
}
Compaction* MaybePickPeriodicCompaction(Compaction* const prev_picked_c) {
if (prev_picked_c != nullptr ||
vstorage_->FilesMarkedForPeriodicCompaction().empty()) {
return prev_picked_c;
}
// Always need to do a full compaction for periodic compaction.
Compaction* c = PickPeriodicCompaction();
TEST_SYNC_POINT_CALLBACK("PostPickPeriodicCompaction", c);
if (c != nullptr) {
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: picked for periodic compaction\n",
cf_name_.c_str());
}
return c;
}
Compaction* MaybePickSizeAmpCompaction(Compaction* const prev_picked_c,
int file_num_compaction_trigger) {
if (prev_picked_c != nullptr ||
sorted_runs_.size() <
static_cast<size_t>(file_num_compaction_trigger)) {
return prev_picked_c;
}
Compaction* c = PickCompactionToReduceSizeAmp();
if (c != nullptr) {
TEST_SYNC_POINT("PickCompactionToReduceSizeAmpReturnNonnullptr");
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: picked for size amp compaction \n",
cf_name_.c_str());
}
return c;
}
Compaction* MaybePickCompactionToReduceSortedRunsBasedFileRatio(
Compaction* const prev_picked_c, int file_num_compaction_trigger,
unsigned int ratio) {
if (prev_picked_c != nullptr ||
sorted_runs_.size() <
static_cast<size_t>(file_num_compaction_trigger)) {
return prev_picked_c;
}
Compaction* c = PickCompactionToReduceSortedRuns(ratio, UINT_MAX);
if (c != nullptr) {
TEST_SYNC_POINT("PickCompactionToReduceSortedRunsReturnNonnullptr");
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: picked for size ratio compaction to "
"reduce sorted run\n",
cf_name_.c_str());
}
return c;
}
Compaction* MaybePickCompactionToReduceSortedRuns(
Compaction* const prev_picked_c, int file_num_compaction_trigger,
unsigned int ratio) {
if (prev_picked_c != nullptr ||
sorted_runs_.size() <
static_cast<size_t>(file_num_compaction_trigger)) {
return prev_picked_c;
}
int num_sr_not_compacted = 0;
int max_num_runs = 0;
const unsigned int max_num_files_to_compact =
GetMaxNumFilesToCompactBasedOnMaxReadAmp(file_num_compaction_trigger,
ratio, &num_sr_not_compacted,
&max_num_runs);
if (max_num_files_to_compact == 0) {
ROCKS_LOG_BUFFER(
log_buffer_,
"[%s] Universal: skipping compaction to reduce sorted run, num "
"sorted runs not "
"being compacted -- %u, max num runs allowed -- %d, max_run_size "
"-- %" PRIu64 "\n",
cf_name_.c_str(), num_sr_not_compacted, max_num_runs, max_run_size_);
return nullptr;
}
Compaction* c =
PickCompactionToReduceSortedRuns(UINT_MAX, max_num_files_to_compact);
if (c != nullptr) {
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: picked for sorted run num compaction "
"to reduce sorted run, to "
"compact file num -- %u, max num runs allowed"
"-- %d, max_run_size -- %" PRIu64 "\n",
cf_name_.c_str(), max_num_files_to_compact, max_num_runs,
max_run_size_);
}
return c;
}
Compaction* MaybePickDeleteTriggeredCompaction(
Compaction* const prev_picked_c) {
if (prev_picked_c != nullptr) {
return prev_picked_c;
}
Compaction* c = PickDeleteTriggeredCompaction();
if (c != nullptr) {
TEST_SYNC_POINT("PickDeleteTriggeredCompactionReturnNonnullptr");
ROCKS_LOG_BUFFER(
log_buffer_,
"[%s] Universal: picked for delete triggered compaction\n",
cf_name_.c_str());
}
return c;
}
// Pick Universal compaction to limit read amplification
Compaction* PickCompactionToReduceSortedRuns(
unsigned int ratio, unsigned int max_number_of_files_to_compact);
@@ -291,11 +98,6 @@ class UniversalCompactionBuilder {
Compaction* PickDeleteTriggeredCompaction();
// Returns true if this given file (that is marked be compaction) should be
// skipped from being picked for now. We do this to best use standalone range
// tombstone files.
bool ShouldSkipMarkedFile(const FileMetaData* file) const;
// Form a compaction from the sorted run indicated by start_index to the
// oldest sorted run.
// The caller is responsible for making sure that those files are not in
@@ -314,7 +116,7 @@ class UniversalCompactionBuilder {
bool ShouldSkipLastSortedRunForSizeAmpCompaction() const {
assert(!sorted_runs_.empty());
return mutable_cf_options_.preclude_last_level_data_seconds > 0 &&
return ioptions_.preclude_last_level_data_seconds > 0 &&
ioptions_.num_levels > 2 &&
sorted_runs_.back().level == ioptions_.num_levels - 1 &&
sorted_runs_.size() > 1;
@@ -421,39 +223,19 @@ class UniversalCompactionBuilder {
return num_l0_to_exclude;
}
bool MeetsOutputLevelRequirements(int output_level) const {
return !require_max_output_level_ ||
Compaction::OutputToNonZeroMaxOutputLevel(
output_level, vstorage_->MaxOutputLevel(allow_ingest_behind_));
}
const ImmutableOptions& ioptions_;
const InternalKeyComparator* icmp_;
double score_;
std::vector<SortedRun> sorted_runs_;
uint64_t max_run_size_;
const std::string& cf_name_;
const MutableCFOptions& mutable_cf_options_;
const MutableDBOptions& mutable_db_options_;
VersionStorageInfo* vstorage_;
UniversalCompactionPicker* picker_;
LogBuffer* log_buffer_;
// Optional earliest snapshot at time of compaction picking. This is only
// provided if the column family doesn't enable user-defined timestamps.
// And this information is only passed to `Compaction` picked by deletion
// triggered compaction for possible optimizations.
std::optional<SequenceNumber> earliest_snapshot_;
const SnapshotChecker* snapshot_checker_;
// Mapping from file id to its index in the sorted run for the files that are
// marked for compaction. This is only populated when snapshot info is
// populated.
std::map<uint64_t, size_t> file_marked_for_compaction_to_sorted_run_index_;
bool require_max_output_level_;
bool allow_ingest_behind_;
std::vector<UniversalCompactionBuilder::SortedRun> CalculateSortedRuns(
const VersionStorageInfo& vstorage, int last_level,
uint64_t* max_run_size);
static std::vector<UniversalCompactionBuilder::SortedRun> CalculateSortedRuns(
const VersionStorageInfo& vstorage, int last_level);
// Pick a path ID to place a newly generated file, with its estimated file
// size.
@@ -468,9 +250,7 @@ class UniversalCompactionBuilder {
// and the index of the file in that level
struct InputFileInfo {
InputFileInfo() : InputFileInfo(nullptr, 0, 0) {}
InputFileInfo(FileMetaData* file_meta, size_t l, size_t i)
: f(file_meta), level(l), index(i) {}
InputFileInfo() : f(nullptr), level(0), index(0) {}
FileMetaData* f;
size_t level;
@@ -503,14 +283,22 @@ SmallestKeyHeap create_level_heap(Compaction* c, const Comparator* ucmp) {
SmallestKeyHeap smallest_key_priority_q =
SmallestKeyHeap(SmallestKeyHeapComparator(ucmp));
InputFileInfo input_file;
for (size_t l = 0; l < c->num_input_levels(); l++) {
if (c->num_input_files(l) != 0) {
if (l == 0 && c->start_level() == 0) {
for (size_t i = 0; i < c->num_input_files(0); i++) {
smallest_key_priority_q.emplace(c->input(0, i), 0, i);
input_file.f = c->input(0, i);
input_file.level = 0;
input_file.index = i;
smallest_key_priority_q.push(std::move(input_file));
}
} else {
smallest_key_priority_q.emplace(c->input(l, 0), l, 0);
input_file.f = c->input(l, 0);
input_file.level = l;
input_file.index = 0;
smallest_key_priority_q.push(std::move(input_file));
}
}
}
@@ -548,7 +336,7 @@ bool UniversalCompactionBuilder::IsInputFilesNonOverlapping(Compaction* c) {
auto comparator = icmp_->user_comparator();
int first_iter = 1;
InputFileInfo prev, curr;
InputFileInfo prev, curr, next;
SmallestKeyHeap smallest_key_priority_q =
create_level_heap(c, icmp_->user_comparator());
@@ -571,10 +359,17 @@ bool UniversalCompactionBuilder::IsInputFilesNonOverlapping(Compaction* c) {
prev = curr;
}
next.f = nullptr;
if (c->level(curr.level) != 0 &&
curr.index < c->num_input_files(curr.level) - 1) {
smallest_key_priority_q.emplace(c->input(curr.level, curr.index + 1),
curr.level, curr.index + 1);
next.f = c->input(curr.level, curr.index + 1);
next.level = curr.level;
next.index = curr.index + 1;
}
if (next.f) {
smallest_key_priority_q.push(std::move(next));
}
}
return true;
@@ -597,14 +392,11 @@ bool UniversalCompactionPicker::NeedsCompaction(
Compaction* UniversalCompactionPicker::PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& existing_snapshots,
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
LogBuffer* log_buffer, bool require_max_output_level) {
UniversalCompactionBuilder builder(
ioptions_, icmp_, cf_name, mutable_cf_options, mutable_db_options,
existing_snapshots, snapshot_checker, vstorage, this, log_buffer,
require_max_output_level);
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
LogBuffer* log_buffer) {
UniversalCompactionBuilder builder(ioptions_, icmp_, cf_name,
mutable_cf_options, mutable_db_options,
vstorage, this, log_buffer);
return builder.PickCompaction();
}
@@ -648,26 +440,16 @@ void UniversalCompactionBuilder::SortedRun::DumpSizeInfo(
std::vector<UniversalCompactionBuilder::SortedRun>
UniversalCompactionBuilder::CalculateSortedRuns(
const VersionStorageInfo& vstorage, int last_level,
uint64_t* max_run_size) {
assert(max_run_size);
*max_run_size = 0;
const VersionStorageInfo& vstorage, int last_level) {
std::vector<UniversalCompactionBuilder::SortedRun> ret;
for (FileMetaData* f : vstorage.LevelFiles(0)) {
if (earliest_snapshot_.has_value() && f->marked_for_compaction) {
file_marked_for_compaction_to_sorted_run_index_.emplace(f->fd.GetNumber(),
ret.size());
}
ret.emplace_back(
0, f, f->fd.GetFileSize(), f->compensated_file_size, f->being_compacted,
f->marked_for_compaction && f->FileIsStandAloneRangeTombstone());
*max_run_size = std::max(*max_run_size, f->fd.GetFileSize());
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
f->being_compacted);
}
for (int level = 1; level <= last_level; level++) {
uint64_t total_compensated_size = 0U;
uint64_t total_size = 0U;
bool being_compacted = false;
bool level_has_marked_standalone_rangedel = false;
for (FileMetaData* f : vstorage.LevelFiles(level)) {
total_compensated_size += f->compensated_file_size;
total_size += f->fd.GetFileSize();
@@ -679,88 +461,35 @@ UniversalCompactionBuilder::CalculateSortedRuns(
if (f->being_compacted) {
being_compacted = f->being_compacted;
}
level_has_marked_standalone_rangedel =
level_has_marked_standalone_rangedel ||
(f->marked_for_compaction && f->FileIsStandAloneRangeTombstone());
if (earliest_snapshot_.has_value() && f->marked_for_compaction) {
file_marked_for_compaction_to_sorted_run_index_.emplace(
f->fd.GetNumber(), ret.size());
}
}
if (total_compensated_size > 0) {
ret.emplace_back(level, nullptr, total_size, total_compensated_size,
being_compacted, level_has_marked_standalone_rangedel);
being_compacted);
}
*max_run_size = std::max(*max_run_size, total_size);
}
return ret;
}
bool UniversalCompactionBuilder::ShouldSkipMarkedFile(
const FileMetaData* file) const {
assert(file->marked_for_compaction);
if (!earliest_snapshot_.has_value()) {
return false;
}
if (!file->FileIsStandAloneRangeTombstone()) {
return false;
}
// Skip until earliest snapshot advances at or above this standalone range
// tombstone file. `DB::ReleaseSnapshot` will re-examine and schedule
// compaction for it.
if (!DataIsDefinitelyInSnapshot(file->fd.largest_seqno,
earliest_snapshot_.value(),
snapshot_checker_)) {
return true;
}
auto iter = file_marked_for_compaction_to_sorted_run_index_.find(
file->fd.GetNumber());
assert(iter != file_marked_for_compaction_to_sorted_run_index_.end());
size_t idx = iter->second;
const SortedRun* succeeding_sorted_run =
idx < sorted_runs_.size() - 1 ? &sorted_runs_[idx + 1] : nullptr;
// Marked standalone range tombstone file is best used if it's in the start
// input level. Skip to let that compaction happen first.
if (succeeding_sorted_run &&
succeeding_sorted_run->level_has_marked_standalone_rangedel) {
return true;
}
return false;
}
// Universal style of compaction. Pick files that are contiguous in
// time-range to compact.
Compaction* UniversalCompactionBuilder::PickCompaction() {
const int kLevel0 = 0;
score_ = vstorage_->CompactionScore(kLevel0);
const int max_output_level = vstorage_->MaxOutputLevel(allow_ingest_behind_);
const int file_num_compaction_trigger =
mutable_cf_options_.level0_file_num_compaction_trigger;
const unsigned int ratio =
mutable_cf_options_.compaction_options_universal.size_ratio;
if (max_output_level == 0 &&
!MeetsOutputLevelRequirements(0 /* output_level */)) {
return nullptr;
}
max_run_size_ = 0;
sorted_runs_ =
CalculateSortedRuns(*vstorage_, max_output_level, &max_run_size_);
int max_output_level =
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
sorted_runs_ = CalculateSortedRuns(*vstorage_, max_output_level);
if (sorted_runs_.size() == 0 ||
(vstorage_->FilesMarkedForPeriodicCompaction().empty() &&
vstorage_->FilesMarkedForCompaction().empty() &&
sorted_runs_.size() < (unsigned int)file_num_compaction_trigger)) {
sorted_runs_.size() < (unsigned int)mutable_cf_options_
.level0_file_num_compaction_trigger)) {
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Universal: nothing to do\n",
cf_name_.c_str());
TEST_SYNC_POINT_CALLBACK(
"UniversalCompactionBuilder::PickCompaction:Return", nullptr);
return nullptr;
}
VersionStorageInfo::LevelSummaryStorage tmp;
ROCKS_LOG_BUFFER_MAX_SZ(
log_buffer_, 3072,
@@ -768,22 +497,85 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
cf_name_.c_str(), sorted_runs_.size(), vstorage_->LevelSummary(&tmp));
Compaction* c = nullptr;
// Periodic compaction has higher priority than other type of compaction
// because it's a hard requirement.
if (!vstorage_->FilesMarkedForPeriodicCompaction().empty()) {
// Always need to do a full compaction for periodic compaction.
c = PickPeriodicCompaction();
TEST_SYNC_POINT_CALLBACK("PostPickPeriodicCompaction", c);
}
c = MaybePickPeriodicCompaction(c);
c = MaybePickSizeAmpCompaction(c, file_num_compaction_trigger);
c = MaybePickCompactionToReduceSortedRunsBasedFileRatio(
c, file_num_compaction_trigger, ratio);
c = MaybePickCompactionToReduceSortedRuns(c, file_num_compaction_trigger,
ratio);
c = MaybePickDeleteTriggeredCompaction(c);
// Check for size amplification.
if (c == nullptr &&
sorted_runs_.size() >=
static_cast<size_t>(
mutable_cf_options_.level0_file_num_compaction_trigger)) {
if ((c = PickCompactionToReduceSizeAmp()) != nullptr) {
TEST_SYNC_POINT("PickCompactionToReduceSizeAmpReturnNonnullptr");
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Universal: compacting for size amp\n",
cf_name_.c_str());
} else {
// Size amplification is within limits. Try reducing read
// amplification while maintaining file size ratios.
unsigned int ratio =
mutable_cf_options_.compaction_options_universal.size_ratio;
if ((c = PickCompactionToReduceSortedRuns(ratio, UINT_MAX)) != nullptr) {
TEST_SYNC_POINT("PickCompactionToReduceSortedRunsReturnNonnullptr");
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: compacting for size ratio\n",
cf_name_.c_str());
} else {
// Size amplification and file size ratios are within configured limits.
// If max read amplification is exceeding configured limits, then force
// compaction without looking at filesize ratios and try to reduce
// the number of files to fewer than level0_file_num_compaction_trigger.
// This is guaranteed by NeedsCompaction()
assert(sorted_runs_.size() >=
static_cast<size_t>(
mutable_cf_options_.level0_file_num_compaction_trigger));
// Get the total number of sorted runs that are not being compacted
int num_sr_not_compacted = 0;
for (size_t i = 0; i < sorted_runs_.size(); i++) {
if (sorted_runs_[i].being_compacted == false) {
num_sr_not_compacted++;
}
}
// The number of sorted runs that are not being compacted is greater
// than the maximum allowed number of sorted runs
if (num_sr_not_compacted >
mutable_cf_options_.level0_file_num_compaction_trigger) {
unsigned int num_files =
num_sr_not_compacted -
mutable_cf_options_.level0_file_num_compaction_trigger + 1;
if ((c = PickCompactionToReduceSortedRuns(UINT_MAX, num_files)) !=
nullptr) {
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: compacting for file num -- %u\n",
cf_name_.c_str(), num_files);
}
}
}
}
}
if (c == nullptr) {
if ((c = PickDeleteTriggeredCompaction()) != nullptr) {
TEST_SYNC_POINT("PickDeleteTriggeredCompactionReturnNonnullptr");
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: delete triggered compaction\n",
cf_name_.c_str());
}
}
if (c == nullptr) {
TEST_SYNC_POINT_CALLBACK(
"UniversalCompactionBuilder::PickCompaction:Return", nullptr);
return nullptr;
}
assert(c->output_level() <= vstorage_->MaxOutputLevel(allow_ingest_behind_));
assert(MeetsOutputLevelRequirements(c->output_level()));
assert(c->output_level() <=
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind));
if (mutable_cf_options_.compaction_options_universal.allow_trivial_move ==
true &&
@@ -900,26 +692,16 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
for (sr = nullptr; loop < sorted_runs_.size(); loop++) {
sr = &sorted_runs_[loop];
if (!sr->being_compacted && !sr->level_has_marked_standalone_rangedel) {
if (!sr->being_compacted) {
candidate_count = 1;
break;
}
char file_num_buf[kFormatFileNumberBufSize];
sr->Dump(file_num_buf, sizeof(file_num_buf));
if (sr->being_compacted) {
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: %s"
"[%d] being compacted, skipping for compaction to "
"reduce sorted runs",
cf_name_.c_str(), file_num_buf, loop);
} else if (sr->level_has_marked_standalone_rangedel) {
ROCKS_LOG_BUFFER(
log_buffer_,
"[%s] Universal: %s"
"[%d] has standalone range tombstone files marked for "
"compaction, skipping for compaction to reduce sorted runs",
cf_name_.c_str(), file_num_buf, loop);
}
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: %s"
"[%d] being compacted, skipping",
cf_name_.c_str(), file_num_buf, loop);
sr = nullptr;
}
@@ -931,8 +713,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
char file_num_buf[kFormatFileNumberBufSize];
sr->Dump(file_num_buf, sizeof(file_num_buf), true);
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: Possible candidate for compaction to "
"reduce sorted runs %s[%d].",
"[%s] Universal: Possible candidate %s[%d].",
cf_name_.c_str(), file_num_buf, loop);
}
@@ -941,8 +722,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
candidate_count < max_files_to_compact && i < sorted_runs_.size();
i++) {
const SortedRun* succeeding_sr = &sorted_runs_[i];
if (succeeding_sr->being_compacted ||
succeeding_sr->level_has_marked_standalone_rangedel) {
if (succeeding_sr->being_compacted) {
break;
}
// Pick files if the total/last candidate file size (increased by the
@@ -1024,7 +804,8 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
int start_level = sorted_runs_[start_index].level;
int output_level;
// last level is reserved for the files ingested behind
int max_output_level = vstorage_->MaxOutputLevel(allow_ingest_behind_);
int max_output_level =
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
if (first_index_after == sorted_runs_.size()) {
output_level = max_output_level;
} else if (sorted_runs_[first_index_after].level == 0) {
@@ -1033,10 +814,6 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
output_level = sorted_runs_[first_index_after].level - 1;
}
if (!MeetsOutputLevelRequirements(output_level)) {
return nullptr;
}
std::vector<CompactionInputFiles> inputs(max_output_level + 1);
for (size_t i = 0; i < inputs.size(); ++i) {
inputs[i].level = start_level + static_cast<int>(i);
@@ -1071,11 +848,11 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
grandparents = vstorage_->LevelFiles(sorted_runs_[first_index_after].level);
}
if (output_level != 0 && picker_->FilesRangeOverlapWithCompaction(
inputs, output_level,
Compaction::EvaluateProximalLevel(
vstorage_, mutable_cf_options_, ioptions_,
start_level, output_level))) {
if (output_level != 0 &&
picker_->FilesRangeOverlapWithCompaction(
inputs, output_level,
Compaction::EvaluatePenultimateLevel(vstorage_, ioptions_,
start_level, output_level))) {
return nullptr;
}
CompactionReason compaction_reason;
@@ -1093,12 +870,11 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
output_level, 1, enable_compression),
GetCompressionOptions(mutable_cf_options_, vstorage_,
output_level, enable_compression),
Temperature::kUnknown,
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, grandparents,
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, compaction_reason,
/* trim_ts */ "", score_,
/* l0_files_might_overlap */ true);
/* is manual */ false, /* trim_ts */ "", score_,
false /* deletion_compaction */,
/* l0_files_might_overlap */ true, compaction_reason);
}
// Look at overall size amplification. If size amplification
@@ -1112,8 +888,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
const size_t end_index = ShouldSkipLastSortedRunForSizeAmpCompaction()
? sorted_runs_.size() - 2
: sorted_runs_.size() - 1;
if (sorted_runs_[end_index].being_compacted ||
sorted_runs_[end_index].level_has_marked_standalone_rangedel) {
if (sorted_runs_[end_index].being_compacted) {
return nullptr;
}
const uint64_t base_sr_size = sorted_runs_[end_index].size;
@@ -1124,24 +899,14 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
// Get longest span (i.e, [start_index, end_index]) of available sorted runs
while (start_index > 0) {
const SortedRun* sr = &sorted_runs_[start_index - 1];
if (sr->being_compacted || sr->level_has_marked_standalone_rangedel) {
if (sr->being_compacted) {
char file_num_buf[kFormatFileNumberBufSize];
sr->Dump(file_num_buf, sizeof(file_num_buf), true);
if (sr->being_compacted) {
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: stopping for size amp compaction at "
"sorted run undergoing compaction: "
"%s[%" ROCKSDB_PRIszt "]",
cf_name_.c_str(), file_num_buf, start_index - 1);
} else if (sr->level_has_marked_standalone_rangedel) {
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: stopping for size amp compaction at "
"sorted run that has "
"standalone range "
"tombstone files marked for compaction: "
"%s[%" ROCKSDB_PRIszt "]",
cf_name_.c_str(), file_num_buf, start_index - 1);
}
ROCKS_LOG_BUFFER(
log_buffer_,
"[%s] Universal: stopping at sorted run undergoing compaction: "
"%s[%" ROCKSDB_PRIszt "]",
cf_name_.c_str(), file_num_buf, start_index - 1);
break;
}
candidate_size += sr->compensated_file_size;
@@ -1156,12 +921,11 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
{
const size_t num_l0_to_exclude = MightExcludeNewL0sToReduceWriteStop(
num_l0_files, end_index, start_index, candidate_size);
ROCKS_LOG_BUFFER(
log_buffer_,
"[%s] Universal: Excluding for size amp compaction %" ROCKSDB_PRIszt
" latest L0 files to reduce potential write stop "
"triggered by `level0_stop_writes_trigger`",
cf_name_.c_str(), num_l0_to_exclude);
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: Excluding %" ROCKSDB_PRIszt
" latest L0 files to reduce potential write stop "
"triggered by `level0_stop_writes_trigger`",
cf_name_.c_str(), num_l0_to_exclude);
}
{
@@ -1179,18 +943,18 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
// size amplification = percentage of additional size
if (candidate_size * 100 < ratio * base_sr_size) {
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: size amp compction not needed. "
"newer-files-total-size %" PRIu64
" earliest-file-size %" PRIu64,
cf_name_.c_str(), candidate_size, base_sr_size);
ROCKS_LOG_BUFFER(
log_buffer_,
"[%s] Universal: size amp not needed. newer-files-total-size %" PRIu64
" earliest-file-size %" PRIu64,
cf_name_.c_str(), candidate_size, base_sr_size);
return nullptr;
} else {
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: size amp compaction needed. "
"newer-files-total-size %" PRIu64
" earliest-file-size %" PRIu64,
cf_name_.c_str(), candidate_size, base_sr_size);
ROCKS_LOG_BUFFER(
log_buffer_,
"[%s] Universal: size amp needed. newer-files-total-size %" PRIu64
" earliest-file-size %" PRIu64,
cf_name_.c_str(), candidate_size, base_sr_size);
}
// Since incremental compaction can't include more than second last
// level, it can introduce penalty, compared to full compaction. We
@@ -1421,11 +1185,11 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
}
// intra L0 compactions outputs could have overlap
if (output_level != 0 && picker_->FilesRangeOverlapWithCompaction(
inputs, output_level,
Compaction::EvaluateProximalLevel(
vstorage_, mutable_cf_options_, ioptions_,
start_level, output_level))) {
if (output_level != 0 &&
picker_->FilesRangeOverlapWithCompaction(
inputs, output_level,
Compaction::EvaluatePenultimateLevel(vstorage_, ioptions_,
start_level, output_level))) {
return nullptr;
}
@@ -1441,13 +1205,11 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
true /* enable_compression */),
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
true /* enable_compression */),
Temperature::kUnknown,
/* max_subcompactions */ 0, /* grandparents */ {},
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
CompactionReason::kUniversalSizeAmplification,
/* trim_ts */ "", score_,
/* l0_files_might_overlap */ true);
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
/* trim_ts */ "", score_, false /* deletion_compaction */,
/* l0_files_might_overlap */ true,
CompactionReason::kUniversalSizeAmplification);
}
// Pick files marked for compaction. Typically, files are marked by
@@ -1475,7 +1237,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
continue;
}
FileMetaData* f = vstorage_->LevelFiles(0)[loop];
if (f->marked_for_compaction && !ShouldSkipMarkedFile(f)) {
if (f->marked_for_compaction) {
start_level_inputs.files.push_back(f);
start_index =
static_cast<int>(loop); // Consider this as the first candidate.
@@ -1489,7 +1251,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
for (size_t loop = start_index + 1; loop < sorted_runs_.size(); loop++) {
SortedRun* sr = &sorted_runs_[loop];
if (sr->being_compacted || sr->level_has_marked_standalone_rangedel) {
if (sr->being_compacted) {
break;
}
@@ -1508,15 +1270,13 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
// leveled. We pick one of the files marked for compaction and compact with
// overlapping files in the adjacent level.
picker_->PickFilesMarkedForCompaction(cf_name_, vstorage_, &start_level,
&output_level, &start_level_inputs,
[this](const FileMetaData* file) {
return ShouldSkipMarkedFile(file);
});
&output_level, &start_level_inputs);
if (start_level_inputs.empty()) {
return nullptr;
}
int max_output_level = vstorage_->MaxOutputLevel(allow_ingest_behind_);
int max_output_level =
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
// Pick the first non-empty level after the start_level
for (output_level = start_level + 1; output_level <= max_output_level;
output_level++) {
@@ -1539,23 +1299,10 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
}
assert(output_level <= max_output_level);
if (!MeetsOutputLevelRequirements(output_level)) {
return nullptr;
}
if (output_level != 0) {
// For standalone range deletion, we don't want to compact it with newer
// L0 files that it doesn't cover.
const FileMetaData* starting_l0_file =
(start_level == 0 && start_level_inputs.size() == 1 &&
start_level_inputs.files[0]->FileIsStandAloneRangeTombstone())
? start_level_inputs.files[0]
: nullptr;
if (start_level == 0) {
if (!picker_->GetOverlappingL0Files(vstorage_, &start_level_inputs,
output_level, nullptr,
starting_l0_file)) {
output_level, nullptr)) {
return nullptr;
}
}
@@ -1566,8 +1313,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
output_level_inputs.level = output_level;
if (!picker_->SetupOtherInputs(cf_name_, mutable_cf_options_, vstorage_,
&start_level_inputs, &output_level_inputs,
&parent_index, -1, false,
starting_l0_file)) {
&parent_index, -1)) {
return nullptr;
}
inputs.push_back(start_level_inputs);
@@ -1576,9 +1322,8 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
}
if (picker_->FilesRangeOverlapWithCompaction(
inputs, output_level,
Compaction::EvaluateProximalLevel(vstorage_, mutable_cf_options_,
ioptions_, start_level,
output_level))) {
Compaction::EvaluatePenultimateLevel(
vstorage_, ioptions_, start_level, output_level))) {
return nullptr;
}
@@ -1604,11 +1349,11 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
/* max_grandparent_overlap_bytes */ GetMaxOverlappingBytes(), path_id,
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1),
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
Temperature::kUnknown,
/* max_subcompactions */ 0, grandparents, earliest_snapshot_,
snapshot_checker_, CompactionReason::kFilesMarkedForCompaction,
/* trim_ts */ "", score_,
/* l0_files_might_overlap */ true);
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, grandparents, /* is manual */ false,
/* trim_ts */ "", score_, false /* deletion_compaction */,
/* l0_files_might_overlap */ true,
CompactionReason::kFilesMarkedForCompaction);
}
Compaction* UniversalCompactionBuilder::PickCompactionToOldest(
@@ -1629,7 +1374,8 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
uint32_t path_id =
GetPathId(ioptions_, mutable_cf_options_, estimated_total_size);
int start_level = sorted_runs_[start_index].level;
int max_output_level = vstorage_->MaxOutputLevel(allow_ingest_behind_);
int max_output_level =
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
std::vector<CompactionInputFiles> inputs(max_output_level + 1);
for (size_t i = 0; i < inputs.size(); ++i) {
inputs[i].level = start_level + static_cast<int>(i);
@@ -1674,16 +1420,12 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
output_level = sorted_runs_[end_index + 1].level - 1;
}
if (!MeetsOutputLevelRequirements(output_level)) {
return nullptr;
}
// intra L0 compactions outputs could have overlap
if (output_level != 0 && picker_->FilesRangeOverlapWithCompaction(
inputs, output_level,
Compaction::EvaluateProximalLevel(
vstorage_, mutable_cf_options_, ioptions_,
start_level, output_level))) {
if (output_level != 0 &&
picker_->FilesRangeOverlapWithCompaction(
inputs, output_level,
Compaction::EvaluatePenultimateLevel(vstorage_, ioptions_,
start_level, output_level))) {
return nullptr;
}
@@ -1700,12 +1442,10 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
true /* enable_compression */),
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
true /* enable_compression */),
Temperature::kUnknown,
/* max_subcompactions */ 0, /* grandparents */ {},
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, compaction_reason,
/* trim_ts */ "", score_,
/* l0_files_might_overlap */ true);
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
/* trim_ts */ "", score_, false /* deletion_compaction */,
/* l0_files_might_overlap */ true, compaction_reason);
}
Compaction* UniversalCompactionBuilder::PickPeriodicCompaction() {
@@ -1724,8 +1464,7 @@ Compaction* UniversalCompactionBuilder::PickPeriodicCompaction() {
// included in the compaction.
size_t start_index = sorted_runs_.size();
while (start_index > 0 && !sorted_runs_[start_index - 1].being_compacted &&
!sorted_runs_[start_index - 1].level_has_marked_standalone_rangedel) {
while (start_index > 0 && !sorted_runs_[start_index - 1].being_compacted) {
start_index--;
}
if (start_index == sorted_runs_.size()) {
+5 -10
View File
@@ -10,7 +10,6 @@
#pragma once
#include "db/compaction/compaction_picker.h"
#include "db/snapshot_checker.h"
namespace ROCKSDB_NAMESPACE {
class UniversalCompactionPicker : public CompactionPicker {
@@ -18,15 +17,11 @@ class UniversalCompactionPicker : public CompactionPicker {
UniversalCompactionPicker(const ImmutableOptions& ioptions,
const InternalKeyComparator* icmp)
: CompactionPicker(ioptions, icmp) {}
// If `require_max_output_level` is true, only pick compaction
// with max output level or return nullptr if no such compaction exists.
Compaction* PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& existing_snapshots,
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
LogBuffer* log_buffer, bool require_max_output_level = false) override;
Compaction* PickCompaction(const std::string& cf_name,
const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
VersionStorageInfo* vstorage,
LogBuffer* log_buffer) override;
int MaxOutputLevel() const override { return NumberLevels() - 1; }
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
+143 -376
View File
@@ -39,20 +39,19 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
MakeTableFileName(file->fd.GetNumber()));
}
}
compaction_input.cf_name = compaction->column_family_data()->GetName();
compaction_input.snapshots = job_context_->snapshot_seqs;
compaction_input.column_family.name =
compaction->column_family_data()->GetName();
compaction_input.column_family.options =
compaction->column_family_data()->GetLatestCFOptions();
compaction_input.db_options =
BuildDBOptions(db_options_, mutable_db_options_copy_);
compaction_input.snapshots = existing_snapshots_;
compaction_input.has_begin = sub_compact->start.has_value();
compaction_input.begin =
compaction_input.has_begin ? sub_compact->start->ToString() : "";
compaction_input.has_end = sub_compact->end.has_value();
compaction_input.end =
compaction_input.has_end ? sub_compact->end->ToString() : "";
compaction_input.options_file_number = options_file_number_;
TEST_SYNC_POINT_CALLBACK(
"CompactionServiceJob::ProcessKeyValueCompactionWithCompactionService",
&compaction_input);
std::string compaction_input_binary;
Status s = compaction_input.Write(&compaction_input_binary);
@@ -71,86 +70,46 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
ROCKS_LOG_INFO(
db_options_.info_log,
"[%s] [JOB %d] Starting remote compaction (output level: %d): %s",
compaction->column_family_data()->GetName().c_str(), job_id_,
compaction_input.column_family.name.c_str(), job_id_,
compaction_input.output_level, input_files_oss.str().c_str());
CompactionServiceJobInfo info(
dbname_, db_id_, db_session_id_,
compaction->column_family_data()->GetID(),
compaction->column_family_data()->GetName(), GetCompactionId(sub_compact),
thread_pri_, compaction->compaction_reason(),
compaction->is_full_compaction(), compaction->is_manual_compaction(),
compaction->bottommost_level(), compaction->start_level(),
compaction->output_level());
CompactionServiceJobInfo info(dbname_, db_id_, db_session_id_,
GetCompactionId(sub_compact), thread_pri_);
CompactionServiceScheduleResponse response =
db_options_.compaction_service->Schedule(info, compaction_input_binary);
switch (response.status) {
case CompactionServiceJobStatus::kSuccess:
break;
case CompactionServiceJobStatus::kAborted:
sub_compact->status =
Status::Aborted("Scheduling a remote compaction job was aborted");
ROCKS_LOG_WARN(
db_options_.info_log,
"[%s] [JOB %d] Remote compaction was aborted at Schedule()",
compaction->column_family_data()->GetName().c_str(), job_id_);
return response.status;
case CompactionServiceJobStatus::kFailure:
sub_compact->status = Status::Incomplete(
"CompactionService failed to schedule a remote compaction job.");
ROCKS_LOG_WARN(db_options_.info_log,
"[%s] [JOB %d] Remote compaction failed to start.",
compaction->column_family_data()->GetName().c_str(),
job_id_);
compaction_input.column_family.name.c_str(), job_id_);
return response.status;
case CompactionServiceJobStatus::kUseLocal:
ROCKS_LOG_INFO(
db_options_.info_log,
"[%s] [JOB %d] Remote compaction fallback to local by API (Schedule)",
compaction->column_family_data()->GetName().c_str(), job_id_);
compaction_input.column_family.name.c_str(), job_id_);
return response.status;
default:
assert(false); // unknown status
break;
}
std::string debug_str_before_wait =
compaction->input_version()->DebugString(/*hex=*/true);
ROCKS_LOG_INFO(db_options_.info_log,
"[%s] [JOB %d] Waiting for remote compaction...",
compaction->column_family_data()->GetName().c_str(), job_id_);
compaction_input.column_family.name.c_str(), job_id_);
std::string compaction_result_binary;
CompactionServiceJobStatus compaction_status =
db_options_.compaction_service->Wait(response.scheduled_job_id,
&compaction_result_binary);
if (compaction_status != CompactionServiceJobStatus::kSuccess) {
ROCKS_LOG_ERROR(
db_options_.info_log,
"[%s] [JOB %d] Wait() status is not kSuccess. "
"\nDebugString Before Wait():\n%s"
"\nDebugString After Wait():\n%s",
compaction->column_family_data()->GetName().c_str(), job_id_,
debug_str_before_wait.c_str(),
compaction->input_version()->DebugString(/*hex=*/true).c_str());
}
if (compaction_status == CompactionServiceJobStatus::kUseLocal) {
ROCKS_LOG_INFO(
db_options_.info_log,
"[%s] [JOB %d] Remote compaction fallback to local by API (Wait)",
compaction->column_family_data()->GetName().c_str(), job_id_);
return compaction_status;
}
if (compaction_status == CompactionServiceJobStatus::kAborted) {
sub_compact->status =
Status::Aborted("Waiting a remote compaction job was aborted");
ROCKS_LOG_INFO(db_options_.info_log,
"[%s] [JOB %d] Remote compaction was aborted during Wait()",
compaction->column_family_data()->GetName().c_str(),
job_id_);
compaction_input.column_family.name.c_str(), job_id_);
return compaction_status;
}
@@ -175,19 +134,15 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
"result is returned).");
compaction_result.status.PermitUncheckedError();
}
ROCKS_LOG_WARN(
db_options_.info_log, "[%s] [JOB %d] Remote compaction failed.",
compaction->column_family_data()->GetName().c_str(), job_id_);
ROCKS_LOG_WARN(db_options_.info_log,
"[%s] [JOB %d] Remote compaction failed.",
compaction_input.column_family.name.c_str(), job_id_);
return compaction_status;
}
// CompactionServiceJobStatus::kSuccess was returned, but somehow we failed to
// read the result. Consider this as an installation failure
if (!s.ok()) {
sub_compact->status = s;
compaction_result.status.PermitUncheckedError();
db_options_.compaction_service->OnInstallation(
response.scheduled_job_id, CompactionServiceJobStatus::kFailure);
return CompactionServiceJobStatus::kFailure;
}
sub_compact->status = compaction_result.status;
@@ -199,46 +154,36 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
is_first_one = false;
}
ROCKS_LOG_INFO(
db_options_.info_log,
"[%s] [JOB %d] Received remote compaction result, output path: "
"%s, files: %s",
compaction->column_family_data()->GetName().c_str(), job_id_,
compaction_result.output_path.c_str(), output_files_oss.str().c_str());
ROCKS_LOG_INFO(db_options_.info_log,
"[%s] [JOB %d] Receive remote compaction result, output path: "
"%s, files: %s",
compaction_input.column_family.name.c_str(), job_id_,
compaction_result.output_path.c_str(),
output_files_oss.str().c_str());
if (!s.ok()) {
sub_compact->status = s;
return CompactionServiceJobStatus::kFailure;
}
// Installation Starts
for (const auto& file : compaction_result.output_files) {
uint64_t file_num = versions_->NewFileNumber();
auto src_file = compaction_result.output_path + "/" + file.file_name;
auto tgt_file = TableFileName(compaction->immutable_options().cf_paths,
auto tgt_file = TableFileName(compaction->immutable_options()->cf_paths,
file_num, compaction->output_path_id());
s = fs_->RenameFile(src_file, tgt_file, IOOptions(), nullptr);
if (!s.ok()) {
sub_compact->status = s;
db_options_.compaction_service->OnInstallation(
response.scheduled_job_id, CompactionServiceJobStatus::kFailure);
return CompactionServiceJobStatus::kFailure;
}
FileMetaData meta;
uint64_t file_size = file.file_size;
// TODO - Clean this up in the next release.
// For backward compatibility - in case the remote worker does not populate
// the file_size yet. If missing, continue to populate this from the file
// system.
if (file_size == 0) {
s = fs_->GetFileSize(tgt_file, IOOptions(), &file_size, nullptr);
}
uint64_t file_size;
s = fs_->GetFileSize(tgt_file, IOOptions(), &file_size, nullptr);
if (!s.ok()) {
sub_compact->status = s;
db_options_.compaction_service->OnInstallation(
response.scheduled_job_id, CompactionServiceJobStatus::kFailure);
return CompactionServiceJobStatus::kFailure;
}
assert(file_size > 0);
meta.fd = FileDescriptor(file_num, compaction->output_path_id(), file_size,
file.smallest_seqno, file.largest_seqno);
meta.smallest.DecodeFrom(file.smallest_internal_key);
@@ -246,44 +191,21 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
meta.oldest_ancester_time = file.oldest_ancester_time;
meta.file_creation_time = file.file_creation_time;
meta.epoch_number = file.epoch_number;
meta.file_checksum = file.file_checksum;
meta.file_checksum_func_name = file.file_checksum_func_name;
meta.marked_for_compaction = file.marked_for_compaction;
meta.unique_id = file.unique_id;
meta.temperature = file.file_temperature;
meta.tail_size =
FileMetaData::CalculateTailSize(file_size, file.table_properties);
auto cfd = compaction->column_family_data();
CompactionOutputs* compaction_outputs =
sub_compact->Outputs(file.is_proximal_level_output);
assert(compaction_outputs);
compaction_outputs->AddOutput(std::move(meta), cfd->internal_comparator(),
false, true, file.paranoid_hash);
compaction_outputs->UpdateTableProperties(file.table_properties);
sub_compact->Current().AddOutput(std::move(meta),
cfd->internal_comparator(), false, true,
file.paranoid_hash);
}
// Set per-level stats
auto compaction_output_stats =
sub_compact->OutputStats(false /* is_proximal_level */);
assert(compaction_output_stats);
compaction_output_stats->Add(
compaction_result.internal_stats.output_level_stats);
if (compaction->SupportsPerKeyPlacement()) {
compaction_output_stats =
sub_compact->OutputStats(true /* is_proximal_level */);
assert(compaction_output_stats);
compaction_output_stats->Add(
compaction_result.internal_stats.proximal_level_stats);
}
// Set job stats
sub_compact->compaction_job_stats = compaction_result.stats;
sub_compact->Current().SetNumOutputRecords(
compaction_result.num_output_records);
sub_compact->Current().SetTotalBytes(compaction_result.total_bytes);
RecordTick(stats_, REMOTE_COMPACT_READ_BYTES, compaction_result.bytes_read);
RecordTick(stats_, REMOTE_COMPACT_WRITE_BYTES,
compaction_result.bytes_written);
db_options_.compaction_service->OnInstallation(
response.scheduled_job_id, CompactionServiceJobStatus::kSuccess);
return CompactionServiceJobStatus::kSuccess;
}
@@ -304,71 +226,70 @@ CompactionServiceCompactionJob::CompactionServiceCompactionJob(
VersionSet* versions, const std::atomic<bool>* shutting_down,
LogBuffer* log_buffer, FSDirectory* output_directory, Statistics* stats,
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
JobContext* job_context, std::shared_ptr<Cache> table_cache,
EventLogger* event_logger, const std::string& dbname,
const std::shared_ptr<IOTracer>& io_tracer,
std::vector<SequenceNumber> existing_snapshots,
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
const std::string& dbname, const std::shared_ptr<IOTracer>& io_tracer,
const std::atomic<bool>& manual_compaction_canceled,
const std::string& db_id, const std::string& db_session_id,
std::string output_path,
const CompactionServiceInput& compaction_service_input,
CompactionServiceResult* compaction_service_result)
: CompactionJob(job_id, compaction, db_options, mutable_db_options,
file_options, versions, shutting_down, log_buffer, nullptr,
output_directory, nullptr, stats, db_mutex,
db_error_handler, job_context, std::move(table_cache),
event_logger,
compaction->mutable_cf_options().paranoid_file_checks,
compaction->mutable_cf_options().report_bg_io_stats, dbname,
&(compaction_service_result->stats), Env::Priority::USER,
io_tracer, manual_compaction_canceled, db_id, db_session_id,
compaction->column_family_data()->GetFullHistoryTsLow()),
: CompactionJob(
job_id, compaction, db_options, mutable_db_options, file_options,
versions, shutting_down, log_buffer, nullptr, output_directory,
nullptr, stats, db_mutex, db_error_handler,
std::move(existing_snapshots), kMaxSequenceNumber, nullptr, nullptr,
std::move(table_cache), event_logger,
compaction->mutable_cf_options()->paranoid_file_checks,
compaction->mutable_cf_options()->report_bg_io_stats, dbname,
&(compaction_service_result->stats), Env::Priority::USER, io_tracer,
manual_compaction_canceled, db_id, db_session_id,
compaction->column_family_data()->GetFullHistoryTsLow()),
output_path_(std::move(output_path)),
compaction_input_(compaction_service_input),
compaction_result_(compaction_service_result) {}
void CompactionServiceCompactionJob::Prepare(
const CompactionProgress& compaction_progress,
log::Writer* compaction_progress_writer) {
std::optional<Slice> begin;
if (compaction_input_.has_begin) {
begin = compaction_input_.begin;
}
std::optional<Slice> end;
if (compaction_input_.has_end) {
end = compaction_input_.end;
}
CompactionJob::Prepare(std::make_pair(begin, end), compaction_progress,
compaction_progress_writer);
}
Status CompactionServiceCompactionJob::Run() {
AutoThreadOperationStageUpdater stage_updater(
ThreadStatus::STAGE_COMPACTION_RUN);
auto* c = compact_->compaction;
assert(c->column_family_data() != nullptr);
assert(c->column_family_data()->current()->storage_info()->NumLevelFiles(
compact_->compaction->level()) > 0);
write_hint_ =
c->column_family_data()->CalculateSSTWriteHint(c->output_level());
bottommost_level_ = c->bottommost_level();
Slice begin = compaction_input_.begin;
Slice end = compaction_input_.end;
compact_->sub_compact_states.emplace_back(
c,
compaction_input_.has_begin ? std::optional<Slice>(begin)
: std::optional<Slice>(),
compaction_input_.has_end ? std::optional<Slice>(end)
: std::optional<Slice>(),
/*sub_job_id*/ 0);
log_buffer_->FlushBufferToLog();
LogCompaction();
compaction_result_->stats.Reset();
const uint64_t start_micros = db_options_.clock->NowMicros();
c->GetOrInitInputTableProperties();
// Pick the only sub-compaction we should have
assert(compact_->sub_compact_states.size() == 1);
SubcompactionState* sub_compact = compact_->sub_compact_states.data();
ProcessKeyValueCompaction(sub_compact);
uint64_t elapsed_micros = db_options_.clock->NowMicros() - start_micros;
internal_stats_.SetMicros(elapsed_micros);
internal_stats_.AddCpuMicros(elapsed_micros);
compaction_stats_.stats.micros =
db_options_.clock->NowMicros() - start_micros;
compaction_stats_.stats.cpu_micros =
sub_compact->compaction_job_stats.cpu_micros;
RecordTimeToHistogram(stats_, COMPACTION_TIME,
internal_stats_.output_level_stats.micros);
compaction_stats_.stats.micros);
RecordTimeToHistogram(stats_, COMPACTION_CPU_TIME,
internal_stats_.output_level_stats.cpu_micros);
compaction_stats_.stats.cpu_micros);
Status status = sub_compact->status;
IOStatus io_s = sub_compact->io_status;
@@ -391,57 +312,38 @@ Status CompactionServiceCompactionJob::Run() {
if (status.ok()) {
status = io_s;
}
if (status.ok()) {
// TODO: Add verify_table()
}
// Finish up all book-keeping to unify the subcompaction results
compact_->AggregateCompactionStats(compaction_stats_, *compaction_job_stats_);
UpdateCompactionStats();
RecordCompactionIOStats();
LogFlush(db_options_.info_log);
compact_->status = status;
compact_->status.PermitUncheckedError();
// Build Compaction Job Stats
// 1. Aggregate internal stats and job stats for all subcompactions
// internal stats: sub_compact.proximal_level_outputs_.stats and
// sub_compact.compaction_outputs_.stats into
// internal_stats_.output_level_stats and
// internal_stats_.proximal_level_stats
// job-level stats: sub_compact.compaction_job_stats into compact.job_stats_
//
// For remote compaction, there's only one subcompaction.
compact_->AggregateCompactionStats(internal_stats_, *job_stats_);
// 2. Update job-level output stats with the aggregated internal_stats_
// Please note that input stats will be updated by primary host when all
// subcompactions are finished
UpdateCompactionJobOutputStatsFromInternalStats(status, internal_stats_);
// and set fields that are not propagated as part of the update
compaction_result_->stats.is_manual_compaction = c->is_manual_compaction();
compaction_result_->stats.is_full_compaction = c->is_full_compaction();
compaction_result_->stats.is_remote_compaction = true;
// 3. Update IO Stats that are not part of the the update above
// (bytes_read, bytes_written)
RecordCompactionIOStats();
// Build Output
compaction_result_->internal_stats = internal_stats_;
// Build compaction result
compaction_result_->output_level = compact_->compaction->output_level();
compaction_result_->output_path = output_path_;
if (status.ok()) {
for (const auto& output_file : sub_compact->GetOutputs()) {
auto& meta = output_file.meta;
compaction_result_->output_files.emplace_back(
MakeTableFileName(meta.fd.GetNumber()), meta.fd.GetFileSize(),
meta.fd.smallest_seqno, meta.fd.largest_seqno,
meta.smallest.Encode().ToString(), meta.largest.Encode().ToString(),
meta.oldest_ancester_time, meta.file_creation_time, meta.epoch_number,
meta.file_checksum, meta.file_checksum_func_name,
output_file.validator.GetHash(), meta.marked_for_compaction,
meta.unique_id, *output_file.table_properties,
output_file.is_proximal_level, meta.temperature);
}
for (const auto& output_file : sub_compact->GetOutputs()) {
auto& meta = output_file.meta;
compaction_result_->output_files.emplace_back(
MakeTableFileName(meta.fd.GetNumber()), meta.fd.smallest_seqno,
meta.fd.largest_seqno, meta.smallest.Encode().ToString(),
meta.largest.Encode().ToString(), meta.oldest_ancester_time,
meta.file_creation_time, meta.epoch_number,
output_file.validator.GetHash(), meta.marked_for_compaction,
meta.unique_id);
}
InternalStats::CompactionStatsFull compaction_stats;
sub_compact->AggregateCompactionStats(compaction_stats);
compaction_result_->num_output_records =
compaction_stats.stats.num_output_records;
compaction_result_->total_bytes = compaction_stats.TotalBytesWritten();
TEST_SYNC_POINT_CALLBACK("CompactionServiceCompactionJob::Run:0",
&compaction_result_);
return status;
}
@@ -494,9 +396,42 @@ static std::unordered_map<std::string, OptionTypeInfo> cfd_type_info = {
};
static std::unordered_map<std::string, OptionTypeInfo> cs_input_type_info = {
{"cf_name",
{offsetof(struct CompactionServiceInput, cf_name),
OptionType::kEncodedString}},
{"column_family",
OptionTypeInfo::Struct(
"column_family", &cfd_type_info,
offsetof(struct CompactionServiceInput, column_family),
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
{"db_options",
{offsetof(struct CompactionServiceInput, db_options),
OptionType::kConfigurable, OptionVerificationType::kNormal,
OptionTypeFlags::kNone,
[](const ConfigOptions& opts, const std::string& /*name*/,
const std::string& value, void* addr) {
auto options = static_cast<DBOptions*>(addr);
return GetDBOptionsFromString(opts, DBOptions(), value, options);
},
[](const ConfigOptions& opts, const std::string& /*name*/,
const void* addr, std::string* value) {
const auto options = static_cast<const DBOptions*>(addr);
std::string result;
auto status = GetStringFromDBOptions(opts, *options, &result);
*value = "{" + result + "}";
return status;
},
[](const ConfigOptions& opts, const std::string& name, const void* addr1,
const void* addr2, std::string* mismatch) {
const auto this_one = static_cast<const DBOptions*>(addr1);
const auto that_one = static_cast<const DBOptions*>(addr2);
auto this_conf = DBOptionsAsConfigurable(*this_one);
auto that_conf = DBOptionsAsConfigurable(*that_one);
std::string mismatch_opt;
bool result =
this_conf->AreEquivalent(opts, that_conf.get(), &mismatch_opt);
if (!result) {
*mismatch = name + "." + mismatch_opt;
}
return result;
}}},
{"snapshots", OptionTypeInfo::Vector<uint64_t>(
offsetof(struct CompactionServiceInput, snapshots),
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
@@ -524,10 +459,6 @@ static std::unordered_map<std::string, OptionTypeInfo> cs_input_type_info = {
{"end",
{offsetof(struct CompactionServiceInput, end), OptionType::kEncodedString,
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
{"options_file_number",
{offsetof(struct CompactionServiceInput, options_file_number),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
};
static std::unordered_map<std::string, OptionTypeInfo>
@@ -536,10 +467,6 @@ static std::unordered_map<std::string, OptionTypeInfo>
{offsetof(struct CompactionServiceOutputFile, file_name),
OptionType::kEncodedString, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"file_size",
{offsetof(struct CompactionServiceOutputFile, file_size),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"smallest_seqno",
{offsetof(struct CompactionServiceOutputFile, smallest_seqno),
OptionType::kUInt64T, OptionVerificationType::kNormal,
@@ -568,14 +495,6 @@ static std::unordered_map<std::string, OptionTypeInfo>
{offsetof(struct CompactionServiceOutputFile, epoch_number),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"file_checksum",
{offsetof(struct CompactionServiceOutputFile, file_checksum),
OptionType::kEncodedString, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"file_checksum_func_name",
{offsetof(struct CompactionServiceOutputFile, file_checksum_func_name),
OptionType::kEncodedString, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"paranoid_hash",
{offsetof(struct CompactionServiceOutputFile, paranoid_hash),
OptionType::kUInt64T, OptionVerificationType::kNormal,
@@ -589,39 +508,7 @@ static std::unordered_map<std::string, OptionTypeInfo>
offsetof(struct CompactionServiceOutputFile, unique_id),
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
{0, OptionType::kUInt64T})},
{"table_properties",
{offsetof(struct CompactionServiceOutputFile, table_properties),
OptionType::kStruct, OptionVerificationType::kNormal,
OptionTypeFlags::kNone,
[](const ConfigOptions& opts, const std::string& /*name*/,
const std::string& value, void* addr) {
auto table_properties = static_cast<TableProperties*>(addr);
return TableProperties::Parse(opts, value, table_properties);
},
[](const ConfigOptions& opts, const std::string& /*name*/,
const void* addr, std::string* value) {
const auto table_properties =
static_cast<const TableProperties*>(addr);
std::string result;
auto status = table_properties->Serialize(opts, &result);
*value = "{" + result + "}";
return status;
},
[](const ConfigOptions& opts, const std::string& /*name*/,
const void* addr1, const void* addr2, std::string* mismatch) {
const auto this_one = static_cast<const TableProperties*>(addr1);
const auto that_one = static_cast<const TableProperties*>(addr2);
return this_one->AreEqual(opts, that_one, mismatch);
}}},
{"is_proximal_level_output",
{offsetof(struct CompactionServiceOutputFile,
is_proximal_level_output),
OptionType::kBoolean, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"file_temperature",
{offsetof(struct CompactionServiceOutputFile, file_temperature),
OptionType::kTemperature, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}}};
};
static std::unordered_map<std::string, OptionTypeInfo>
compaction_job_stats_type_info = {
@@ -668,10 +555,6 @@ static std::unordered_map<std::string, OptionTypeInfo>
{offsetof(struct CompactionJobStats, is_manual_compaction),
OptionType::kBoolean, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"is_remote_compaction",
{offsetof(struct CompactionJobStats, is_remote_compaction),
OptionType::kBoolean, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"total_input_bytes",
{offsetof(struct CompactionJobStats, total_input_bytes),
OptionType::kUInt64T, OptionVerificationType::kNormal,
@@ -746,125 +629,6 @@ static std::unordered_map<std::string, OptionTypeInfo>
OptionTypeFlags::kNone}},
};
static std::unordered_map<std::string, OptionTypeInfo>
compaction_stats_type_info = {
{"micros",
{offsetof(struct InternalStats::CompactionStats, micros),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"cpu_micros",
{offsetof(struct InternalStats::CompactionStats, cpu_micros),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"bytes_read_non_output_levels",
{offsetof(struct InternalStats::CompactionStats,
bytes_read_non_output_levels),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"bytes_read_output_level",
{offsetof(struct InternalStats::CompactionStats,
bytes_read_output_level),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"bytes_skipped_non_output_levels",
{offsetof(struct InternalStats::CompactionStats,
bytes_skipped_non_output_levels),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"bytes_skipped_output_level",
{offsetof(struct InternalStats::CompactionStats,
bytes_skipped_output_level),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"bytes_read_blob",
{offsetof(struct InternalStats::CompactionStats, bytes_read_blob),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"bytes_written",
{offsetof(struct InternalStats::CompactionStats, bytes_written),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"bytes_written_blob",
{offsetof(struct InternalStats::CompactionStats, bytes_written_blob),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"bytes_moved",
{offsetof(struct InternalStats::CompactionStats, bytes_moved),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_input_files_in_non_output_levels",
{offsetof(struct InternalStats::CompactionStats,
num_input_files_in_non_output_levels),
OptionType::kInt, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_input_files_in_output_level",
{offsetof(struct InternalStats::CompactionStats,
num_input_files_in_output_level),
OptionType::kInt, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_filtered_input_files_in_non_output_levels",
{offsetof(struct InternalStats::CompactionStats,
num_filtered_input_files_in_non_output_levels),
OptionType::kInt, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_filtered_input_files_in_output_level",
{offsetof(struct InternalStats::CompactionStats,
num_filtered_input_files_in_output_level),
OptionType::kInt, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_output_files",
{offsetof(struct InternalStats::CompactionStats, num_output_files),
OptionType::kInt, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_output_files_blob",
{offsetof(struct InternalStats::CompactionStats,
num_output_files_blob),
OptionType::kInt, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_input_records",
{offsetof(struct InternalStats::CompactionStats, num_input_records),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_dropped_records",
{offsetof(struct InternalStats::CompactionStats, num_dropped_records),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_output_records",
{offsetof(struct InternalStats::CompactionStats, num_output_records),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"count",
{offsetof(struct InternalStats::CompactionStats, count),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"counts", OptionTypeInfo::Array<
int, static_cast<int>(CompactionReason::kNumOfReasons)>(
offsetof(struct InternalStats::CompactionStats, counts),
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
{0, OptionType::kInt})},
};
static std::unordered_map<std::string, OptionTypeInfo>
compaction_internal_stats_type_info = {
{"output_level_stats",
OptionTypeInfo::Struct(
"output_level_stats", &compaction_stats_type_info,
offsetof(struct InternalStats::CompactionStatsFull,
output_level_stats),
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
{"has_proximal_level_output",
{offsetof(struct InternalStats::CompactionStatsFull,
has_proximal_level_output),
OptionType::kBoolean, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"proximal_level_stats",
OptionTypeInfo::Struct(
"proximal_level_stats", &compaction_stats_type_info,
offsetof(struct InternalStats::CompactionStatsFull,
proximal_level_stats),
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
};
namespace {
// this is a helper struct to serialize and deserialize class Status, because
// Status's members are not public.
@@ -959,6 +723,14 @@ static std::unordered_map<std::string, OptionTypeInfo> cs_result_type_info = {
{offsetof(struct CompactionServiceResult, output_path),
OptionType::kEncodedString, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_output_records",
{offsetof(struct CompactionServiceResult, num_output_records),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"total_bytes",
{offsetof(struct CompactionServiceResult, total_bytes),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"bytes_read",
{offsetof(struct CompactionServiceResult, bytes_read),
OptionType::kUInt64T, OptionVerificationType::kNormal,
@@ -971,11 +743,6 @@ static std::unordered_map<std::string, OptionTypeInfo> cs_result_type_info = {
"stats", &compaction_job_stats_type_info,
offsetof(struct CompactionServiceResult, stats),
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
{"internal_stats",
OptionTypeInfo::Struct(
"internal_stats", &compaction_internal_stats_type_info,
offsetof(struct CompactionServiceResult, internal_stats),
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
};
Status CompactionServiceInput::Read(const std::string& data_str,
File diff suppressed because it is too large. Load diff
+4 -4
View File
@@ -36,11 +36,11 @@ Slice CompactionState::LargestUserKey() {
}
void CompactionState::AggregateCompactionStats(
InternalStats::CompactionStatsFull& internal_stats,
CompactionJobStats& job_stats) {
InternalStats::CompactionStatsFull& compaction_stats,
CompactionJobStats& compaction_job_stats) {
for (const auto& sc : sub_compact_states) {
sc.AggregateCompactionOutputStats(internal_stats);
job_stats.Add(sc.compaction_job_stats);
sc.AggregateCompactionStats(compaction_stats);
compaction_job_stats.Add(sc.compaction_job_stats);
}
}
} // namespace ROCKSDB_NAMESPACE
+2 -2
View File
@@ -29,8 +29,8 @@ class CompactionState {
Status status;
void AggregateCompactionStats(
InternalStats::CompactionStatsFull& internal_stats,
CompactionJobStats& job_stats);
InternalStats::CompactionStatsFull& compaction_stats,
CompactionJobStats& compaction_job_stats);
explicit CompactionState(Compaction* c) : compaction(c) {}
Loaded 100 of 807 files, more files were not shown because too many files have changed in this diff. Show more