mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 22:55:23 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c464412cf8 | |||
| 4411488585 | |||
| 8cc712c0eb | |||
| 62b71b3094 |
+428
-426
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
$VS_DOWNLOAD_LINK = "https://go.microsoft.com/fwlink/?LinkId=691126"
|
||||
$COLLECT_DOWNLOAD_LINK = "https://aka.ms/vscollect.exe"
|
||||
curl.exe --retry 3 -kL $VS_DOWNLOAD_LINK --output vs_installer.exe
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
echo "Download of the VS 2015 installer failed"
|
||||
exit 1
|
||||
}
|
||||
$VS_INSTALL_ARGS = @("/Quiet", "/NoRestart")
|
||||
$process = Start-Process "${PWD}\vs_installer.exe" -ArgumentList $VS_INSTALL_ARGS -NoNewWindow -Wait -PassThru
|
||||
Remove-Item -Path vs_installer.exe -Force
|
||||
$exitCode = $process.ExitCode
|
||||
if (($exitCode -ne 0) -and ($exitCode -ne 3010)) {
|
||||
echo "VS 2015 installer exited with code $exitCode, which should be one of [0, 3010]."
|
||||
curl.exe --retry 3 -kL $COLLECT_DOWNLOAD_LINK --output Collect.exe
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
echo "Download of the VS Collect tool failed."
|
||||
exit 1
|
||||
}
|
||||
Start-Process "${PWD}\Collect.exe" -NoNewWindow -Wait -PassThru
|
||||
New-Item -Path "C:\w\build-results" -ItemType "directory" -Force
|
||||
Copy-Item -Path "C:\Users\circleci\AppData\Local\Temp\vslogs.zip" -Destination "C:\w\build-results\"
|
||||
exit 1
|
||||
}
|
||||
echo "VS 2015 installed."
|
||||
@@ -0,0 +1,35 @@
|
||||
$VS_DOWNLOAD_LINK = "https://aka.ms/vs/15/release/vs_buildtools.exe"
|
||||
$COLLECT_DOWNLOAD_LINK = "https://aka.ms/vscollect.exe"
|
||||
$VS_INSTALL_ARGS = @("--nocache","--quiet","--wait", "--add Microsoft.VisualStudio.Workload.VCTools",
|
||||
"--add Microsoft.VisualStudio.Component.VC.Tools.14.13",
|
||||
"--add Microsoft.Component.MSBuild",
|
||||
"--add Microsoft.VisualStudio.Component.Roslyn.Compiler",
|
||||
"--add Microsoft.VisualStudio.Component.TextTemplating",
|
||||
"--add Microsoft.VisualStudio.Component.VC.CoreIde",
|
||||
"--add Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
|
||||
"--add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core",
|
||||
"--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||
"--add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Win81")
|
||||
|
||||
curl.exe --retry 3 -kL $VS_DOWNLOAD_LINK --output vs_installer.exe
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
echo "Download of the VS 2017 installer failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$process = Start-Process "${PWD}\vs_installer.exe" -ArgumentList $VS_INSTALL_ARGS -NoNewWindow -Wait -PassThru
|
||||
Remove-Item -Path vs_installer.exe -Force
|
||||
$exitCode = $process.ExitCode
|
||||
if (($exitCode -ne 0) -and ($exitCode -ne 3010)) {
|
||||
echo "VS 2017 installer exited with code $exitCode, which should be one of [0, 3010]."
|
||||
curl.exe --retry 3 -kL $COLLECT_DOWNLOAD_LINK --output Collect.exe
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
echo "Download of the VS Collect tool failed."
|
||||
exit 1
|
||||
}
|
||||
Start-Process "${PWD}\Collect.exe" -NoNewWindow -Wait -PassThru
|
||||
New-Item -Path "C:\w\build-results" -ItemType "directory" -Force
|
||||
Copy-Item -Path "C:\Users\circleci\AppData\Local\Temp\vslogs.zip" -Destination "C:\w\build-results\"
|
||||
exit 1
|
||||
}
|
||||
echo "VS 2017 installed."
|
||||
@@ -1,7 +0,0 @@
|
||||
name: build-folly
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Build folly and dependencies
|
||||
run: make build_folly
|
||||
shell: bash
|
||||
@@ -1,8 +0,0 @@
|
||||
name: build-for-benchmarks
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Linux build for benchmarks
|
||||
run: make V=1 J=8 -j8 release
|
||||
shell: bash
|
||||
@@ -1,10 +0,0 @@
|
||||
name: increase-max-open-files-on-macos
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Increase max open files
|
||||
run: |-
|
||||
sudo sysctl -w kern.maxfiles=1048576
|
||||
sudo sysctl -w kern.maxfilesperproc=1048576
|
||||
sudo launchctl limit maxfiles 1048576
|
||||
shell: bash
|
||||
@@ -1,7 +0,0 @@
|
||||
name: install-gflags-on-macos
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install gflags on macos
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install gflags
|
||||
shell: bash
|
||||
@@ -1,7 +0,0 @@
|
||||
name: install-gflags
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install gflags
|
||||
run: sudo apt-get update -y && sudo apt-get install -y libgflags-dev
|
||||
shell: bash
|
||||
@@ -1,9 +0,0 @@
|
||||
name: install-jdk8-on-macos
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install JDK 8 on macos
|
||||
run: |-
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew tap bell-sw/liberica
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install --cask liberica-jdk8
|
||||
shell: bash
|
||||
@@ -1,11 +0,0 @@
|
||||
name: install-maven
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install Maven
|
||||
run: |
|
||||
wget --no-check-certificate https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz
|
||||
tar zxf apache-maven-3.9.6-bin.tar.gz
|
||||
echo "export M2_HOME=$(pwd)/apache-maven-3.9.6" >> $GITHUB_ENV
|
||||
echo "$(pwd)/apache-maven-3.9.6/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
@@ -1,22 +0,0 @@
|
||||
name: perform-benchmarks
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Test low-variance benchmarks
|
||||
run: "./tools/benchmark_ci.py --db_dir ${{ runner.temp }}/rocksdb-benchmark-datadir --output_dir ${{ runner.temp }}/benchmark-results --num_keys 20000000"
|
||||
env:
|
||||
LD_LIBRARY_PATH: "/usr/local/lib"
|
||||
DURATION_RO: 300
|
||||
DURATION_RW: 500
|
||||
NUM_THREADS: 1
|
||||
MAX_BACKGROUND_JOBS: 4
|
||||
CI_TESTS_ONLY: 'true'
|
||||
WRITE_BUFFER_SIZE_MB: 16
|
||||
TARGET_FILE_SIZE_BASE_MB: 16
|
||||
MAX_BYTES_FOR_LEVEL_BASE_MB: 64
|
||||
COMPRESSION_TYPE: none
|
||||
CACHE_INDEX_AND_FILTER_BLOCKS: 1
|
||||
MIN_LEVEL_TO_COMPRESS: 3
|
||||
CACHE_SIZE_MB: 10240
|
||||
MB_WRITE_PER_SEC: 2
|
||||
shell: bash
|
||||
@@ -1,17 +0,0 @@
|
||||
name: post-benchmarks
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Upload Benchmark Results artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: benchmark-results
|
||||
path: "${{ runner.temp }}/benchmark-results/**"
|
||||
if-no-files-found: error
|
||||
- name: Send benchmark report to visualisation
|
||||
run: |-
|
||||
set +e
|
||||
set +o pipefail
|
||||
./build_tools/benchmark_log_tool.py --tsvfile ${{ runner.temp }}/benchmark-results/report.tsv --esdocument https://search-rocksdb-bench-k2izhptfeap2hjfxteolsgsynm.us-west-2.es.amazonaws.com/bench_test3_rix/_doc
|
||||
true
|
||||
shell: bash
|
||||
@@ -1,38 +0,0 @@
|
||||
name: post-steps
|
||||
description: Steps that are taken after a RocksDB job
|
||||
inputs:
|
||||
artifact-prefix:
|
||||
description: Prefix to append to the name of artifacts that are uploaded
|
||||
required: true
|
||||
default: "${{ github.job }}"
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Upload Test Results artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: "${{ inputs.artifact-prefix }}-test-results"
|
||||
path: "${{ runner.temp }}/test-results/**"
|
||||
- name: Upload DB LOG file artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: "${{ inputs.artifact-prefix }}-db-log-file"
|
||||
path: LOG
|
||||
- name: Copy Test Logs (on Failure)
|
||||
if: ${{ failure() }}
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/failure-test-logs
|
||||
cp -r t/* ${{ runner.temp }}/failure-test-logs
|
||||
shell: bash
|
||||
- name: Upload Test Logs (on Failure) artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: "${{ inputs.artifact-prefix }}-failure-test-logs"
|
||||
path: ${{ runner.temp }}/failure-test-logs/**
|
||||
if-no-files-found: ignore
|
||||
- name: Upload Core Dumps artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: "${{ inputs.artifact-prefix }}-core-dumps"
|
||||
path: "core.*"
|
||||
if-no-files-found: ignore
|
||||
@@ -1,5 +0,0 @@
|
||||
name: pre-steps-macos
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -1,18 +0,0 @@
|
||||
name: pre-steps
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup Environment Variables
|
||||
run: |-
|
||||
echo "GTEST_THROW_ON_FAILURE=0" >> "$GITHUB_ENV"
|
||||
echo "GTEST_OUTPUT=\"xml:${{ runner.temp }}/test-results/\"" >> "$GITHUB_ENV"
|
||||
echo "SKIP_FORMAT_BUCK_CHECKS=1" >> "$GITHUB_ENV"
|
||||
echo "GTEST_COLOR=1" >> "$GITHUB_ENV"
|
||||
echo "CTEST_OUTPUT_ON_FAILURE=1" >> "$GITHUB_ENV"
|
||||
echo "CTEST_TEST_TIMEOUT=300" >> "$GITHUB_ENV"
|
||||
echo "ZLIB_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zlib" >> "$GITHUB_ENV"
|
||||
echo "BZIP2_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/bzip2" >> "$GITHUB_ENV"
|
||||
echo "SNAPPY_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/snappy" >> "$GITHUB_ENV"
|
||||
echo "LZ4_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/lz4" >> "$GITHUB_ENV"
|
||||
echo "ZSTD_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zstd" >> "$GITHUB_ENV"
|
||||
shell: bash
|
||||
@@ -1,7 +0,0 @@
|
||||
name: setup-folly
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Checkout folly sources
|
||||
run: make checkout_folly
|
||||
shell: bash
|
||||
@@ -1,20 +0,0 @@
|
||||
name: build-folly
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Fix repo ownership
|
||||
# Needed in some cases, as safe.directory setting doesn't take effect
|
||||
# under env -i
|
||||
run: chown `whoami` . || true
|
||||
shell: bash
|
||||
- name: Set upstream
|
||||
run: git remote add upstream https://github.com/facebook/rocksdb.git
|
||||
shell: bash
|
||||
- name: Fetch upstream
|
||||
run: git fetch upstream
|
||||
shell: bash
|
||||
- name: Git status
|
||||
# NOTE: some old branch builds under check_format_compatible.sh invoke
|
||||
# git under env -i
|
||||
run: git status && git remote -v && env -i git branch
|
||||
shell: bash
|
||||
@@ -1,54 +0,0 @@
|
||||
name: windows-build-steps
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v1.3.1
|
||||
- name: Custom steps
|
||||
env:
|
||||
THIRDPARTY_HOME: ${{ github.workspace }}/thirdparty
|
||||
CMAKE_HOME: C:/Program Files/CMake
|
||||
CMAKE_BIN: C:/Program Files/CMake/bin/cmake.exe
|
||||
CTEST_BIN: C:/Program Files/CMake/bin/ctest.exe
|
||||
JAVA_HOME: C:/Program Files/BellSoft/LibericaJDK-8
|
||||
SNAPPY_HOME: ${{ github.workspace }}/thirdparty/snappy-1.1.8
|
||||
SNAPPY_INCLUDE: ${{ github.workspace }}/thirdparty/snappy-1.1.8;${{ github.workspace }}/thirdparty/snappy-1.1.8/build
|
||||
SNAPPY_LIB_DEBUG: ${{ github.workspace }}/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
|
||||
run: |-
|
||||
# NOTE: if ... Exit $LASTEXITCODE lines needed to exit and report failure
|
||||
echo ===================== Install Dependencies =====================
|
||||
choco install liberica8jdk -y
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
mkdir $Env:THIRDPARTY_HOME
|
||||
cd $Env:THIRDPARTY_HOME
|
||||
echo "Building Snappy dependency..."
|
||||
curl -Lo snappy-1.1.8.zip https://github.com/google/snappy/archive/refs/tags/1.1.8.zip
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
unzip -q snappy-1.1.8.zip
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
cd snappy-1.1.8
|
||||
mkdir build
|
||||
cd build
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" ..
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
msbuild Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
echo ======================== Build RocksDB =========================
|
||||
cd ${{ github.workspace }}
|
||||
$env:Path = $env:JAVA_HOME + ";" + $env:Path
|
||||
mkdir build
|
||||
cd build
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DJNI=1 ..
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
cd ..
|
||||
echo "Building with VS version: $Env:CMAKE_GENERATOR"
|
||||
msbuild build/rocksdb.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
echo ========================= Test RocksDB =========================
|
||||
build_tools\run_ci_db_test.ps1 -SuiteRun arena_test,db_basic_test,db_test,db_test2,db_merge_operand_test,bloom_test,c_test,coding_test,crc32c_test,dynamic_bloom_test,env_basic_test,env_test,hash_test,random_test -Concurrency 16
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
echo ======================== Test RocksJava ========================
|
||||
cd build\java
|
||||
& ctest -C Debug -j 16
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
shell: pwsh
|
||||
@@ -1,15 +0,0 @@
|
||||
name: facebook/rocksdb/benchmark-linux
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
# FIXME: when this job is fixed, it should be given a cron schedule like
|
||||
# schedule:
|
||||
# - cron: 0 * * * *
|
||||
# workflow_dispatch:
|
||||
benchmark-linux:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/build-for-benchmarks"
|
||||
- uses: "./.github/actions/perform-benchmarks"
|
||||
- uses: "./.github/actions/post-benchmarks"
|
||||
@@ -1,18 +0,0 @@
|
||||
name: facebook/rocksdb/nightly
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
# These jobs would be in nightly but are failing or otherwise broken for
|
||||
# some reason.
|
||||
build-linux-arm-test-full:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: arm64large
|
||||
container:
|
||||
image: ubuntu-2004:202111-02
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/install-gflags"
|
||||
- run: make V=1 J=4 -j4 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
@@ -1,98 +0,0 @@
|
||||
name: facebook/rocksdb/nightly
|
||||
on:
|
||||
schedule:
|
||||
- cron: 0 9 * * *
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
build-format-compatible:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
with:
|
||||
fetch-depth: 0 # Need full repo history
|
||||
fetch-tags: true
|
||||
- uses: "./.github/actions/setup-upstream"
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: test
|
||||
run: |-
|
||||
export TEST_TMPDIR=/dev/shm/rocksdb
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
git config --global --add safe.directory /__w/rocksdb/rocksdb
|
||||
tools/check_format_compatible.sh
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-run-microbench:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: DEBUG_LEVEL=0 make -j32 run_microbench
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-non-shm:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
TEST_TMPDIR: "/tmp/rocksdb_test_tmp"
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: make V=1 -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang-13-asan-ubsan-with-folly:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/build-folly"
|
||||
- run: CC=clang-13 CXX=clang++-13 LIB_MODE=static USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-valgrind:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: PORTABLE=1 make V=1 -j32 valgrind_test
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-windows-vs2022-avx2:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: windows-2022
|
||||
env:
|
||||
CMAKE_GENERATOR: Visual Studio 17 2022
|
||||
CMAKE_PORTABLE: AVX2
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/windows-build-steps"
|
||||
build-windows-vs2022:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: windows-2022
|
||||
env:
|
||||
CMAKE_GENERATOR: Visual Studio 17 2022
|
||||
CMAKE_PORTABLE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/windows-build-steps"
|
||||
@@ -1,46 +0,0 @@
|
||||
name: facebook/rocksdb/pr-jobs-candidate
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
# These jobs would be in pr-jobs but are failing or otherwise broken for
|
||||
# some reason.
|
||||
# =========================== ARM Jobs ============================ #
|
||||
build-linux-arm:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: arm64large # GitHub hosted ARM runners do not yet exist
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/install-gflags"
|
||||
- run: ROCKSDBTESTS_PLATFORM_DEPENDENT=only make V=1 J=4 -j4 all_but_some_tests check_some
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-arm-cmake-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: arm64large # GitHub hosted ARM runners do not yet exist
|
||||
env:
|
||||
JAVA_HOME: "/usr/lib/jvm/java-8-openjdk-arm64"
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/install-gflags"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Build with cmake
|
||||
run: |-
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=0 -DWITH_GFLAGS=1 -DWITH_BENCHMARK_TOOLS=0 -DWITH_TOOLS=0 -DWITH_CORE_TOOLS=1 ..
|
||||
make -j4
|
||||
- name: Build Java with cmake
|
||||
run: |-
|
||||
rm -rf build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DJNI=1 -DCMAKE_BUILD_TYPE=Release -DWITH_GFLAGS=1 ..
|
||||
make -j4 rocksdb rocksdbjni
|
||||
- uses: "./.github/actions/post-steps"
|
||||
@@ -1,609 +0,0 @@
|
||||
name: facebook/rocksdb/pr-jobs
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
# NOTE: multiple workflows would be recommended, but the current GHA UI in
|
||||
# PRs doesn't make it clear when there's an overall error with a workflow,
|
||||
# making it easy to overlook something broken. Grouping everything into one
|
||||
# workflow minimizes the problem because it will be suspicious if there are
|
||||
# no GHA results.
|
||||
#
|
||||
# The if: ${{ github.repository_owner == 'facebook' }} lines prevent the
|
||||
# jobs from attempting to run on repo forks, because of a few problems:
|
||||
# * runs-on labels are repository (owner) specific, so the job might wait
|
||||
# for days waiting for a runner that simply isn't available.
|
||||
# * Pushes to branches on forks for pull requests (the normal process) would
|
||||
# run the workflow jobs twice: once in the pull-from fork and once for the PR
|
||||
# destination repo. This is wasteful and dumb.
|
||||
# * It is not known how to avoid copy-pasting the line to each job,
|
||||
# increasing the risk of misconfiguration, especially on forks that might
|
||||
# want to run with this GHA setup.
|
||||
#
|
||||
# DEBUGGING WITH SSH: Temporarily add this as a job step, either before the
|
||||
# step of interest without the "if:" line or after the failing step with the
|
||||
# "if:" line. Then use ssh command printed in CI output.
|
||||
# - name: Setup tmate session # TEMPORARY!
|
||||
# if: ${{ failure() }}
|
||||
# uses: mxschmitt/action-tmate@v3
|
||||
# with:
|
||||
# limit-access-to-actor: true
|
||||
|
||||
# ======================== Fast Initial Checks ====================== #
|
||||
check-format-and-targets:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
with:
|
||||
fetch-depth: 0 # Need full checkout to determine merge base
|
||||
fetch-tags: true
|
||||
- uses: "./.github/actions/setup-upstream"
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
- name: Install Dependencies
|
||||
run: python -m pip install --upgrade pip
|
||||
- name: Install argparse
|
||||
run: pip install argparse
|
||||
- name: Download clang-format-diff.py
|
||||
run: wget https://raw.githubusercontent.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
|
||||
- name: Check format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
- name: Compare buckify output
|
||||
run: make check-buck-targets
|
||||
- name: Simple source code checks
|
||||
run: make check-sources
|
||||
# ========================= Linux With Tests ======================== #
|
||||
build-linux:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: make V=1 J=32 -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-mingw:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
|
||||
- name: Build cmake-mingw
|
||||
run: |-
|
||||
export PATH=$JAVA_HOME/bin:$PATH
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
mkdir build && cd build && cmake -DJNI=1 -DWITH_GFLAGS=OFF .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb rocksdbjni
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-folly:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/build-folly"
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make V=1 -j20 && ctest -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-folly-lite-no-test:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY_LITE=1 -DWITH_GFLAGS=1 .. && make V=1 -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-gcc-7-with-folly:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/build-folly"
|
||||
- run: USE_FOLLY=1 LIB_MODE=static CC=gcc-7 CXX=g++-7 V=1 make -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-gcc-7-with-folly-lite-no-test:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- run: USE_FOLLY_LITE=1 CC=gcc-7 CXX=g++-7 V=1 make -j32 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-folly-coroutines:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/build-folly"
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_COROUTINES=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make V=1 -j20 && ctest -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-benchmark:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: mkdir build && cd build && cmake -DWITH_GFLAGS=1 -DWITH_BENCHMARK=1 .. && make V=1 -j20 && ctest -j20
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-encrypted_env-no_compression:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: ENCRYPTED_ENV=1 ROCKSDB_DISABLE_SNAPPY=1 ROCKSDB_DISABLE_ZLIB=1 ROCKSDB_DISABLE_BZIP=1 ROCKSDB_DISABLE_LZ4=1 ROCKSDB_DISABLE_ZSTD=1 make V=1 J=32 -j32 check
|
||||
- run: "./sst_dump --help | grep -E -q 'Supported compression types: kNoCompression$' # Verify no compiled in compression\n"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================== Linux No Test Runs ======================= #
|
||||
build-linux-release:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- run: make V=1 -j32 LIB_MODE=shared release
|
||||
- run: ls librocksdb.so
|
||||
- run: "./db_stress --version"
|
||||
- run: make clean
|
||||
- run: make V=1 -j32 release
|
||||
- run: ls librocksdb.a
|
||||
- run: "./db_stress --version"
|
||||
- run: make clean
|
||||
- run: apt-get remove -y libgflags-dev
|
||||
- run: make V=1 -j32 LIB_MODE=shared release
|
||||
- run: ls librocksdb.so
|
||||
- run: if ./db_stress --version; then false; else true; fi
|
||||
- run: make clean
|
||||
- run: make V=1 -j32 release
|
||||
- run: ls librocksdb.a
|
||||
- run: if ./db_stress --version; then false; else true; fi
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-release-rtti:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 8-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j16 static_lib tools db_bench
|
||||
- run: "./db_stress --version"
|
||||
- run: make clean
|
||||
- run: apt-get remove -y libgflags-dev
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j16 static_lib tools db_bench
|
||||
- run: if ./db_stress --version; then false; else true; fi
|
||||
build-examples:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Build examples
|
||||
run: make V=1 -j4 static_lib && cd examples && make V=1 -j4
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-fuzzers:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Build rocksdb lib
|
||||
run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j4 static_lib
|
||||
- name: Build fuzzers
|
||||
run: cd fuzz && make sst_file_writer_fuzzer db_fuzzer db_map_fuzzer
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 8-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- run: CC=clang CXX=clang++ USE_CLANG=1 PORTABLE=1 make V=1 -j16 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang-13-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j32 all microbench
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-gcc-8-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: CC=gcc-8 CXX=g++-8 V=1 make -j32 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-gcc-10-cxx20-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: CC=gcc-10 CXX=g++-10 V=1 ROCKSDB_CXX_STANDARD=c++20 make -j32 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-gcc-11-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: LIB_MODE=static CC=gcc-11 CXX=g++-11 V=1 make -j32 all microbench
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================== Linux Other Checks ======================= #
|
||||
build-linux-clang10-clang-analyze:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 CLANG_ANALYZER="/usr/bin/clang++-10" CLANG_SCAN_BUILD=scan-build-10 USE_CLANG=1 make V=1 -j32 analyze
|
||||
- uses: "./.github/actions/post-steps"
|
||||
- name: compress test report
|
||||
run: tar -cvzf scan_build_report.tar.gz scan_build_report
|
||||
if: failure()
|
||||
- uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: scan-build-report
|
||||
path: scan_build_report.tar.gz
|
||||
build-linux-unity-and-headers:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: gcc:latest
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- run: apt-get update -y && apt-get install -y libgflags-dev
|
||||
- name: Unity build
|
||||
run: make V=1 -j8 unity_test
|
||||
- run: make V=1 -j8 -k check-headers
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-mini-crashtest:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000' blackbox_crash_test_with_atomic_flush
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================= Linux with Sanitizers ===================== #
|
||||
build-linux-clang10-asan:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 32-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang10-ubsan:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: COMPILE_WITH_UBSAN=1 OPT="-fsanitize-blacklist=.circleci/ubsan_suppression_list.txt" CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 ubsan_check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang10-mini-tsan:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 32-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: COMPILE_WITH_TSAN=1 CC=clang-13 CXX=clang++-13 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-static_lib-alt_namespace-status_checked:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: ASSERT_STATUS_CHECKED=1 TEST_UINT128_COMPAT=1 ROCKSDB_MODIFY_NPHASH=1 LIB_MODE=static OPT="-DROCKSDB_NAMESPACE=alternative_rocksdb_ns" make V=1 -j24 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ========================= MacOS build only ======================== #
|
||||
build-macos:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
env:
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 14.3.1
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Build
|
||||
run: ulimit -S -n `ulimit -H -n` && make V=1 J=16 -j16 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ========================= MacOS with Tests ======================== #
|
||||
build-macos-cmake:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
strategy:
|
||||
matrix:
|
||||
run_even_tests: [true, false]
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 14.3.1
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: cmake generate project file
|
||||
run: ulimit -S -n `ulimit -H -n` && mkdir build && cd build && cmake -DWITH_GFLAGS=1 ..
|
||||
- name: Build tests
|
||||
run: cd build && make V=1 -j16
|
||||
- name: Run even tests
|
||||
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j16 -I 0,,2
|
||||
if: ${{ matrix.run_even_tests }}
|
||||
- name: Run odd tests
|
||||
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j16 -I 1,,2
|
||||
if: ${{ ! matrix.run_even_tests }}
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================== Windows with Tests ======================= #
|
||||
# NOTE: some windows jobs are in "nightly" to save resources
|
||||
build-windows-vs2019:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: windows-2019
|
||||
env:
|
||||
CMAKE_GENERATOR: Visual Studio 16 2019
|
||||
CMAKE_PORTABLE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/windows-build-steps"
|
||||
# ============================ Java Jobs ============================ #
|
||||
build-linux-java:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: evolvedbinary/rocksjava:centos6_x64-be
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
# The docker image is intentionally based on an OS that has an older GLIBC version.
|
||||
# That GLIBC is incompatibile with GitHub's actions/checkout. Thus we implement a manual checkout step.
|
||||
- name: Checkout
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
chown `whoami` . || true
|
||||
git clone --no-checkout https://oath2:$GH_TOKEN@github.com/${{ github.repository }}.git .
|
||||
git -c protocol.version=2 fetch --update-head-ok --no-tags --prune --no-recurse-submodules --depth=1 origin +${{ github.sha }}:${{ github.ref }}
|
||||
git checkout --progress --force ${{ github.ref }}
|
||||
git log -1 --format='%H'
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Test RocksDBJava
|
||||
run: scl enable devtoolset-7 'make V=1 J=8 -j8 jtest'
|
||||
# NOTE: post-steps skipped because of compatibility issues with docker image
|
||||
build-linux-java-static:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: evolvedbinary/rocksjava:centos6_x64-be
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
# The docker image is intentionally based on an OS that has an older GLIBC version.
|
||||
# That GLIBC is incompatibile with GitHub's actions/checkout. Thus we implement a manual checkout step.
|
||||
- name: Checkout
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
chown `whoami` . || true
|
||||
git clone --no-checkout https://oath2:$GH_TOKEN@github.com/${{ github.repository }}.git .
|
||||
git -c protocol.version=2 fetch --update-head-ok --no-tags --prune --no-recurse-submodules --depth=1 origin +${{ github.sha }}:${{ github.ref }}
|
||||
git checkout --progress --force ${{ github.ref }}
|
||||
git log -1 --format='%H'
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Build RocksDBJava Static Library
|
||||
run: scl enable devtoolset-7 'make V=1 J=8 -j8 rocksdbjavastatic'
|
||||
# NOTE: post-steps skipped because of compatibility issues with docker image
|
||||
build-macos-java:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
env:
|
||||
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 14.3.1
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/install-jdk8-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Test RocksDBJava
|
||||
run: make V=1 J=16 -j16 jtest
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-macos-java-static:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
env:
|
||||
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 14.3.1
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/install-jdk8-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Build RocksDBJava x86 and ARM Static Libraries
|
||||
run: make V=1 J=16 -j16 rocksdbjavastaticosx
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-macos-java-static-universal:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
env:
|
||||
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 14.3.1
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/install-jdk8-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Build RocksDBJava Universal Binary Static Library
|
||||
run: make V=1 J=16 -j16 rocksdbjavastaticosx_ub
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-java-pmd:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: evolvedbinary/rocksjava:rockylinux8_x64-be
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/install-maven"
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: PMD RocksDBJava
|
||||
run: make V=1 J=8 -j8 jpmd
|
||||
- uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: pmd-report
|
||||
path: "${{ github.workspace }}/java/target/pmd.xml"
|
||||
- uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: maven-site
|
||||
path: "${{ github.workspace }}/java/target/site"
|
||||
@@ -0,0 +1,44 @@
|
||||
name: Check buck targets and code format
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
check:
|
||||
name: Check TARGETS file and code format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout feature branch
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Fetch from upstream
|
||||
run: |
|
||||
git remote add upstream https://github.com/facebook/rocksdb.git && git fetch upstream
|
||||
|
||||
- name: Where am I
|
||||
run: |
|
||||
echo git status && git status
|
||||
echo "git remote -v" && git remote -v
|
||||
echo git branch && git branch
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v1
|
||||
|
||||
- name: Install Dependencies
|
||||
run: python -m pip install --upgrade pip
|
||||
|
||||
- name: Install argparse
|
||||
run: pip install argparse
|
||||
|
||||
- name: Download clang-format-diff.py
|
||||
uses: wei/wget@v1
|
||||
with:
|
||||
args: 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
|
||||
+2
-5
@@ -36,6 +36,7 @@ manifest_dump
|
||||
sst_dump
|
||||
blob_dump
|
||||
block_cache_trace_analyzer
|
||||
db_with_timestamp_basic_test
|
||||
tools/block_cache_analyzer/*.pyc
|
||||
column_aware_encoding_exp
|
||||
util/build_version.cc
|
||||
@@ -51,12 +52,12 @@ rocksdb_dump
|
||||
rocksdb_undump
|
||||
db_test2
|
||||
trace_analyzer
|
||||
trace_analyzer_test
|
||||
block_cache_trace_analyzer
|
||||
io_tracer_parser
|
||||
.DS_Store
|
||||
.vs
|
||||
.vscode
|
||||
.clangd
|
||||
|
||||
java/out
|
||||
java/target
|
||||
@@ -85,7 +86,6 @@ fbcode/
|
||||
fbcode
|
||||
buckifier/*.pyc
|
||||
buckifier/__pycache__
|
||||
.arcconfig
|
||||
|
||||
compile_commands.json
|
||||
clang-format-diff.py
|
||||
@@ -95,6 +95,3 @@ fuzz/proto/gen/
|
||||
fuzz/crash-*
|
||||
|
||||
cmake-build-*
|
||||
third-party/folly/
|
||||
.cache
|
||||
*.sublime-*
|
||||
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
dist: xenial
|
||||
language: cpp
|
||||
os:
|
||||
- linux
|
||||
arch:
|
||||
- arm64
|
||||
- ppc64le
|
||||
- s390x
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
cache:
|
||||
- ccache
|
||||
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- libgflags-dev
|
||||
- libbz2-dev
|
||||
- liblz4-dev
|
||||
- libsnappy-dev
|
||||
- liblzma-dev # xv
|
||||
- libzstd-dev
|
||||
- zlib1g-dev
|
||||
|
||||
env:
|
||||
- TEST_GROUP=platform_dependent # 16-18 minutes
|
||||
- TEST_GROUP=1 # 33-35 minutes
|
||||
- TEST_GROUP=2 # 18-20 minutes
|
||||
- TEST_GROUP=3 # 20-22 minutes
|
||||
- TEST_GROUP=4 # 12-14 minutes
|
||||
# Run java tests
|
||||
- JOB_NAME=java_test # 4-11 minutes
|
||||
# Build ROCKSDB_LITE
|
||||
- JOB_NAME=lite_build # 3-4 minutes
|
||||
# Build examples
|
||||
- JOB_NAME=examples # 5-7 minutes
|
||||
- JOB_NAME=cmake # 3-5 minutes
|
||||
- JOB_NAME=cmake-gcc8 # 3-5 minutes
|
||||
- JOB_NAME=cmake-gcc9 # 3-5 minutes
|
||||
- JOB_NAME=cmake-gcc9-c++20 # 3-5 minutes
|
||||
- JOB_NAME=cmake-mingw # 3 minutes
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: linux
|
||||
compiler: clang
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: arm64
|
||||
env: TEST_GROUP=platform_dependent
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: TEST_GROUP=1
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=1
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: TEST_GROUP=1
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: TEST_GROUP=2
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=2
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: TEST_GROUP=2
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: TEST_GROUP=3
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=3
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: TEST_GROUP=3
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/ AND commit_message !~ /java/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/ AND commit_message !~ /java/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/ AND commit_message !~ /java/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=lite_build
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=lite_build
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=lite_build
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=examples
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=examples
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=examples
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
|
||||
install:
|
||||
- CC=gcc-7 && CXX=g++-7
|
||||
- if [ "${JOB_NAME}" == cmake-gcc8 ]; then
|
||||
sudo apt-get install -y g++-8 || exit $?;
|
||||
CC=gcc-8 && CXX=g++-8;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == cmake-gcc9 ] || [ "${JOB_NAME}" == cmake-gcc9-c++20 ]; then
|
||||
sudo apt-get install -y g++-9 || exit $?;
|
||||
CC=gcc-9 && CXX=g++-9;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == cmake-mingw ]; then
|
||||
sudo apt-get install -y mingw-w64 || exit $?;
|
||||
fi
|
||||
- if [ "${CXX}" == "g++-7" ]; then
|
||||
sudo apt-get install -y g++-7 || exit $?;
|
||||
fi
|
||||
- |
|
||||
if [[ "${JOB_NAME}" == cmake* ]]; then
|
||||
sudo apt-get remove -y cmake cmake-data
|
||||
export CMAKE_DEB="cmake-3.14.5-Linux-$(uname -m).deb"
|
||||
export CMAKE_DEB_URL="https://rocksdb-deps.s3-us-west-2.amazonaws.com/cmake/${CMAKE_DEB}"
|
||||
curl --silent --fail --show-error --location --output "${CMAKE_DEB}" "${CMAKE_DEB_URL}" || exit $?
|
||||
sudo dpkg -i "${CMAKE_DEB}" || exit $?
|
||||
which cmake && cmake --version
|
||||
fi
|
||||
- |
|
||||
if [[ "${JOB_NAME}" == java_test || "${JOB_NAME}" == cmake* ]]; then
|
||||
# Ensure JDK 8
|
||||
sudo apt-get install -y openjdk-8-jdk || exit $?
|
||||
export PATH=/usr/lib/jvm/java-8-openjdk-$(dpkg --print-architecture)/bin:$PATH
|
||||
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-$(dpkg --print-architecture)
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
fi
|
||||
|
||||
before_script:
|
||||
# Increase the maximum number of open file descriptors, since some tests use
|
||||
# more FDs than the default limit.
|
||||
- ulimit -n 8192
|
||||
|
||||
script:
|
||||
- date; ${CXX} --version
|
||||
- if [ `command -v ccache` ]; then ccache -C; fi
|
||||
- export MK_PARALLEL=4;
|
||||
if [[ "$TRAVIS_CPU_ARCH" == s390x ]]; then
|
||||
export MK_PARALLEL=1;
|
||||
fi
|
||||
- case $TEST_GROUP in
|
||||
platform_dependent)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=only make -j$MK_PARALLEL all_but_some_tests check_some
|
||||
;;
|
||||
1)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_END=backupable_db_test make -j$MK_PARALLEL check_some
|
||||
;;
|
||||
2)
|
||||
OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" LIB_MODE=shared V=1 make -j$MK_PARALLEL tools && OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=backupable_db_test ROCKSDBTESTS_END=db_universal_compaction_test make -j$MK_PARALLEL check_some
|
||||
;;
|
||||
3)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=db_universal_compaction_test ROCKSDBTESTS_END=table_properties_collector_test make -j$MK_PARALLEL check_some
|
||||
;;
|
||||
4)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=table_properties_collector_test make -j$MK_PARALLEL check_some
|
||||
;;
|
||||
esac
|
||||
- case $JOB_NAME in
|
||||
java_test)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 make rocksdbjava jtest
|
||||
;;
|
||||
lite_build)
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' LIB_MODE=shared V=1 make -j$MK_PARALLEL all
|
||||
;;
|
||||
examples)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 make -j$MK_PARALLEL static_lib && cd examples && make -j$MK_PARALLEL
|
||||
;;
|
||||
cmake-mingw)
|
||||
sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix;
|
||||
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
|
||||
;;
|
||||
cmake*)
|
||||
case $JOB_NAME in
|
||||
*-c++20)
|
||||
OPT=-DCMAKE_CXX_STANDARD=20
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=0 -DWITH_GFLAGS=0 -DWITH_BENCHMARK_TOOLS=0 -DWITH_TOOLS=0 -DWITH_CORE_TOOLS=1 .. && make -j$MK_PARALLEL && cd .. && rm -rf build && mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_BUILD_TYPE=Release $OPT && make -j$MK_PARALLEL rocksdb rocksdbjni
|
||||
;;
|
||||
esac
|
||||
notifications:
|
||||
email:
|
||||
- leveldb@fb.com
|
||||
+156
-286
@@ -2,7 +2,7 @@
|
||||
# This cmake build is for Windows 64-bit only.
|
||||
#
|
||||
# Prerequisites:
|
||||
# You must have at least Visual Studio 2019. Start the Developer Command Prompt window that is a part of Visual Studio installation.
|
||||
# You must have at least Visual Studio 2015 Update 3. Start the Developer Command Prompt window that is a part of Visual Studio installation.
|
||||
# Run the build commands from within the Developer Command Prompt window to have paths to the compiler and runtime libraries set.
|
||||
# You must have git.exe in your %PATH% environment variable.
|
||||
#
|
||||
@@ -40,8 +40,6 @@ include(GoogleTest)
|
||||
get_rocksdb_version(rocksdb_VERSION)
|
||||
project(rocksdb
|
||||
VERSION ${rocksdb_VERSION}
|
||||
DESCRIPTION "An embeddable persistent key-value store for fast storage"
|
||||
HOMEPAGE_URL https://rocksdb.org/
|
||||
LANGUAGES CXX C ASM)
|
||||
|
||||
if(POLICY CMP0042)
|
||||
@@ -74,7 +72,24 @@ option(WITH_WINDOWS_UTF8_FILENAMES "use UTF8 as characterset for opening files,
|
||||
if (WITH_WINDOWS_UTF8_FILENAMES)
|
||||
add_definitions(-DROCKSDB_WINDOWS_UTF8_FILENAMES)
|
||||
endif()
|
||||
option(ROCKSDB_BUILD_SHARED "Build shared versions of the RocksDB libraries" ON)
|
||||
|
||||
if ($ENV{CIRCLECI})
|
||||
message(STATUS "Build for CircieCI env, a few tests may be disabled")
|
||||
add_definitions(-DCIRCLECI)
|
||||
endif()
|
||||
|
||||
# third-party/folly is only validated to work on Linux and Windows for now.
|
||||
# So only turn it on there by default.
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux|Windows")
|
||||
if(MSVC AND MSVC_VERSION LESS 1910)
|
||||
# Folly does not compile with MSVC older than VS2017
|
||||
option(WITH_FOLLY_DISTRIBUTED_MUTEX "build with folly::DistributedMutex" OFF)
|
||||
else()
|
||||
option(WITH_FOLLY_DISTRIBUTED_MUTEX "build with folly::DistributedMutex" ON)
|
||||
endif()
|
||||
else()
|
||||
option(WITH_FOLLY_DISTRIBUTED_MUTEX "build with folly::DistributedMutex" OFF)
|
||||
endif()
|
||||
|
||||
if( NOT DEFINED CMAKE_CXX_STANDARD )
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
@@ -85,11 +100,7 @@ include(CMakeDependentOption)
|
||||
if(MSVC)
|
||||
option(WITH_GFLAGS "build with GFlags" OFF)
|
||||
option(WITH_XPRESS "build with windows built in compression" OFF)
|
||||
option(ROCKSDB_SKIP_THIRDPARTY "skip thirdparty.inc" OFF)
|
||||
|
||||
if(NOT ROCKSDB_SKIP_THIRDPARTY)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
endif()
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
else()
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD" AND NOT CMAKE_SYSTEM_NAME MATCHES "kFreeBSD")
|
||||
# FreeBSD has jemalloc as default malloc
|
||||
@@ -166,11 +177,31 @@ else()
|
||||
if(WITH_ZSTD)
|
||||
find_package(zstd REQUIRED)
|
||||
add_definitions(-DZSTD)
|
||||
include_directories(${ZSTD_INCLUDE_DIRS})
|
||||
include_directories(${ZSTD_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS zstd::zstd)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
string(TIMESTAMP TS "%Y-%m-%d %H:%M:%S" UTC)
|
||||
set(BUILD_DATE "${TS}" CACHE STRING "the time we first built rocksdb")
|
||||
|
||||
find_package(Git)
|
||||
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_SHA COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD )
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE GIT_MOD COMMAND "${GIT_EXECUTABLE}" diff-index HEAD --quiet)
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_DATE COMMAND "${GIT_EXECUTABLE}" log -1 --date=format:"%Y-%m-%d %T" --format="%ad")
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_TAG RESULT_VARIABLE rv COMMAND "${GIT_EXECUTABLE}" symbolic-ref -q --short HEAD OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (rv AND NOT rv EQUAL 0)
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_TAG COMMAND "${GIT_EXECUTABLE}" describe --tags --exact-match OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
else()
|
||||
set(GIT_SHA 0)
|
||||
set(GIT_MOD 1)
|
||||
endif()
|
||||
string(REGEX REPLACE "[^0-9a-fA-F]+" "" GIT_SHA "${GIT_SHA}")
|
||||
string(REGEX REPLACE "[^0-9: /-]+" "" GIT_DATE "${GIT_DATE}")
|
||||
|
||||
option(WITH_MD_LIBRARY "build with MD" ON)
|
||||
if(WIN32 AND MSVC)
|
||||
if(WITH_MD_LIBRARY)
|
||||
@@ -180,6 +211,9 @@ if(WIN32 AND MSVC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(BUILD_VERSION_CC ${CMAKE_BINARY_DIR}/build_version.cc)
|
||||
configure_file(util/build_version.cc.in ${BUILD_VERSION_CC} @ONLY)
|
||||
|
||||
if(MSVC)
|
||||
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 /wd4800 /wd4996 /wd4351 /wd4100 /wd4204 /wd4324")
|
||||
@@ -190,7 +224,7 @@ else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes")
|
||||
endif()
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format -fno-asynchronous-unwind-tables")
|
||||
add_definitions(-D_POSIX_C_SOURCE=1)
|
||||
endif()
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
@@ -240,46 +274,45 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "s390x")
|
||||
endif(HAS_S390X_MARCH_NATIVE)
|
||||
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} -mcpu=loongarch64 -mtune=loongarch64")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=loongarch64 -mtune=loongarch64")
|
||||
endif(HAS_LOONGARCH64)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "loongarch64")
|
||||
|
||||
set(PORTABLE 0 CACHE STRING "Minimum CPU arch to support, or 0 = current CPU, 1 = baseline CPU")
|
||||
if(PORTABLE MATCHES "1|ON|YES|TRUE|Y")
|
||||
# Usually nothing to do; compiler default is typically the most general
|
||||
if(NOT MSVC)
|
||||
option(PORTABLE "build a portable binary" OFF)
|
||||
option(FORCE_SSE42 "force building with SSE4.2, even when PORTABLE=ON" OFF)
|
||||
option(FORCE_AVX "force building with AVX, even when PORTABLE=ON" OFF)
|
||||
option(FORCE_AVX2 "force building with AVX2, even when PORTABLE=ON" OFF)
|
||||
if(PORTABLE)
|
||||
# MSVC does not need a separate compiler flag to enable SSE4.2; if nmmintrin.h
|
||||
# is available, it is available by default.
|
||||
if(FORCE_SSE42 AND NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.2 -mpclmul")
|
||||
endif()
|
||||
if(MSVC)
|
||||
if(FORCE_AVX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX")
|
||||
endif()
|
||||
# MSVC automatically enables BMI / lzcnt with AVX2.
|
||||
if(FORCE_AVX2)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
endif()
|
||||
else()
|
||||
if(FORCE_AVX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx")
|
||||
endif()
|
||||
if(FORCE_AVX2)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx2 -mbmi -mlzcnt")
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390x")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=z196")
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^loongarch64")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=loongarch64")
|
||||
endif()
|
||||
endif()
|
||||
elseif(PORTABLE MATCHES "0|OFF|NO|FALSE|N")
|
||||
else()
|
||||
if(MSVC)
|
||||
# NOTE: No auto-detection of current CPU, but instead assume some useful
|
||||
# level of optimization is supported
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
else()
|
||||
# Require instruction set from current CPU (with some legacy or opt-out
|
||||
# exceptions)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390x" AND NOT HAS_S390X_MARCH_NATIVE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=z196")
|
||||
elseif(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64" AND NOT HAS_ARMV8_CRC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
# Name of a CPU arch spec or feature set to require
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:${PORTABLE}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${PORTABLE}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
@@ -288,6 +321,27 @@ if(NOT MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
|
||||
if (NOT PORTABLE OR FORCE_SSE42)
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
#include <wmmintrin.h>
|
||||
int main() {
|
||||
volatile uint32_t x = _mm_crc32_u32(0, 0);
|
||||
const auto a = _mm_set_epi64x(0, 0);
|
||||
const auto b = _mm_set_epi64x(0, 0);
|
||||
const auto c = _mm_clmulepi64_si128(a, b, 0x00);
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
}
|
||||
" HAVE_SSE42)
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
add_definitions(-DHAVE_PCLMUL)
|
||||
elseif(FORCE_SSE42)
|
||||
message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Check if -latomic is required or not
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "--std=c++17")
|
||||
@@ -317,6 +371,9 @@ endif()
|
||||
# Reset the required flags
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
|
||||
# thread_local is part of C++11 and later (TODO: clean up this define)
|
||||
add_definitions(-DROCKSDB_SUPPORT_THREAD_LOCAL)
|
||||
|
||||
option(WITH_IOSTATS_CONTEXT "Enable IO stats context" ON)
|
||||
if (NOT WITH_IOSTATS_CONTEXT)
|
||||
add_definitions(-DNIOSTATS_CONTEXT)
|
||||
@@ -371,7 +428,7 @@ option(WITH_NUMA "build with NUMA policy support" OFF)
|
||||
if(WITH_NUMA)
|
||||
find_package(NUMA REQUIRED)
|
||||
add_definitions(-DNUMA)
|
||||
include_directories(${NUMA_INCLUDE_DIRS})
|
||||
include_directories(${NUMA_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS NUMA::NUMA)
|
||||
endif()
|
||||
|
||||
@@ -399,32 +456,30 @@ if (ASSERT_STATUS_CHECKED)
|
||||
add_definitions(-DROCKSDB_ASSERT_STATUS_CHECKED)
|
||||
endif()
|
||||
|
||||
|
||||
# RTTI is by default AUTO which enables it in debug and disables it in release.
|
||||
set(USE_RTTI AUTO CACHE STRING "Enable RTTI in builds")
|
||||
set_property(CACHE USE_RTTI PROPERTY STRINGS AUTO ON OFF)
|
||||
if(USE_RTTI STREQUAL "AUTO")
|
||||
if(DEFINED USE_RTTI)
|
||||
if(USE_RTTI)
|
||||
message(STATUS "Enabling RTTI")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DROCKSDB_USE_RTTI")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DROCKSDB_USE_RTTI")
|
||||
else()
|
||||
if(MSVC)
|
||||
message(STATUS "Disabling RTTI in Release builds. Always on in Debug.")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DROCKSDB_USE_RTTI")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GR-")
|
||||
else()
|
||||
message(STATUS "Disabling RTTI in Release builds")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-rtti")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-rtti")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Enabling RTTI in Debug builds only (default)")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DROCKSDB_USE_RTTI")
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GR-")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GR-")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-rtti")
|
||||
endif()
|
||||
elseif(USE_RTTI)
|
||||
message(STATUS "Enabling RTTI in all builds")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DROCKSDB_USE_RTTI")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DROCKSDB_USE_RTTI")
|
||||
else()
|
||||
if(MSVC)
|
||||
message(STATUS "Disabling RTTI in Release builds. Always on in Debug.")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DROCKSDB_USE_RTTI")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GR-")
|
||||
else()
|
||||
message(STATUS "Disabling RTTI in all builds")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-rtti")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-rtti")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Used to run CI build and tests so we can run faster
|
||||
@@ -460,6 +515,12 @@ if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp")
|
||||
endif()
|
||||
|
||||
option(ROCKSDB_LITE "Build RocksDBLite version" OFF)
|
||||
if(ROCKSDB_LITE)
|
||||
add_definitions(-DROCKSDB_LITE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -Os")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Cygwin")
|
||||
add_definitions(-fno-builtin-memcmp -DCYGWIN)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||
@@ -542,7 +603,7 @@ if(HAVE_SCHED_GETCPU)
|
||||
add_definitions(-DROCKSDB_SCHED_GETCPU_PRESENT)
|
||||
endif()
|
||||
|
||||
check_cxx_symbol_exists(getauxval "sys/auxv.h" HAVE_AUXV_GETAUXVAL)
|
||||
check_cxx_symbol_exists(getauxval auvx.h HAVE_AUXV_GETAUXVAL)
|
||||
if(HAVE_AUXV_GETAUXVAL)
|
||||
add_definitions(-DROCKSDB_AUXV_GETAUXVAL_PRESENT)
|
||||
endif()
|
||||
@@ -554,61 +615,8 @@ endif()
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
if(USE_COROUTINES)
|
||||
if(USE_FOLLY OR USE_FOLLY_LITE)
|
||||
message(FATAL_ERROR "Please specify exactly one of USE_COROUTINES,"
|
||||
" USE_FOLLY, and USE_FOLLY_LITE")
|
||||
endif()
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines -Wno-maybe-uninitialized")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-redundant-move")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-memory-model")
|
||||
add_compile_definitions(USE_COROUTINES)
|
||||
set(USE_FOLLY 1)
|
||||
endif()
|
||||
|
||||
if(USE_FOLLY)
|
||||
if(USE_FOLLY_LITE)
|
||||
message(FATAL_ERROR "Please specify one of USE_FOLLY or USE_FOLLY_LITE")
|
||||
endif()
|
||||
if(ROCKSDB_BUILD_SHARED)
|
||||
message(FATAL_ERROR "Cannot build RocksDB shared library with folly")
|
||||
endif()
|
||||
set(ROCKSDB_BUILD_SHARED OFF)
|
||||
set(GFLAGS_SHARED FALSE)
|
||||
find_package(folly)
|
||||
# If cmake could not find the folly-config.cmake file, fall back
|
||||
# to looking in third-party/folly for folly and its dependencies
|
||||
if(NOT FOLLY_LIBRARIES)
|
||||
exec_program(python3 ${PROJECT_SOURCE_DIR}/third-party/folly ARGS
|
||||
build/fbcode_builder/getdeps.py show-inst-dir OUTPUT_VARIABLE
|
||||
FOLLY_INST_PATH)
|
||||
exec_program(ls ARGS -d ${FOLLY_INST_PATH}/../boost* OUTPUT_VARIABLE
|
||||
BOOST_INST_PATH)
|
||||
exec_program(ls ARGS -d ${FOLLY_INST_PATH}/../fmt* OUTPUT_VARIABLE
|
||||
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.78.0)
|
||||
if(EXISTS ${FMT_INST_PATH}/lib64)
|
||||
set(fmt_DIR ${FMT_INST_PATH}/lib64/cmake/fmt)
|
||||
else()
|
||||
set(fmt_DIR ${FMT_INST_PATH}/lib/cmake/fmt)
|
||||
endif()
|
||||
set(gflags_DIR ${GFLAGS_INST_PATH}/lib/cmake/gflags)
|
||||
|
||||
exec_program(sed ARGS -i 's/gflags_shared//g'
|
||||
${FOLLY_INST_PATH}/lib/cmake/folly/folly-targets.cmake)
|
||||
|
||||
include(${FOLLY_INST_PATH}/lib/cmake/folly/folly-config.cmake)
|
||||
endif()
|
||||
|
||||
add_compile_definitions(USE_FOLLY FOLLY_NO_CONFIG HAVE_CXX11_ATOMIC)
|
||||
list(APPEND THIRDPARTY_LIBS Folly::folly)
|
||||
set(FOLLY_LIBS Folly::folly)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--copy-dt-needed-entries")
|
||||
if(WITH_FOLLY_DISTRIBUTED_MUTEX)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third-party/folly)
|
||||
endif()
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
@@ -618,18 +626,12 @@ set(SOURCES
|
||||
cache/cache.cc
|
||||
cache/cache_entry_roles.cc
|
||||
cache/cache_key.cc
|
||||
cache/cache_helpers.cc
|
||||
cache/cache_reservation_manager.cc
|
||||
cache/charged_cache.cc
|
||||
cache/clock_cache.cc
|
||||
cache/compressed_secondary_cache.cc
|
||||
cache/lru_cache.cc
|
||||
cache/secondary_cache.cc
|
||||
cache/secondary_cache_adapter.cc
|
||||
cache/lru_secondary_cache.cc
|
||||
cache/sharded_cache.cc
|
||||
cache/tiered_secondary_cache.cc
|
||||
db/arena_wrapped_db_iter.cc
|
||||
db/blob/blob_contents.cc
|
||||
db/blob/blob_fetcher.cc
|
||||
db/blob/blob_file_addition.cc
|
||||
db/blob/blob_file_builder.cc
|
||||
@@ -641,7 +643,6 @@ set(SOURCES
|
||||
db/blob/blob_log_format.cc
|
||||
db/blob/blob_log_sequential_reader.cc
|
||||
db/blob/blob_log_writer.cc
|
||||
db/blob/blob_source.cc
|
||||
db/blob/prefetch_buffer_collection.cc
|
||||
db/builder.cc
|
||||
db/c.cc
|
||||
@@ -653,11 +654,7 @@ set(SOURCES
|
||||
db/compaction/compaction_picker_fifo.cc
|
||||
db/compaction/compaction_picker_level.cc
|
||||
db/compaction/compaction_picker_universal.cc
|
||||
db/compaction/compaction_service_job.cc
|
||||
db/compaction/compaction_state.cc
|
||||
db/compaction/compaction_outputs.cc
|
||||
db/compaction/sst_partitioner.cc
|
||||
db/compaction/subcompaction_state.cc
|
||||
db/convenience.cc
|
||||
db/db_filesnapshot.cc
|
||||
db/db_impl/compacted_db_impl.cc
|
||||
@@ -692,11 +689,10 @@ set(SOURCES
|
||||
db/merge_helper.cc
|
||||
db/merge_operator.cc
|
||||
db/output_validator.cc
|
||||
db/periodic_task_scheduler.cc
|
||||
db/periodic_work_scheduler.cc
|
||||
db/range_del_aggregator.cc
|
||||
db/range_tombstone_fragmenter.cc
|
||||
db/repair.cc
|
||||
db/seqno_to_time_mapping.cc
|
||||
db/snapshot_impl.cc
|
||||
db/table_cache.cc
|
||||
db/table_properties_collector.cc
|
||||
@@ -708,13 +704,9 @@ set(SOURCES
|
||||
db/version_set.cc
|
||||
db/wal_edit.cc
|
||||
db/wal_manager.cc
|
||||
db/wide/wide_column_serialization.cc
|
||||
db/wide/wide_columns.cc
|
||||
db/wide/wide_columns_helper.cc
|
||||
db/write_batch.cc
|
||||
db/write_batch_base.cc
|
||||
db/write_controller.cc
|
||||
db/write_stall_stats.cc
|
||||
db/write_thread.cc
|
||||
env/composite_env.cc
|
||||
env/env.cc
|
||||
@@ -767,21 +759,19 @@ set(SOURCES
|
||||
options/configurable.cc
|
||||
options/customizable.cc
|
||||
options/db_options.cc
|
||||
options/offpeak_time_info.cc
|
||||
options/options.cc
|
||||
options/options_helper.cc
|
||||
options/options_parser.cc
|
||||
port/mmap.cc
|
||||
port/stack_trace.cc
|
||||
table/adaptive/adaptive_table_factory.cc
|
||||
table/block_based/binary_search_index_reader.cc
|
||||
table/block_based/block.cc
|
||||
table/block_based/block_based_filter_block.cc
|
||||
table/block_based/block_based_table_builder.cc
|
||||
table/block_based/block_based_table_factory.cc
|
||||
table/block_based/block_based_table_iterator.cc
|
||||
table/block_based/block_based_table_reader.cc
|
||||
table/block_based/block_builder.cc
|
||||
table/block_based/block_cache.cc
|
||||
table/block_based/block_prefetcher.cc
|
||||
table/block_based/block_prefix_index.cc
|
||||
table/block_based/data_block_hash_index.cc
|
||||
@@ -807,7 +797,6 @@ set(SOURCES
|
||||
table/get_context.cc
|
||||
table/iterator.cc
|
||||
table/merging_iterator.cc
|
||||
table/compaction_merging_iterator.cc
|
||||
table/meta_blocks.cc
|
||||
table/persistent_cache_helper.cc
|
||||
table/plain/plain_table_bloom.cc
|
||||
@@ -840,8 +829,6 @@ set(SOURCES
|
||||
trace_replay/trace_record_result.cc
|
||||
trace_replay/trace_record.cc
|
||||
trace_replay/trace_replay.cc
|
||||
util/async_file_reader.cc
|
||||
util/cleanable.cc
|
||||
util/coding.cc
|
||||
util/compaction_job_stats_impl.cc
|
||||
util/comparator.cc
|
||||
@@ -849,7 +836,6 @@ set(SOURCES
|
||||
util/compression_context_cache.cc
|
||||
util/concurrent_task_limiter_impl.cc
|
||||
util/crc32c.cc
|
||||
util/data_structure.cc
|
||||
util/dynamic_bloom.cc
|
||||
util/hash.cc
|
||||
util/murmurhash.cc
|
||||
@@ -859,15 +845,11 @@ set(SOURCES
|
||||
util/slice.cc
|
||||
util/file_checksum_helper.cc
|
||||
util/status.cc
|
||||
util/stderr_logger.cc
|
||||
util/string_util.cc
|
||||
util/thread_local.cc
|
||||
util/threadpool_imp.cc
|
||||
util/udt_util.cc
|
||||
util/write_batch_util.cc
|
||||
util/xxhash.cc
|
||||
utilities/agg_merge/agg_merge.cc
|
||||
utilities/backup/backup_engine.cc
|
||||
utilities/backupable/backupable_db.cc
|
||||
utilities/blob_db/blob_compaction_filter.cc
|
||||
utilities/blob_db/blob_db.cc
|
||||
utilities/blob_db/blob_db_impl.cc
|
||||
@@ -959,12 +941,6 @@ if ( ROCKSDB_PLUGINS )
|
||||
plugin/${plugin}/${src}
|
||||
PROPERTIES COMPILE_FLAGS "${${plugin}_COMPILE_FLAGS}")
|
||||
endforeach()
|
||||
foreach (test ${${plugin}_TESTS})
|
||||
list(APPEND PLUGIN_TESTS plugin/${plugin}/${test})
|
||||
set_source_files_properties(
|
||||
plugin/${plugin}/${test}
|
||||
PROPERTIES COMPILE_FLAGS "${${plugin}_COMPILE_FLAGS}")
|
||||
endforeach()
|
||||
foreach (path ${${plugin}_INCLUDE_PATHS})
|
||||
include_directories(${path})
|
||||
endforeach()
|
||||
@@ -979,6 +955,12 @@ if ( ROCKSDB_PLUGINS )
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(HAVE_SSE42 AND NOT MSVC)
|
||||
set_source_files_properties(
|
||||
util/crc32c.cc
|
||||
PROPERTIES COMPILE_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64")
|
||||
list(APPEND SOURCES
|
||||
util/crc32c_ppc.c
|
||||
@@ -1016,24 +998,20 @@ else()
|
||||
env/io_posix.cc)
|
||||
endif()
|
||||
|
||||
if(USE_FOLLY_LITE)
|
||||
if(WITH_FOLLY_DISTRIBUTED_MUTEX)
|
||||
list(APPEND SOURCES
|
||||
third-party/folly/folly/container/detail/F14Table.cpp
|
||||
third-party/folly/folly/detail/Futex.cpp
|
||||
third-party/folly/folly/lang/SafeAssert.cpp
|
||||
third-party/folly/folly/lang/ToAscii.cpp
|
||||
third-party/folly/folly/ScopeGuard.cpp
|
||||
third-party/folly/folly/synchronization/AtomicNotification.cpp
|
||||
third-party/folly/folly/synchronization/DistributedMutex.cpp
|
||||
third-party/folly/folly/synchronization/ParkingLot.cpp)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third-party/folly)
|
||||
add_definitions(-DUSE_FOLLY -DFOLLY_NO_CONFIG)
|
||||
list(APPEND THIRDPARTY_LIBS glog)
|
||||
third-party/folly/folly/synchronization/ParkingLot.cpp
|
||||
third-party/folly/folly/synchronization/WaitOptions.cpp)
|
||||
endif()
|
||||
|
||||
set(ROCKSDB_STATIC_LIB rocksdb${ARTIFACT_SUFFIX})
|
||||
set(ROCKSDB_SHARED_LIB rocksdb-shared${ARTIFACT_SUFFIX})
|
||||
|
||||
option(ROCKSDB_BUILD_SHARED "Build shared versions of the RocksDB libraries" ON)
|
||||
|
||||
|
||||
if(WIN32)
|
||||
set(SYSTEM_LIBS ${SYSTEM_LIBS} shlwapi.lib rpcrt4.lib)
|
||||
@@ -1041,75 +1019,12 @@ else()
|
||||
set(SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
set(ROCKSDB_PLUGIN_EXTERNS "")
|
||||
set(ROCKSDB_PLUGIN_BUILTINS "")
|
||||
message(STATUS "ROCKSDB PLUGINS TO BUILD ${ROCKSDB_PLUGINS}")
|
||||
foreach(PLUGIN IN LISTS PLUGINS)
|
||||
set(PLUGIN_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/plugin/${PLUGIN}/")
|
||||
message(STATUS "PLUGIN ${PLUGIN} including rocksb plugin ${PLUGIN_ROOT}")
|
||||
set(PLUGINMKFILE "${PLUGIN_ROOT}${PLUGIN}.mk")
|
||||
if (NOT EXISTS ${PLUGINMKFILE})
|
||||
message(FATAL_ERROR "PLUGIN ${PLUGIN} Missing plugin makefile: ${PLUGINMKFILE}")
|
||||
endif()
|
||||
file(READ ${PLUGINMKFILE} PLUGINMK)
|
||||
|
||||
string(REGEX MATCH "SOURCES = ([^\n]*)" FOO ${PLUGINMK})
|
||||
set(MK_SOURCES ${CMAKE_MATCH_1})
|
||||
separate_arguments(MK_SOURCES)
|
||||
foreach(MK_FILE IN LISTS MK_SOURCES)
|
||||
list(APPEND SOURCES "${PLUGIN_ROOT}${MK_FILE}")
|
||||
message(STATUS "PLUGIN ${PLUGIN} Appending ${PLUGIN_ROOT}${MK_FILE} to SOURCES")
|
||||
endforeach()
|
||||
|
||||
string(REGEX MATCH "_FUNC = ([^\n]*)" FOO ${PLUGINMK})
|
||||
if (NOT ${CMAKE_MATCH_1} STREQUAL "")
|
||||
string(APPEND ROCKSDB_PLUGIN_BUILTINS "{\"${PLUGIN}\", " ${CMAKE_MATCH_1} "},")
|
||||
string(APPEND ROCKSDB_PLUGIN_EXTERNS "int " ${CMAKE_MATCH_1} "(ROCKSDB_NAMESPACE::ObjectLibrary&, const std::string&); ")
|
||||
endif()
|
||||
|
||||
string(REGEX MATCH "_LIBS = ([^\n]*)" FOO ${PLUGINMK})
|
||||
separate_arguments(CMAKE_MATCH_1)
|
||||
foreach(MK_LIB IN LISTS CMAKE_MATCH_1)
|
||||
list(APPEND THIRDPARTY_LIBS "${MK_LIB}")
|
||||
endforeach()
|
||||
message(STATUS "PLUGIN ${PLUGIN} THIRDPARTY_LIBS=${THIRDPARTY_LIBS}")
|
||||
|
||||
#TODO: We need to set any compile/link-time flags and add any link libraries
|
||||
endforeach()
|
||||
|
||||
string(TIMESTAMP TS "%Y-%m-%d %H:%M:%S" UTC)
|
||||
set(BUILD_DATE "${TS}" CACHE STRING "the time we first built rocksdb")
|
||||
|
||||
find_package(Git)
|
||||
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_SHA COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD )
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE GIT_MOD COMMAND "${GIT_EXECUTABLE}" diff-index HEAD --quiet)
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_DATE COMMAND "${GIT_EXECUTABLE}" log -1 --date=format:"%Y-%m-%d %T" --format="%ad")
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_TAG RESULT_VARIABLE rv COMMAND "${GIT_EXECUTABLE}" symbolic-ref -q --short HEAD OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (rv AND NOT rv EQUAL 0)
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_TAG COMMAND "${GIT_EXECUTABLE}" describe --tags --exact-match OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
else()
|
||||
set(GIT_SHA 0)
|
||||
set(GIT_MOD 1)
|
||||
endif()
|
||||
string(REGEX REPLACE "[^0-9a-fA-F]+" "" GIT_SHA "${GIT_SHA}")
|
||||
string(REGEX REPLACE "[^0-9: /-]+" "" GIT_DATE "${GIT_DATE}")
|
||||
|
||||
set(BUILD_VERSION_CC ${CMAKE_BINARY_DIR}/build_version.cc)
|
||||
configure_file(util/build_version.cc.in ${BUILD_VERSION_CC} @ONLY)
|
||||
|
||||
add_library(${ROCKSDB_STATIC_LIB} STATIC ${SOURCES} ${BUILD_VERSION_CC})
|
||||
target_include_directories(${ROCKSDB_STATIC_LIB} PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB} PRIVATE
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(ROCKSDB_BUILD_SHARED)
|
||||
add_library(${ROCKSDB_SHARED_LIB} SHARED ${SOURCES} ${BUILD_VERSION_CC})
|
||||
target_include_directories(${ROCKSDB_SHARED_LIB} PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
|
||||
target_link_libraries(${ROCKSDB_SHARED_LIB} PRIVATE
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
@@ -1183,20 +1098,8 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
configure_file(
|
||||
${PROJECT_NAME}.pc.in
|
||||
${PROJECT_NAME}.pc
|
||||
@ONLY
|
||||
)
|
||||
|
||||
install(DIRECTORY include/rocksdb COMPONENT devel DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
|
||||
foreach (plugin ${PLUGINS})
|
||||
foreach (header ${${plugin}_HEADERS})
|
||||
install(FILES plugin/${plugin}/${header} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rocksdb/plugin/${plugin})
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
install(DIRECTORY "${PROJECT_SOURCE_DIR}/cmake/modules" COMPONENT devel DESTINATION ${package_config_destination})
|
||||
|
||||
install(
|
||||
@@ -1233,13 +1136,6 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
COMPONENT devel
|
||||
DESTINATION ${package_config_destination}
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
|
||||
COMPONENT devel
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
)
|
||||
endif()
|
||||
|
||||
option(WITH_ALL_TESTS "Build all test, rather than a small subset" ON)
|
||||
@@ -1248,7 +1144,6 @@ if(WITH_TESTS OR WITH_BENCHMARK_TOOLS)
|
||||
add_subdirectory(third-party/gtest-1.8.1/fused-src/gtest)
|
||||
add_library(testharness STATIC
|
||||
test_util/mock_time_env.cc
|
||||
test_util/secondary_cache_test_util.cc
|
||||
test_util/testharness.cc)
|
||||
target_link_libraries(testharness gtest)
|
||||
endif()
|
||||
@@ -1262,9 +1157,8 @@ if(WITH_TESTS)
|
||||
list(APPEND TESTS
|
||||
cache/cache_reservation_manager_test.cc
|
||||
cache/cache_test.cc
|
||||
cache/compressed_secondary_cache_test.cc
|
||||
cache/lru_cache_test.cc
|
||||
cache/tiered_secondary_cache_test.cc
|
||||
cache/lru_secondary_cache_test.cc
|
||||
db/blob/blob_counting_iterator_test.cc
|
||||
db/blob/blob_file_addition_test.cc
|
||||
db/blob/blob_file_builder_test.cc
|
||||
@@ -1272,7 +1166,6 @@ if(WITH_TESTS)
|
||||
db/blob/blob_file_garbage_test.cc
|
||||
db/blob/blob_file_reader_test.cc
|
||||
db/blob/blob_garbage_meter_test.cc
|
||||
db/blob/blob_source_test.cc
|
||||
db/blob/db_blob_basic_test.cc
|
||||
db/blob/db_blob_compaction_test.cc
|
||||
db/blob/db_blob_corruption_test.cc
|
||||
@@ -1285,17 +1178,14 @@ if(WITH_TESTS)
|
||||
db/compaction/compaction_iterator_test.cc
|
||||
db/compaction/compaction_picker_test.cc
|
||||
db/compaction/compaction_service_test.cc
|
||||
db/compaction/tiered_compaction_test.cc
|
||||
db/comparator_db_test.cc
|
||||
db/corruption_test.cc
|
||||
db/cuckoo_table_db_test.cc
|
||||
db/db_readonly_with_timestamp_test.cc
|
||||
db/db_with_timestamp_basic_test.cc
|
||||
db/db_block_cache_test.cc
|
||||
db/db_bloom_filter_test.cc
|
||||
db/db_compaction_filter_test.cc
|
||||
db/db_compaction_test.cc
|
||||
db/db_clip_test.cc
|
||||
db/db_dynamic_level_test.cc
|
||||
db/db_encryption_test.cc
|
||||
db/db_flush_test.cc
|
||||
@@ -1345,9 +1235,8 @@ if(WITH_TESTS)
|
||||
db/merge_test.cc
|
||||
db/options_file_test.cc
|
||||
db/perf_context_test.cc
|
||||
db/periodic_task_scheduler_test.cc
|
||||
db/periodic_work_scheduler_test.cc
|
||||
db/plain_table_db_test.cc
|
||||
db/seqno_time_test.cc
|
||||
db/prefix_test.cc
|
||||
db/range_del_aggregator_test.cc
|
||||
db/range_tombstone_fragmenter_test.cc
|
||||
@@ -1358,9 +1247,6 @@ if(WITH_TESTS)
|
||||
db/version_set_test.cc
|
||||
db/wal_manager_test.cc
|
||||
db/wal_edit_test.cc
|
||||
db/wide/db_wide_basic_test.cc
|
||||
db/wide/wide_column_serialization_test.cc
|
||||
db/wide/wide_columns_helper_test.cc
|
||||
db/write_batch_test.cc
|
||||
db/write_callback_test.cc
|
||||
db/write_controller_test.cc
|
||||
@@ -1386,6 +1272,7 @@ if(WITH_TESTS)
|
||||
options/customizable_test.cc
|
||||
options/options_settable_test.cc
|
||||
options/options_test.cc
|
||||
table/block_based/block_based_filter_block_test.cc
|
||||
table/block_based/block_based_table_reader_test.cc
|
||||
table/block_based/block_test.cc
|
||||
table/block_based/data_block_hash_index_test.cc
|
||||
@@ -1423,15 +1310,12 @@ if(WITH_TESTS)
|
||||
util/ribbon_test.cc
|
||||
util/slice_test.cc
|
||||
util/slice_transform_test.cc
|
||||
util/string_util_test.cc
|
||||
util/timer_queue_test.cc
|
||||
util/timer_test.cc
|
||||
util/thread_list_test.cc
|
||||
util/thread_local_test.cc
|
||||
util/udt_util_test.cc
|
||||
util/work_queue_test.cc
|
||||
utilities/agg_merge/agg_merge_test.cc
|
||||
utilities/backup/backup_engine_test.cc
|
||||
utilities/backupable/backupable_db_test.cc
|
||||
utilities/blob_db/blob_db_test.cc
|
||||
utilities/cassandra/cassandra_functional_test.cc
|
||||
utilities/cassandra/cassandra_format_test.cc
|
||||
@@ -1456,27 +1340,27 @@ if(WITH_TESTS)
|
||||
utilities/transactions/write_prepared_transaction_test.cc
|
||||
utilities/transactions/write_unprepared_transaction_test.cc
|
||||
utilities/transactions/lock/range/range_locking_test.cc
|
||||
utilities/transactions/timestamped_snapshot_test.cc
|
||||
utilities/ttl/ttl_test.cc
|
||||
utilities/util_merge_operators_test.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_test.cc
|
||||
${PLUGIN_TESTS}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_FOLLY_DISTRIBUTED_MUTEX)
|
||||
list(APPEND TESTS third-party/folly/folly/synchronization/test/DistributedMutexTest.cpp)
|
||||
endif()
|
||||
|
||||
set(TESTUTIL_SOURCE
|
||||
db/db_test_util.cc
|
||||
db/db_with_timestamp_test_util.cc
|
||||
monitoring/thread_status_updater_debug.cc
|
||||
table/mock_table.cc
|
||||
utilities/agg_merge/test_agg_merge.cc
|
||||
utilities/cassandra/test_utils.cc
|
||||
)
|
||||
enable_testing()
|
||||
add_custom_target(rocksdb_check COMMAND ${CMAKE_CTEST_COMMAND})
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})
|
||||
set(TESTUTILLIB testutillib${ARTIFACT_SUFFIX})
|
||||
add_library(${TESTUTILLIB} STATIC ${TESTUTIL_SOURCE})
|
||||
target_link_libraries(${TESTUTILLIB} ${ROCKSDB_LIB} ${FOLLY_LIBS})
|
||||
target_link_libraries(${TESTUTILLIB} ${ROCKSDB_LIB})
|
||||
if(MSVC)
|
||||
set_target_properties(${TESTUTILLIB} PROPERTIES COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/testutillib${ARTIFACT_SUFFIX}.pdb")
|
||||
endif()
|
||||
@@ -1498,7 +1382,7 @@ if(WITH_TESTS)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} testharness gtest ${THIRDPARTY_LIBS} ${ROCKSDB_LIB})
|
||||
if(NOT "${exename}" MATCHES "db_sanity_test")
|
||||
gtest_discover_tests(${exename} DISCOVERY_TIMEOUT 120)
|
||||
add_dependencies(rocksdb_check ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${exename}${ARTIFACT_SUFFIX})
|
||||
endif()
|
||||
endforeach(sourcefile ${TESTS})
|
||||
|
||||
@@ -1518,7 +1402,7 @@ if(WITH_TESTS)
|
||||
add_executable(c_test db/c_test.c)
|
||||
target_link_libraries(c_test ${ROCKSDB_LIB_FOR_C} testharness)
|
||||
add_test(NAME c_test COMMAND c_test${ARTIFACT_SUFFIX})
|
||||
add_dependencies(rocksdb_check c_test)
|
||||
add_dependencies(check c_test)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -1534,46 +1418,32 @@ if(WITH_BENCHMARK_TOOLS)
|
||||
cache/cache_bench.cc
|
||||
cache/cache_bench_tool.cc)
|
||||
target_link_libraries(cache_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB} ${FOLLY_LIBS})
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB})
|
||||
|
||||
add_executable(memtablerep_bench${ARTIFACT_SUFFIX}
|
||||
memtable/memtablerep_bench.cc)
|
||||
target_link_libraries(memtablerep_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB} ${FOLLY_LIBS})
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB})
|
||||
|
||||
add_executable(range_del_aggregator_bench${ARTIFACT_SUFFIX}
|
||||
db/range_del_aggregator_bench.cc)
|
||||
target_link_libraries(range_del_aggregator_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB} ${FOLLY_LIBS})
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB})
|
||||
|
||||
add_executable(table_reader_bench${ARTIFACT_SUFFIX}
|
||||
table/table_reader_bench.cc)
|
||||
target_link_libraries(table_reader_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} testharness ${GFLAGS_LIB} ${FOLLY_LIBS})
|
||||
${ROCKSDB_LIB} testharness ${GFLAGS_LIB})
|
||||
|
||||
add_executable(filter_bench${ARTIFACT_SUFFIX}
|
||||
util/filter_bench.cc)
|
||||
target_link_libraries(filter_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB} ${FOLLY_LIBS})
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB})
|
||||
|
||||
add_executable(hash_table_bench${ARTIFACT_SUFFIX}
|
||||
utilities/persistent_cache/hash_table_bench.cc)
|
||||
target_link_libraries(hash_table_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB} ${FOLLY_LIBS})
|
||||
endif()
|
||||
|
||||
option(WITH_TRACE_TOOLS "build with trace tools" ON)
|
||||
if(WITH_TRACE_TOOLS)
|
||||
add_executable(block_cache_trace_analyzer${ARTIFACT_SUFFIX}
|
||||
tools/block_cache_analyzer/block_cache_trace_analyzer_tool.cc)
|
||||
target_link_libraries(block_cache_trace_analyzer${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB} ${FOLLY_LIBS})
|
||||
|
||||
add_executable(trace_analyzer${ARTIFACT_SUFFIX}
|
||||
tools/trace_analyzer.cc)
|
||||
target_link_libraries(trace_analyzer${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB} ${FOLLY_LIBS})
|
||||
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB})
|
||||
endif()
|
||||
|
||||
if(WITH_CORE_TOOLS OR WITH_TOOLS)
|
||||
|
||||
+1
-732
@@ -1,735 +1,4 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 9.0.1 (04/11/2024)
|
||||
### Bug Fixes
|
||||
* Fixed CMake Javadoc and source jar builds
|
||||
* Fixed Java `SstFileMetaData` to prevent throwing `java.lang.NoSuchMethodError`
|
||||
|
||||
## 9.0.0 (02/16/2024)
|
||||
### New Features
|
||||
* Provide support for FSBuffer for point lookups. Also added support for scans and compactions that don't go through prefetching.
|
||||
* *Make `SstFileWriter` create SST files without persisting user defined timestamps when the `Option.persist_user_defined_timestamps` flag is set to false.
|
||||
* Add support for user-defined timestamps in APIs `DeleteFilesInRanges` and `GetPropertiesOfTablesInRange`.
|
||||
* Mark wal\_compression feature as production-ready. Currently only compatible with ZSTD compression.
|
||||
|
||||
### Public API Changes
|
||||
* Allow setting Stderr logger via C API
|
||||
* Declare one Get and one MultiGet variant as pure virtual, and make all the other variants non-overridable. The methods required to be implemented by derived classes of DB allow returning timestamps. It is up to the implementation to check and return an error if timestamps are not supported. The non-batched MultiGet APIs are reimplemented in terms of batched MultiGet, so callers might see a performance improvement.
|
||||
* Exposed mode option to Rate Limiter via c api.
|
||||
* Removed deprecated option `access_hint_on_compaction_start`
|
||||
* Removed deprecated option `ColumnFamilyOptions::check_flush_compaction_key_order`
|
||||
* *Remove the default `WritableFile::GetFileSize` and `FSWritableFile::GetFileSize` implementation that returns 0 and make it pure virtual, so that subclasses are enforced to explicitly provide an implementation.
|
||||
* Removed deprecated option `ColumnFamilyOptions::level_compaction_dynamic_file_size`
|
||||
* *Removed tickers with typos "rocksdb.error.handler.bg.errro.count", "rocksdb.error.handler.bg.io.errro.count", "rocksdb.error.handler.bg.retryable.io.errro.count".
|
||||
* Remove the force mode for `EnableFileDeletions` API because it is unsafe with no known legitimate use.
|
||||
* Removed deprecated option `ColumnFamilyOptions::ignore_max_compaction_bytes_for_input`
|
||||
* `sst_dump --command=check` now compares the number of records in a table with `num_entries` in table property, and reports corruption if there is a mismatch. API `SstFileDumper::ReadSequential()` is updated to optionally do this verification. (#12322)
|
||||
|
||||
### Behavior Changes
|
||||
* format\_version=6 is the new default setting in BlockBasedTableOptions, for more robust data integrity checking. DBs and SST files written with this setting cannot be read by RocksDB versions before 8.6.0.
|
||||
* Compactions can be scheduled in parallel in an additional scenario: multiple files are marked for compaction within a single column family
|
||||
* For leveled compaction, RocksDB will try to do intra-L0 compaction if the total L0 size is small compared to Lbase (#12214). Users with atomic_flush=true are more likely to see the impact of this change.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a data race in `DBImpl::RenameTempFileToOptionsFile`.
|
||||
* Fix some perf context statistics error in write steps. which include missing write_memtable_time in unordered_write. missing write_memtable_time in PipelineWrite when Writer stat is STATE_PARALLEL_MEMTABLE_WRITER. missing write_delay_time when calling DelayWrite in WriteImplWALOnly function.
|
||||
* Fixed a bug that can, under rare circumstances, cause MultiGet to return an incorrect result for a duplicate key in a MultiGet batch.
|
||||
* Fix a bug where older data of an ingested key can be returned for read when universal compaction is used
|
||||
|
||||
## 8.11.0 (01/19/2024)
|
||||
### New Features
|
||||
* Add new statistics: `rocksdb.sst.write.micros` measures time of each write to SST file; `rocksdb.file.write.{flush|compaction|db.open}.micros` measure time of each write to SST table (currently only block-based table format) and blob file for flush, compaction and db open.
|
||||
|
||||
### Public API Changes
|
||||
* Added another enumerator `kVerify` to enum class `FileOperationType` in listener.h. Update your `switch` statements as needed.
|
||||
* Add CompressionOptions to the CompressedSecondaryCacheOptions structure to allow users to specify library specific options when creating the compressed secondary cache.
|
||||
* Deprecated several options: `level_compaction_dynamic_file_size`, `ignore_max_compaction_bytes_for_input`, `check_flush_compaction_key_order`, `flush_verify_memtable_count`, `compaction_verify_record_count`, `fail_if_options_file_error`, and `enforce_single_del_contracts`
|
||||
* Exposed options ttl via c api.
|
||||
|
||||
### Behavior Changes
|
||||
* `rocksdb.blobdb.blob.file.write.micros` expands to also measure time writing the header and footer. Therefore the COUNT may be higher and values may be smaller than before. For stacked BlobDB, it no longer measures the time of explictly flushing blob file.
|
||||
* Files will be compacted to the next level if the data age exceeds periodic_compaction_seconds except for the last level.
|
||||
* Reduced the compaction debt ratio trigger for scheduling parallel compactions
|
||||
* For leveled compaction with default compaction pri (kMinOverlappingRatio), files marked for compaction will be prioritized over files not marked when picking a file from a level for compaction.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix bug in auto_readahead_size that combined with IndexType::kBinarySearchWithFirstKey + fails or iterator lands at a wrong key
|
||||
* Fixed some cases in which DB file corruption was detected but ignored on creating a backup with BackupEngine.
|
||||
* Fix bugs where `rocksdb.blobdb.blob.file.synced` includes blob files failed to get synced and `rocksdb.blobdb.blob.file.bytes.written` includes blob bytes failed to get written.
|
||||
* Fixed a possible memory leak or crash on a failure (such as I/O error) in automatic atomic flush of multiple column families.
|
||||
* Fixed some cases of in-memory data corruption using mmap reads with `BackupEngine`, `sst_dump`, or `ldb`.
|
||||
* Fixed issues with experimental `preclude_last_level_data_seconds` option that could interfere with expected data tiering.
|
||||
* Fixed the handling of the edge case when all existing blob files become unreferenced. Such files are now correctly deleted.
|
||||
|
||||
## 8.10.0 (12/15/2023)
|
||||
### New Features
|
||||
* Provide support for async_io to trim readahead_size by doing block cache lookup
|
||||
* Added initial wide-column support in `WriteBatchWithIndex`. This includes the `PutEntity` API and support for wide columns in the existing read APIs (`GetFromBatch`, `GetFromBatchAndDB`, `MultiGetFromBatchAndDB`, and `BaseDeltaIterator`).
|
||||
|
||||
### Public API Changes
|
||||
* Custom implementations of `TablePropertiesCollectorFactory` may now return a `nullptr` collector to decline processing a file, reducing callback overheads in such cases.
|
||||
|
||||
### Behavior Changes
|
||||
* Make ReadOptions.auto_readahead_size default true which does prefetching optimizations for forward scans if iterate_upper_bound and block_cache is also specified.
|
||||
* Compactions can be scheduled in parallel in an additional scenario: high compaction debt relative to the data size
|
||||
* HyperClockCache now has built-in protection against excessive CPU consumption under the extreme stress condition of no (or very few) evictable cache entries, which can slightly increase memory usage such conditions. New option `HyperClockCacheOptions::eviction_effort_cap` controls the space-time trade-off of the response. The default should be generally well-balanced, with no measurable affect on normal operation.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a corner case with auto_readahead_size where Prev Operation returns NOT SUPPORTED error when scans direction is changed from forward to backward.
|
||||
* Avoid destroying the periodic task scheduler's default timer in order to prevent static destruction order issues.
|
||||
* Fix double counting of BYTES_WRITTEN ticker when doing writes with transactions.
|
||||
* Fix a WRITE_STALL counter that was reporting wrong value in few cases.
|
||||
* A lookup by MultiGet in a TieredCache that goes to the local flash cache and finishes with very low latency, i.e before the subsequent call to WaitAll, is ignored, resulting in a false negative and a memory leak.
|
||||
|
||||
### Performance Improvements
|
||||
* Java API extensions to improve consistency and completeness of APIs
|
||||
1 Extended `RocksDB.get([ColumnFamilyHandle columnFamilyHandle,] ReadOptions opt, ByteBuffer key, ByteBuffer value)` which now accepts indirect buffer parameters as well as direct buffer parameters
|
||||
2 Extended `RocksDB.put( [ColumnFamilyHandle columnFamilyHandle,] WriteOptions writeOpts, final ByteBuffer key, final ByteBuffer value)` which now accepts indirect buffer parameters as well as direct buffer parameters
|
||||
3 Added `RocksDB.merge([ColumnFamilyHandle columnFamilyHandle,] WriteOptions writeOptions, ByteBuffer key, ByteBuffer value)` methods with the same parameter options as `put(...)` - direct and indirect buffers are supported
|
||||
4 Added `RocksIterator.key( byte[] key [, int offset, int len])` methods which retrieve the iterator key into the supplied buffer
|
||||
5 Added `RocksIterator.value( byte[] value [, int offset, int len])` methods which retrieve the iterator value into the supplied buffer
|
||||
6 Deprecated `get(final ColumnFamilyHandle columnFamilyHandle, final ReadOptions readOptions, byte[])` in favour of `get(final ReadOptions readOptions, final ColumnFamilyHandle columnFamilyHandle, byte[])` which has consistent parameter ordering with other methods in the same class
|
||||
7 Added `Transaction.get( ReadOptions opt, [ColumnFamilyHandle columnFamilyHandle, ] byte[] key, byte[] value)` methods which retrieve the requested value into the supplied buffer
|
||||
8 Added `Transaction.get( ReadOptions opt, [ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value)` methods which retrieve the requested value into the supplied buffer
|
||||
9 Added `Transaction.getForUpdate( ReadOptions readOptions, [ColumnFamilyHandle columnFamilyHandle, ] byte[] key, byte[] value, boolean exclusive [, boolean doValidate])` methods which retrieve the requested value into the supplied buffer
|
||||
10 Added `Transaction.getForUpdate( ReadOptions readOptions, [ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value, boolean exclusive [, boolean doValidate])` methods which retrieve the requested value into the supplied buffer
|
||||
11 Added `Transaction.getIterator()` method as a convenience which defaults the `ReadOptions` value supplied to existing `Transaction.iterator()` methods. This mirrors the existing `RocksDB.iterator()` method.
|
||||
12 Added `Transaction.put([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value [, boolean assumeTracked])` methods which supply the key, and the value to be written in a `ByteBuffer` parameter
|
||||
13 Added `Transaction.merge([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value [, boolean assumeTracked])` methods which supply the key, and the value to be written/merged in a `ByteBuffer` parameter
|
||||
14 Added `Transaction.mergeUntracked([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value)` methods which supply the key, and the value to be written/merged in a `ByteBuffer` parameter
|
||||
|
||||
|
||||
## 8.9.0 (11/17/2023)
|
||||
### New Features
|
||||
* Add GetEntity() and PutEntity() API implementation for Attribute Group support. Through the use of Column Families, AttributeGroup enables users to logically group wide-column entities.
|
||||
|
||||
### Public API Changes
|
||||
* Added rocksdb_ratelimiter_create_auto_tuned API to create an auto-tuned GenericRateLimiter.
|
||||
* Added clipColumnFamily() to the Java API to clip the entries in the CF according to the range [begin_key, end_key).
|
||||
* Make the `EnableFileDeletion` API not default to force enabling. For users that rely on this default behavior and still
|
||||
want to continue to use force enabling, they need to explicitly pass a `true` to `EnableFileDeletion`.
|
||||
* Add new Cache APIs GetSecondaryCacheCapacity() and GetSecondaryCachePinnedUsage() to return the configured capacity, and cache reservation charged to the secondary cache.
|
||||
|
||||
### Behavior Changes
|
||||
* During off-peak hours defined by `daily_offpeak_time_utc`, the compaction picker will select a larger number of files for periodic compaction. This selection will include files that are projected to expire by the next off-peak start time, ensuring that these files are not chosen for periodic compaction outside of off-peak hours.
|
||||
* If an error occurs when writing to a trace file after `DB::StartTrace()`, the subsequent trace writes are skipped to avoid writing to a file that has previously seen error. In this case, `DB::EndTrace()` will also return a non-ok status with info about the error occured previously in its status message.
|
||||
* Deleting stale files upon recovery are delegated to SstFileManger if available so they can be rate limited.
|
||||
* Make RocksDB only call `TablePropertiesCollector::Finish()` once.
|
||||
* When `WAL_ttl_seconds > 0`, we now process archived WALs for deletion at least every `WAL_ttl_seconds / 2` seconds. Previously it could be less frequent in case of small `WAL_ttl_seconds` values when size-based expiration (`WAL_size_limit_MB > 0 `) was simultaneously enabled.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a crash or assertion failure bug in experimental new HyperClockCache variant, especially when running with a SecondaryCache.
|
||||
* Fix a race between flush error recovery and db destruction that can lead to db crashing.
|
||||
* Fixed some bugs in the index builder/reader path for user-defined timestamps in Memtable only feature.
|
||||
|
||||
## 8.8.0 (10/23/2023)
|
||||
### New Features
|
||||
* Introduce AttributeGroup by adding the first AttributeGroup support API, MultiGetEntity(). Through the use of Column Families, AttributeGroup enables users to logically group wide-column entities. More APIs to support AttributeGroup will come soon, including GetEntity, PutEntity, and others.
|
||||
* Added new tickers `rocksdb.fifo.{max.size|ttl}.compactions` to count FIFO compactions that drop files for different reasons
|
||||
* Add an experimental offpeak duration awareness by setting `DBOptions::daily_offpeak_time_utc` in "HH:mm-HH:mm" format. This information will be used for resource optimization in the future
|
||||
* Users can now change the max bytes granted in a single refill period (i.e, burst) during runtime by `SetSingleBurstBytes()` for RocksDB rate limiter
|
||||
|
||||
### Public API Changes
|
||||
* The default value of `DBOptions::fail_if_options_file_error` changed from `false` to `true`. Operations that set in-memory options (e.g., `DB::Open*()`, `DB::SetOptions()`, `DB::CreateColumnFamily*()`, and `DB::DropColumnFamily()`) but fail to persist the change will now return a non-OK `Status` by default.
|
||||
|
||||
### Behavior Changes
|
||||
* For non direct IO, eliminate the file system prefetching attempt for compaction read when `Options::compaction_readahead_size` is 0
|
||||
* During a write stop, writes now block on in-progress recovery attempts
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in auto_readahead_size where first_internal_key of index blocks wasn't copied properly resulting in corruption error when first_internal_key was used for comparison.
|
||||
* Fixed a bug where compaction read under non direct IO still falls back to RocksDB internal prefetching after file system's prefetching returns non-OK status other than `Status::NotSupported()`
|
||||
* Add bounds check in WBWIIteratorImpl and make BaseDeltaIterator, WriteUnpreparedTxn and WritePreparedTxn respect the upper bound and lower bound in ReadOption. See 11680.
|
||||
* Fixed the handling of wide-column base values in the `max_successive_merges` logic.
|
||||
* Fixed a rare race bug involving a concurrent combination of Create/DropColumnFamily and/or Set(DB)Options that could lead to inconsistency between (a) the DB's reported options state, (b) the DB options in effect, and (c) the latest persisted OPTIONS file.
|
||||
* Fixed a possible underflow when computing the compressed secondary cache share of memory reservations while updating the compressed secondary to total block cache ratio.
|
||||
|
||||
### Performance Improvements
|
||||
* Improved the I/O efficiency of DB::Open a new DB with `create_missing_column_families=true` and many column families.
|
||||
|
||||
## 8.7.0 (09/22/2023)
|
||||
### New Features
|
||||
* Added an experimental new "automatic" variant of HyperClockCache that does not require a prior estimate of the average size of cache entries. This variant is activated when HyperClockCacheOptions::estimated\_entry\_charge = 0 and has essentially the same concurrency benefits as the existing HyperClockCache.
|
||||
* Add a new statistic `COMPACTION_CPU_TOTAL_TIME` that records cumulative compaction cpu time. This ticker is updated regularly while a compaction is running.
|
||||
* Add `GetEntity()` API for ReadOnly DB and Secondary DB.
|
||||
* Add a new iterator API `Iterator::Refresh(const Snapshot *)` that allows iterator to be refreshed while using the input snapshot to read.
|
||||
* Added a new read option `merge_operand_count_threshold`. When the number of merge operands applied during a successful point lookup exceeds this threshold, the query will return a special OK status with a new subcode `kMergeOperandThresholdExceeded`. Applications might use this signal to take action to reduce the number of merge operands for the affected key(s), for example by running a compaction.
|
||||
* For `NewRibbonFilterPolicy()`, made the `bloom_before_level` option mutable through the Configurable interface and the SetOptions API, allowing dynamic switching between all-Bloom and all-Ribbon configurations, and configurations in between. See comments on `NewRibbonFilterPolicy()`
|
||||
* RocksDB now allows the block cache to be stacked on top of a compressed secondary cache and a non-volatile secondary cache, thus creating a three-tier cache. To set it up, use the `NewTieredCache()` API in rocksdb/cache.h..
|
||||
* Added a new wide-column aware full merge API called `FullMergeV3` to `MergeOperator`. `FullMergeV3` supports wide columns both as base value and merge result, which enables the application to perform more general transformations during merges. For backward compatibility, the default implementation implements the earlier logic of applying the merge operation to the default column of any wide-column entities. Specifically, if there is no base value or the base value is a plain key-value, the default implementation falls back to `FullMergeV2`. If the base value is a wide-column entity, the default implementation invokes `FullMergeV2` to perform the merge on the default column, and leaves any other columns unchanged.
|
||||
* Add wide column support to ldb commands (scan, dump, idump, dump_wal) and sst_dump tool's scan command
|
||||
|
||||
### Public API Changes
|
||||
* Expose more information about input files used in table creation (if any) in `CompactionFilter::Context`. See `CompactionFilter::Context::input_start_level`,`CompactionFilter::Context::input_table_properties` for more.
|
||||
* `Options::compaction_readahead_size` 's default value is changed from 0 to 2MB.
|
||||
* When using LZ4 compression, the `acceleration` parameter is configurable by setting the negated value in `CompressionOptions::level`. For example, `CompressionOptions::level=-10` will set `acceleration=10`
|
||||
* The `NewTieredCache` API has been changed to take the total cache capacity (inclusive of both the primary and the compressed secondary cache) and the ratio of total capacity to allocate to the compressed cache. These are specified in `TieredCacheOptions`. Any capacity specified in `LRUCacheOptions`, `HyperClockCacheOptions` and `CompressedSecondaryCacheOptions` is ignored. A new API, `UpdateTieredCache` is provided to dynamically update the total capacity, ratio of compressed cache, and admission policy.
|
||||
* The `NewTieredVolatileCache()` API in rocksdb/cache.h has been renamed to `NewTieredCache()`.
|
||||
|
||||
### Behavior Changes
|
||||
* Compaction read performance will regress when `Options::compaction_readahead_size` is explicitly set to 0
|
||||
* Universal size amp compaction will conditionally exclude some of the newest L0 files when selecting input with a small negative impact to size amp. This is to prevent a large number of L0 files from being locked by a size amp compaction, potentially leading to write stop with a few more flushes.
|
||||
* Change ldb scan command delimiter from ':' to '==>'.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug where if there is an error reading from offset 0 of a file from L1+ and that the file is not the first file in the sorted run, data can be lost in compaction and read/scan can return incorrect results.
|
||||
* Fix a bug where iterator may return incorrect result for DeleteRange() users if there was an error reading from a file.
|
||||
* Fix a bug with atomic_flush=true that can cause DB to stuck after a flush fails (#11872).
|
||||
* Fix a bug where RocksDB (with atomic_flush=false) can delete output SST files of pending flushes when a previous concurrent flush fails (#11865). This can result in DB entering read-only state with error message like `IO error: No such file or directory: While open a file for random read: /tmp/rocksdbtest-501/db_flush_test_87732_4230653031040984171/000013.sst`.
|
||||
* Fix an assertion fault during seek with async_io when readahead trimming is enabled.
|
||||
* When the compressed secondary cache capacity is reduced to 0, it should be completely disabled. Before this fix, inserts and lookups would still go to the backing `LRUCache` before returning, thus incurring locking overhead. With this fix, inserts and lookups are no-ops and do not add any overhead.
|
||||
* Updating the tiered cache (cache allocated using NewTieredCache()) by calling SetCapacity() on it was not working properly. The initial creation would set the primary cache capacity to the combined primary and compressed secondary cache capacity. But SetCapacity() would just set the primary cache capacity. With this fix, the user always specifies the total budget and compressed secondary cache ratio on creation. Subsequently, SetCapacity() will distribute the new capacity across the two caches by the same ratio.
|
||||
* Fixed a bug in `MultiGet` for cleaning up SuperVersion acquired with locking db mutex.
|
||||
* Fix a bug where row cache can falsely return kNotFound even though row cache entry is hit.
|
||||
* Fixed a race condition in `GenericRateLimiter` that could cause it to stop granting requests
|
||||
* Fix a bug (Issue #10257) where DB can hang after write stall since no compaction is scheduled (#11764).
|
||||
* Add a fix for async_io where during seek, when reading a block for seeking a target key in a file without any readahead, the iterator aligned the read on a page boundary and reading more than necessary. This increased the storage read bandwidth usage.
|
||||
* Fix an issue in sst dump tool to handle bounds specified for data with user-defined timestamps.
|
||||
* When auto_readahead_size is enabled, update readahead upper bound during readahead trimming when reseek changes iterate_upper_bound dynamically.
|
||||
* Fixed a bug where `rocksdb.file.read.verify.file.checksums.micros` is not populated
|
||||
|
||||
### Performance Improvements
|
||||
* Added additional improvements in tuning readahead_size during Scans when auto_readahead_size is enabled. However it's not supported with Iterator::Prev operation and will return NotSupported error.
|
||||
* During async_io, the Seek happens in 2 phases. Phase 1 starts an asynchronous read on a block cache miss, and phase 2 waits for it to complete and finishes the seek. In both phases, it tries to lookup the block cache for the data block first before looking in the prefetch buffer. It's optimized by doing the block cache lookup only in the first phase that would save some CPU.
|
||||
|
||||
## 8.6.0 (08/18/2023)
|
||||
### New Features
|
||||
* Added enhanced data integrity checking on SST files with new format_version=6. Performance impact is very small or negligible. Previously if SST data was misplaced or re-arranged by the storage layer, it could pass block checksum with higher than 1 in 4 billion probability. With format_version=6, block checksums depend on what file they are in and location within the file. This way, misplaced SST data is no more likely to pass checksum verification than randomly corrupted data. Also in format_version=6, SST footers are checksum-protected.
|
||||
* Add a new feature to trim readahead_size during scans upto upper_bound when iterate_upper_bound is specified. It's enabled through ReadOptions.auto_readahead_size. Users must also specify ReadOptions.iterate_upper_bound.
|
||||
* RocksDB will compare the number of input keys to the number of keys processed after each compaction. Compaction will fail and report Corruption status if the verification fails. Option `compaction_verify_record_count` is introduced for this purpose and is enabled by default.
|
||||
* Add a CF option `bottommost_file_compaction_delay` to allow specifying the delay of bottommost level single-file compactions.
|
||||
* Add support to allow enabling / disabling user-defined timestamps feature for an existing column family in combination with the in-Memtable only feature.
|
||||
* Implement a new admission policy for the compressed secondary cache that admits blocks evicted from the primary cache with the hit bit set. This policy can be specified in TieredVolatileCacheOptions by setting the newly added adm_policy option.
|
||||
* Add a column family option `memtable_max_range_deletions` that limits the number of range deletions in a memtable. RocksDB will try to do an automatic flush after the limit is reached. (#11358)
|
||||
* Add PutEntity API in sst_file_writer
|
||||
* Add `timeout` in microsecond option to `WaitForCompactOptions` to allow timely termination of prolonged waiting in scenarios like recurring recoverable errors, such as out-of-space situations and continuous write streams that sustain ongoing flush and compactions
|
||||
* New statistics `rocksdb.file.read.{get|multiget|db.iterator|verify.checksum|verify.file.checksums}.micros` measure read time of block-based SST tables or blob files during db open, `Get()`, `MultiGet()`, using db iterator, `VerifyFileChecksums()` and `VerifyChecksum()`. They require stats level greater than `StatsLevel::kExceptDetailedTimers`.
|
||||
* Add close_db option to `WaitForCompactOptions` to call Close() after waiting is done.
|
||||
* Add a new compression option `CompressionOptions::checksum` for enabling ZSTD's checksum feature to detect corruption during decompression.
|
||||
|
||||
### Public API Changes
|
||||
* Mark `Options::access_hint_on_compaction_start` related APIs as deprecated. See #11631 for alternative behavior.
|
||||
|
||||
### Behavior Changes
|
||||
* Statistics `rocksdb.sst.read.micros` now includes time spent on multi read and async read into the file
|
||||
* For Universal Compaction users, periodic compaction (option `periodic_compaction_seconds`) will be set to 30 days by default if block based table is used.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in FileTTLBooster that can cause users with a large number of levels (more than 65) to see errors like "runtime error: shift exponent .. is too large.." (#11673).
|
||||
|
||||
## 8.5.0 (07/21/2023)
|
||||
### Public API Changes
|
||||
* Removed recently added APIs `GeneralCache` and `MakeSharedGeneralCache()` as our plan changed to stop exposing a general-purpose cache interface. The old forms of these APIs, `Cache` and `NewLRUCache()`, are still available, although general-purpose caching support will be dropped eventually.
|
||||
|
||||
### Behavior Changes
|
||||
* Option `periodic_compaction_seconds` no longer supports FIFO compaction: setting it has no effect on FIFO compactions. FIFO compaction users should only set option `ttl` instead.
|
||||
* Move prefetching responsibility to page cache for compaction read for non directIO use case
|
||||
|
||||
### Performance Improvements
|
||||
* In case of direct_io, if buffer passed by callee is already aligned, RandomAccessFileRead::Read will avoid realloacting a new buffer, reducing memcpy and use already passed aligned buffer.
|
||||
* Small efficiency improvement to HyperClockCache by reducing chance of compiler-generated heap allocations
|
||||
|
||||
### Bug Fixes
|
||||
* Fix use_after_free bug in async_io MultiReads when underlying FS enabled kFSBuffer. kFSBuffer is when underlying FS pass their own buffer instead of using RocksDB scratch in FSReadRequest. Right now it's an experimental feature.
|
||||
|
||||
## 8.4.0 (06/26/2023)
|
||||
### New Features
|
||||
* Add FSReadRequest::fs_scratch which is a data buffer allocated and provided by underlying FileSystem to RocksDB during reads, when FS wants to provide its own buffer with data instead of using RocksDB provided FSReadRequest::scratch. This can help in cpu optimization by avoiding copy from file system's buffer to RocksDB buffer. More details on how to use/enable it in file_system.h. Right now its supported only for MultiReads(async + sync) with non direct io.
|
||||
* Start logging non-zero user-defined timestamp sizes in WAL to signal user key format in subsequent records and use it during recovery. This change will break recovery from WAL files written by early versions that contain user-defined timestamps. The workaround is to ensure there are no WAL files to recover (i.e. by flushing before close) before upgrade.
|
||||
* Added new property "rocksdb.obsolete-sst-files-size-property" that reports the size of SST files that have become obsolete but have not yet been deleted or scheduled for deletion
|
||||
* Start to record the value of the flag `AdvancedColumnFamilyOptions.persist_user_defined_timestamps` in the Manifest and table properties for a SST file when it is created. And use the recorded flag when creating a table reader for the SST file. This flag is only explicitly record if it's false.
|
||||
* Add a new option OptimisticTransactionDBOptions::shared_lock_buckets that enables sharing mutexes for validating transactions between DB instances, for better balancing memory efficiency and validation contention across DB instances. Different column families and DBs also now use different hash seeds in this validation, so that the same set of key names will not contend across DBs or column families.
|
||||
* Add a new ticker `rocksdb.files.marked.trash.deleted` to track the number of trash files deleted by background thread from the trash queue.
|
||||
* Add an API NewTieredVolatileCache() in include/rocksdb/cache.h to allocate an instance of a block cache with a primary block cache tier and a compressed secondary cache tier. A cache of this type distributes memory reservations against the block cache, such as WriteBufferManager, table reader memory etc., proportionally across both the primary and compressed secondary cache.
|
||||
* Add `WaitForCompact()` to wait for all flush and compactions jobs to finish. Jobs to wait include the unscheduled (queued, but not scheduled yet).
|
||||
* Add `WriteBatch::Release()` that releases the batch's serialized data to the caller.
|
||||
|
||||
### Public API Changes
|
||||
* Add C API `rocksdb_options_add_compact_on_deletion_collector_factory_del_ratio`.
|
||||
* change the FileSystem::use_async_io() API to SupportedOps API in order to extend it to various operations supported by underlying FileSystem. Right now it contains FSSupportedOps::kAsyncIO and FSSupportedOps::kFSBuffer. More details about FSSupportedOps in filesystem.h
|
||||
* Add new tickers: `rocksdb.error.handler.bg.error.count`, `rocksdb.error.handler.bg.io.error.count`, `rocksdb.error.handler.bg.retryable.io.error.count` to replace the misspelled ones: `rocksdb.error.handler.bg.errro.count`, `rocksdb.error.handler.bg.io.errro.count`, `rocksdb.error.handler.bg.retryable.io.errro.count` ('error' instead of 'errro'). Users should switch to use the new tickers before 9.0 release as the misspelled old tickers will be completely removed then.
|
||||
* Overload the API CreateColumnFamilyWithImport() to support creating ColumnFamily by importing multiple ColumnFamilies It requires that CFs should not overlap in user key range.
|
||||
|
||||
### Behavior Changes
|
||||
* Change the default value for option `level_compaction_dynamic_level_bytes` to true. This affects users who use leveled compaction and do not set this option explicitly. These users may see additional background compactions following DB open. These compactions help to shape the LSM according to `level_compaction_dynamic_level_bytes` such that the size of each level Ln is approximately size of Ln-1 * `max_bytes_for_level_multiplier`. Turning on this option has other benefits too: see more detail in wiki: https://github.com/facebook/rocksdb/wiki/Leveled-Compaction#option-level_compaction_dynamic_level_bytes-and-levels-target-size and in option comment in advanced_options.h (#11525).
|
||||
* For Leveled Compaction users, `CompactRange()` will now always try to compact to the last non-empty level. (#11468)
|
||||
For Leveled Compaction users, `CompactRange()` with `bottommost_level_compaction = BottommostLevelCompaction::kIfHaveCompactionFilter` will behave similar to `kForceOptimized` in that it will skip files created during this manual compaction when compacting files in the bottommost level. (#11468)
|
||||
* RocksDB will try to drop range tombstones during non-bottommost compaction when it is safe to do so. (#11459)
|
||||
* When a DB is openend with `allow_ingest_behind=true` (currently only Universal compaction is supported), files in the last level, i.e. the ingested files, will not be included in any compaction. (#11489)
|
||||
* Statistics `rocksdb.sst.read.micros` scope is expanded to all SST reads except for file ingestion and column family import (some compaction reads were previously excluded).
|
||||
|
||||
### Bug Fixes
|
||||
* Reduced cases of illegally using Env::Default() during static destruction by never destroying the internal PosixEnv itself (except for builds checking for memory leaks). (#11538)
|
||||
* Fix extra prefetching during seek in async_io when BlockBasedTableOptions.num_file_reads_for_auto_readahead is 1 leading to extra reads than required.
|
||||
* Fix a bug where compactions that are qualified to be run as 2 subcompactions were only run as one subcompaction.
|
||||
* Fix a use-after-move bug in block.cc.
|
||||
|
||||
## 8.3.0 (05/19/2023)
|
||||
### New Features
|
||||
* Introduced a new option `block_protection_bytes_per_key`, which can be used to enable per key-value integrity protection for in-memory blocks in block cache (#11287).
|
||||
* Added `JemallocAllocatorOptions::num_arenas`. Setting `num_arenas > 1` may mitigate mutex contention in the allocator, particularly in scenarios where block allocations commonly bypass jemalloc tcache.
|
||||
* Improve the operational safety of publishing a DB or SST files to many hosts by using different block cache hash seeds on different hosts. The exact behavior is controlled by new option `ShardedCacheOptions::hash_seed`, which also documents the solved problem in more detail.
|
||||
* Introduced a new option `CompactionOptionsFIFO::file_temperature_age_thresholds` that allows FIFO compaction to compact files to different temperatures based on key age (#11428).
|
||||
* Added a new ticker stat to count how many times RocksDB detected a corruption while verifying a block checksum: `BLOCK_CHECKSUM_MISMATCH_COUNT`.
|
||||
* New statistics `rocksdb.file.read.db.open.micros` that measures read time of block-based SST tables or blob files during db open.
|
||||
* New statistics tickers for various iterator seek behaviors and relevant filtering, as \*`_LEVEL_SEEK_`\*. (#11460)
|
||||
|
||||
### Public API Changes
|
||||
* EXPERIMENTAL: Add new API `DB::ClipColumnFamily` to clip the key in CF to a certain range. It will physically deletes all keys outside the range including tombstones.
|
||||
* Add `MakeSharedCache()` construction functions to various cache Options objects, and deprecated the `NewWhateverCache()` functions with long parameter lists.
|
||||
* Changed the meaning of various Bloom filter stats (prefix vs. whole key), with iterator-related filtering only being tracked in the new \*`_LEVEL_SEEK_`\*. stats. (#11460)
|
||||
|
||||
### Behavior changes
|
||||
* For x86, CPU features are no longer detected at runtime nor in build scripts, but in source code using common preprocessor defines. This will likely unlock some small performance improvements on some newer hardware, but could hurt performance of the kCRC32c checksum, which is no longer the default, on some "portable" builds. See PR #11419 for details.
|
||||
|
||||
### Bug Fixes
|
||||
* Delete an empty WAL file on DB open if the log number is less than the min log number to keep
|
||||
* Delete temp OPTIONS file on DB open if there is a failure to write it out or rename it
|
||||
|
||||
### Performance Improvements
|
||||
* Improved the I/O efficiency of prefetching SST metadata by recording more information in the DB manifest. Opening files written with previous versions will still rely on heuristics for how much to prefetch (#11406).
|
||||
|
||||
## 8.2.0 (04/24/2023)
|
||||
### Public API Changes
|
||||
* `SstFileWriter::DeleteRange()` now returns `Status::InvalidArgument` if the range's end key comes before its start key according to the user comparator. Previously the behavior was undefined.
|
||||
* Add `multi_get_for_update` to C API.
|
||||
* Remove unnecessary constructor for CompressionOptions.
|
||||
|
||||
### Behavior changes
|
||||
* Changed default block cache size from an 8MB to 32MB LRUCache, which increases the default number of cache shards from 16 to 64. This change is intended to minimize cache mutex contention under stress conditions. See https://github.com/facebook/rocksdb/wiki/Block-Cache for more information.
|
||||
* For level compaction with `level_compaction_dynamic_level_bytes=true`, RocksDB now trivially moves levels down to fill LSM starting from bottommost level during DB open. See more in comments for option `level_compaction_dynamic_level_bytes` (#11321).
|
||||
* User-provided `ReadOptions` take effect for more reads of non-`CacheEntryRole::kDataBlock` blocks.
|
||||
* For level compaction with `level_compaction_dynamic_level_bytes=true`, RocksDB now drains unnecessary levels through background compaction automatically (#11340). This together with #11321 makes it automatic to migrate other compaction settings to level compaction with `level_compaction_dynamic_level_bytes=true`. In addition, a live DB that becomes smaller will now have unnecessary levels drained which can help to reduce read and space amp.
|
||||
* If `CompactRange()` is called with `CompactRangeOptions::bottommost_level_compaction=kForce*` to compact from L0 to L1, RocksDB now will try to do trivial move from L0 to L1 and then do an intra L1 compaction, instead of a L0 to L1 compaction with trivial move disabled (#11375)).
|
||||
|
||||
### Bug Fixes
|
||||
* In the DB::VerifyFileChecksums API, ensure that file system reads of SST files are equal to the readahead_size in ReadOptions, if specified. Previously, each read was 2x the readahead_size.
|
||||
* In block cache tracing, fixed some cases of bad hit/miss information (and more) with MultiGet.
|
||||
|
||||
### New Features
|
||||
* Add experimental `PerfContext` counters `iter_{next|prev|seek}_count` for db iterator, each counting the times of corresponding API being called.
|
||||
* Allow runtime changes to whether `WriteBufferManager` allows stall or not by calling `SetAllowStall()`
|
||||
* Added statistics tickers BYTES_COMPRESSED_FROM, BYTES_COMPRESSED_TO, BYTES_COMPRESSION_BYPASSED, BYTES_COMPRESSION_REJECTED, NUMBER_BLOCK_COMPRESSION_BYPASSED, and NUMBER_BLOCK_COMPRESSION_REJECTED. Disabled/deprecated histograms BYTES_COMPRESSED and BYTES_DECOMPRESSED, and ticker NUMBER_BLOCK_NOT_COMPRESSED. The new tickers offer more inight into compression ratios, rejected vs. disabled compression, etc. (#11388)
|
||||
* New statistics `rocksdb.file.read.{flush|compaction}.micros` that measure read time of block-based SST tables or blob files during flush or compaction.
|
||||
|
||||
## 8.1.0 (03/18/2023)
|
||||
### Behavior changes
|
||||
* Compaction output file cutting logic now considers range tombstone start keys. For example, SST partitioner now may receive ParitionRequest for range tombstone start keys.
|
||||
* If the async_io ReadOption is specified for MultiGet or NewIterator on a platform that doesn't support IO uring, the option is ignored and synchronous IO is used.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed an issue for backward iteration when user defined timestamp is enabled in combination with BlobDB.
|
||||
* Fixed a couple of cases where a Merge operand encountered during iteration wasn't reflected in the `internal_merge_count` PerfContext counter.
|
||||
* Fixed a bug in CreateColumnFamilyWithImport()/ExportColumnFamily() which did not support range tombstones (#11252).
|
||||
* Fixed a bug where an excluded column family from an atomic flush contains unflushed data that should've been included in this atomic flush (i.e, data of seqno less than the max seqno of this atomic flush), leading to potential data loss in this excluded column family when `WriteOptions::disableWAL == true` (#11148).
|
||||
|
||||
### New Features
|
||||
* Add statistics rocksdb.secondary.cache.filter.hits, rocksdb.secondary.cache.index.hits, and rocksdb.secondary.cache.filter.hits
|
||||
* Added a new PerfContext counter `internal_merge_point_lookup_count` which tracks the number of Merge operands applied while serving point lookup queries.
|
||||
* Add new statistics rocksdb.table.open.prefetch.tail.read.bytes, rocksdb.table.open.prefetch.tail.{miss|hit}
|
||||
* Add support for SecondaryCache with HyperClockCache (`HyperClockCacheOptions` inherits `secondary_cache` option from `ShardedCacheOptions`)
|
||||
* Add new db properties `rocksdb.cf-write-stall-stats`, `rocksdb.db-write-stall-stats`and APIs to examine them in a structured way. In particular, users of `GetMapProperty()` with property `kCFWriteStallStats`/`kDBWriteStallStats` can now use the functions in `WriteStallStatsMapKeys` to find stats in the map.
|
||||
|
||||
### Public API Changes
|
||||
* Changed various functions and features in `Cache` that are mostly relevant to custom implementations or wrappers. Especially, asychronous lookup functionality is moved from `Lookup()` to a new `StartAsyncLookup()` function.
|
||||
|
||||
## 8.0.0 (02/19/2023)
|
||||
### Behavior changes
|
||||
* `ReadOptions::verify_checksums=false` disables checksum verification for more reads of non-`CacheEntryRole::kDataBlock` blocks.
|
||||
* In case of scan with async_io enabled, if posix doesn't support IOUring, Status::NotSupported error will be returned to the users. Initially that error was swallowed and reads were switched to synchronous reads.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a data race on `ColumnFamilyData::flush_reason` caused by concurrent flushes.
|
||||
* Fixed an issue in `Get` and `MultiGet` when user-defined timestamps is enabled in combination with BlobDB.
|
||||
* Fixed some atypical behaviors for `LockWAL()` such as allowing concurrent/recursive use and not expecting `UnlockWAL()` after non-OK result. See API comments.
|
||||
* Fixed a feature interaction bug where for blobs `GetEntity` would expose the blob reference instead of the blob value.
|
||||
* Fixed `DisableManualCompaction()` and `CompactRangeOptions::canceled` to cancel compactions even when they are waiting on conflicting compactions to finish
|
||||
* Fixed a bug in which a successful `GetMergeOperands()` could transiently return `Status::MergeInProgress()`
|
||||
* Return the correct error (Status::NotSupported()) to MultiGet caller when ReadOptions::async_io flag is true and IO uring is not enabled. Previously, Status::Corruption() was being returned when the actual failure was lack of async IO support.
|
||||
* Fixed a bug in DB open/recovery from a compressed WAL that was caused due to incorrect handling of certain record fragments with the same offset within a WAL block.
|
||||
|
||||
### Feature Removal
|
||||
* Remove RocksDB Lite.
|
||||
* The feature block_cache_compressed is removed. Statistics related to it are removed too.
|
||||
* Remove deprecated Env::LoadEnv(). Use Env::CreateFromString() instead.
|
||||
* Remove deprecated FileSystem::Load(). Use FileSystem::CreateFromString() instead.
|
||||
* Removed the deprecated version of these utility functions and the corresponding Java bindings: `LoadOptionsFromFile`, `LoadLatestOptions`, `CheckOptionsCompatibility`.
|
||||
* Remove the FactoryFunc from the LoadObject method from the Customizable helper methods.
|
||||
|
||||
### Public API Changes
|
||||
* Moved rarely-needed Cache class definition to new advanced_cache.h, and added a CacheWrapper class to advanced_cache.h. Minor changes to SimCache API definitions.
|
||||
* Completely removed the following deprecated/obsolete statistics: the tickers `BLOCK_CACHE_INDEX_BYTES_EVICT`, `BLOCK_CACHE_FILTER_BYTES_EVICT`, `BLOOM_FILTER_MICROS`, `NO_FILE_CLOSES`, `STALL_L0_SLOWDOWN_MICROS`, `STALL_MEMTABLE_COMPACTION_MICROS`, `STALL_L0_NUM_FILES_MICROS`, `RATE_LIMIT_DELAY_MILLIS`, `NO_ITERATORS`, `NUMBER_FILTERED_DELETES`, `WRITE_TIMEDOUT`, `BLOB_DB_GC_NUM_KEYS_OVERWRITTEN`, `BLOB_DB_GC_NUM_KEYS_EXPIRED`, `BLOB_DB_GC_BYTES_OVERWRITTEN`, `BLOB_DB_GC_BYTES_EXPIRED`, `BLOCK_CACHE_COMPRESSION_DICT_BYTES_EVICT` as well as the histograms `STALL_L0_SLOWDOWN_COUNT`, `STALL_MEMTABLE_COMPACTION_COUNT`, `STALL_L0_NUM_FILES_COUNT`, `HARD_RATE_LIMIT_DELAY_COUNT`, `SOFT_RATE_LIMIT_DELAY_COUNT`, `BLOB_DB_GC_MICROS`, and `NUM_DATA_BLOCKS_READ_PER_LEVEL`. Note that as a result, the C++ enum values of the still supported statistics have changed. Developers are advised to not rely on the actual numeric values.
|
||||
* Deprecated IngestExternalFileOptions::write_global_seqno and change default to false. This option only needs to be set to true to generate a DB compatible with RocksDB versions before 5.16.0.
|
||||
* Remove deprecated APIs `GetColumnFamilyOptionsFrom{Map|String}(const ColumnFamilyOptions&, ..)`, `GetDBOptionsFrom{Map|String}(const DBOptions&, ..)`, `GetBlockBasedTableOptionsFrom{Map|String}(const BlockBasedTableOptions& table_options, ..)` and ` GetPlainTableOptionsFrom{Map|String}(const PlainTableOptions& table_options,..)`.
|
||||
* Added a subcode of `Status::Corruption`, `Status::SubCode::kMergeOperatorFailed`, for users to identify corruption failures originating in the merge operator, as opposed to RocksDB's internally identified data corruptions
|
||||
|
||||
### Build Changes
|
||||
* The `make` build now builds a shared library by default instead of a static library. Use `LIB_MODE=static` to override.
|
||||
|
||||
### New Features
|
||||
* Compaction filters are now supported for wide-column entities by means of the `FilterV3` API. See the comment of the API for more details.
|
||||
* Added `do_not_compress_roles` to `CompressedSecondaryCacheOptions` to disable compression on certain kinds of block. Filter blocks are now not compressed by CompressedSecondaryCache by default.
|
||||
* Added a new `MultiGetEntity` API that enables batched wide-column point lookups. See the API comments for more details.
|
||||
|
||||
## 7.10.0 (01/23/2023)
|
||||
### Behavior changes
|
||||
* Make best-efforts recovery verify SST unique ID before Version construction (#10962)
|
||||
* Introduce `epoch_number` and sort L0 files by `epoch_number` instead of `largest_seqno`. `epoch_number` represents the order of a file being flushed or ingested/imported. Compaction output file will be assigned with the minimum `epoch_number` among input files'. For L0, larger `epoch_number` indicates newer L0 file.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a regression in iterator where range tombstones after `iterate_upper_bound` is processed.
|
||||
* Fixed a memory leak in MultiGet with async_io read option, caused by IO errors during table file open
|
||||
* Fixed a bug that multi-level FIFO compaction deletes one file in non-L0 even when `CompactionOptionsFIFO::max_table_files_size` is no exceeded since #10348 or 7.8.0.
|
||||
* Fixed a bug caused by `DB::SyncWAL()` affecting `track_and_verify_wals_in_manifest`. Without the fix, application may see "open error: Corruption: Missing WAL with log number" while trying to open the db. The corruption is a false alarm but prevents DB open (#10892).
|
||||
* Fixed a BackupEngine bug in which RestoreDBFromLatestBackup would fail if the latest backup was deleted and there is another valid backup available.
|
||||
* Fix L0 file misorder corruption caused by ingesting files of overlapping seqnos with memtable entries' through introducing `epoch_number`. Before the fix, `force_consistency_checks=true` may catch the corruption before it's exposed to readers, in which case writes returning `Status::Corruption` would be expected. Also replace the previous incomplete fix (#5958) to the same corruption with this new and more complete fix.
|
||||
* Fixed a bug in LockWAL() leading to re-locking mutex (#11020).
|
||||
* Fixed a heap use after free bug in async scan prefetching when the scan thread and another thread try to read and load the same seek block into cache.
|
||||
* Fixed a heap use after free in async scan prefetching if dictionary compression is enabled, in which case sync read of the compression dictionary gets mixed with async prefetching
|
||||
* Fixed a data race bug of `CompactRange()` under `change_level=true` acts on overlapping range with an ongoing file ingestion for level compaction. This will either result in overlapping file ranges corruption at a certain level caught by `force_consistency_checks=true` or protentially two same keys both with seqno 0 in two different levels (i.e, new data ends up in lower/older level). The latter will be caught by assertion in debug build but go silently and result in read returning wrong result in release build. This fix is general so it also replaced previous fixes to a similar problem for `CompactFiles()` (#4665), general `CompactRange()` and auto compaction (commit 5c64fb6 and 87dfc1d).
|
||||
* Fixed a bug in compaction output cutting where small output files were produced due to TTL file cutting states were not being updated (#11075).
|
||||
|
||||
### New Features
|
||||
* When an SstPartitionerFactory is configured, CompactRange() now automatically selects for compaction any files overlapping a partition boundary that is in the compaction range, even if no actual entries are in the requested compaction range. With this feature, manual compaction can be used to (re-)establish SST partition points when SstPartitioner changes, without a full compaction.
|
||||
* Add BackupEngine feature to exclude files from backup that are known to be backed up elsewhere, using `CreateBackupOptions::exclude_files_callback`. To restore the DB, the excluded files must be provided in alternative backup directories using `RestoreOptions::alternate_dirs`.
|
||||
|
||||
### Public API Changes
|
||||
* Substantial changes have been made to the Cache class to support internal development goals. Direct use of Cache class members is discouraged and further breaking modifications are expected in the future. SecondaryCache has some related changes and implementations will need to be updated. (Unlike Cache, SecondaryCache is still intended to support user implementations, and disruptive changes will be avoided.) (#10975)
|
||||
* Add `MergeOperationOutput::op_failure_scope` for merge operator users to control the blast radius of merge operator failures. Existing merge operator users do not need to make any change to preserve the old behavior
|
||||
|
||||
### Performance Improvements
|
||||
* Updated xxHash source code, which should improve kXXH3 checksum speed, at least on ARM (#11098).
|
||||
* Improved CPU efficiency of DB reads, from block cache access improvements (#10975).
|
||||
|
||||
## 7.9.0 (11/21/2022)
|
||||
### Performance Improvements
|
||||
* Fixed an iterator performance regression for delete range users when scanning through a consecutive sequence of range tombstones (#10877).
|
||||
|
||||
### Bug Fixes
|
||||
* Fix memory corruption error in scans if async_io is enabled. Memory corruption happened if there is IOError while reading the data leading to empty buffer and other buffer already in progress of async read goes again for reading.
|
||||
* Fix failed memtable flush retry bug that could cause wrongly ordered updates, which would surface to writers as `Status::Corruption` in case of `force_consistency_checks=true` (default). It affects use cases that enable both parallel flush (`max_background_flushes > 1` or `max_background_jobs >= 8`) and non-default memtable count (`max_write_buffer_number > 2`).
|
||||
* Fixed an issue where the `READ_NUM_MERGE_OPERANDS` ticker was not updated when the base key-value or tombstone was read from an SST file.
|
||||
* Fixed a memory safety bug when using a SecondaryCache with `block_cache_compressed`. `block_cache_compressed` no longer attempts to use SecondaryCache features.
|
||||
* Fixed a regression in scan for async_io. During seek, valid buffers were getting cleared causing a regression.
|
||||
* Tiered Storage: fixed excessive keys written to penultimate level in non-debug builds.
|
||||
|
||||
### New Features
|
||||
* Add basic support for user-defined timestamp to Merge (#10819).
|
||||
* Add stats for ReadAsync time spent and async read errors.
|
||||
* Basic support for the wide-column data model is now available. Wide-column entities can be stored using the `PutEntity` API, and retrieved using `GetEntity` and the new `columns` API of iterator. For compatibility, the classic APIs `Get` and `MultiGet`, as well as iterator's `value` API return the value of the anonymous default column of wide-column entities; also, `GetEntity` and iterator's `columns` return any plain key-values in the form of an entity which only has the anonymous default column. `Merge` (and `GetMergeOperands`) currently also apply to the default column; any other columns of entities are unaffected by `Merge` operations. Note that some features like compaction filters, transactions, user-defined timestamps, and the SST file writer do not yet support wide-column entities; also, there is currently no `MultiGet`-like API to retrieve multiple entities at once. We plan to gradually close the above gaps and also implement new features like column-level operations (e.g. updating or querying only certain columns of an entity).
|
||||
* Marked HyperClockCache as a production-ready alternative to LRUCache for the block cache. HyperClockCache greatly improves hot-path CPU efficiency under high parallel load or high contention, with some documented caveats and limitations. As much as 4.5x higher ops/sec vs. LRUCache has been seen in db_bench under high parallel load.
|
||||
* Add periodic diagnostics to info_log (LOG file) for HyperClockCache block cache if performance is degraded by bad `estimated_entry_charge` option.
|
||||
|
||||
### Public API Changes
|
||||
* Marked `block_cache_compressed` as a deprecated feature. Use SecondaryCache instead.
|
||||
* Added a `SecondaryCache::InsertSaved()` API, with default implementation depending on `Insert()`. Some implementations might need to add a custom implementation of `InsertSaved()`. (Details in API comments.)
|
||||
|
||||
## 7.8.0 (10/22/2022)
|
||||
### New Features
|
||||
* `DeleteRange()` now supports user-defined timestamp.
|
||||
* Provide support for async_io with tailing iterators when ReadOptions.tailing is enabled during scans.
|
||||
* Tiered Storage: allow data moving up from the last level to the penultimate level if the input level is penultimate level or above.
|
||||
* Added `DB::Properties::kFastBlockCacheEntryStats`, which is similar to `DB::Properties::kBlockCacheEntryStats`, except returns cached (stale) values in more cases to reduce overhead.
|
||||
* FIFO compaction now supports migrating from a multi-level DB via DB::Open(). During the migration phase, FIFO compaction picker will:
|
||||
* picks the sst file with the smallest starting key in the bottom-most non-empty level.
|
||||
* Note that during the migration phase, the file purge order will only be an approximation of "FIFO" as files in lower-level might sometime contain newer keys than files in upper-level.
|
||||
* Added an option `ignore_max_compaction_bytes_for_input` to ignore max_compaction_bytes limit when adding files to be compacted from input level. This should help reduce write amplification. The option is enabled by default.
|
||||
* Tiered Storage: allow data moving up from the last level even if it's a last level only compaction, as long as the penultimate level is empty.
|
||||
* Add a new option IOOptions.do_not_recurse that can be used by underlying file systems to skip recursing through sub directories and list only files in GetChildren API.
|
||||
* Add option `preserve_internal_time_seconds` to preserve the time information for the latest data. Which can be used to determine the age of data when `preclude_last_level_data_seconds` is enabled. The time information is attached with SST in table property `rocksdb.seqno.time.map` which can be parsed by tool ldb or sst_dump.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in io_uring_prep_cancel in AbortIO API for posix which expects sqe->addr to match with read request submitted and wrong paramter was being passed.
|
||||
* Fixed a regression in iterator performance when the entire DB is a single memtable introduced in #10449. The fix is in #10705 and #10716.
|
||||
* Fixed an optimistic transaction validation bug caused by DBImpl::GetLatestSequenceForKey() returning non-latest seq for merge (#10724).
|
||||
* Fixed a bug in iterator refresh which could segfault for DeleteRange users (#10739).
|
||||
* Fixed a bug causing manual flush with `flush_opts.wait=false` to stall when database has stopped all writes (#10001).
|
||||
* Fixed a bug in iterator refresh that was not freeing up SuperVersion, which could cause excessive resource pinniung (#10770).
|
||||
* Fixed a bug where RocksDB could be doing compaction endlessly when allow_ingest_behind is true and the bottommost level is not filled (#10767).
|
||||
* Fixed a memory safety bug in experimental HyperClockCache (#10768)
|
||||
* Fixed some cases where `ldb update_manifest` and `ldb unsafe_remove_sst_file` are not usable because they were requiring the DB files to match the existing manifest state (before updating the manifest to match a desired state).
|
||||
|
||||
### Performance Improvements
|
||||
* Try to align the compaction output file boundaries to the next level ones, which can reduce more than 10% compaction load for the default level compaction. The feature is enabled by default, to disable, set `AdvancedColumnFamilyOptions.level_compaction_dynamic_file_size` to false. As a side effect, it can create SSTs larger than the target_file_size (capped at 2x target_file_size) or smaller files.
|
||||
* Improve RoundRobin TTL compaction, which is going to be the same as normal RoundRobin compaction to move the compaction cursor.
|
||||
* Fix a small CPU regression caused by a change that UserComparatorWrapper was made Customizable, because Customizable itself has small CPU overhead for initialization.
|
||||
|
||||
### Behavior Changes
|
||||
* Sanitize min_write_buffer_number_to_merge to 1 if atomic flush is enabled to prevent unexpected data loss when WAL is disabled in a multi-column-family setting (#10773).
|
||||
* With periodic stat dumper waits up every options.stats_dump_period_sec seconds, it won't dump stats for a CF if it has no change in the period, unless 7 periods have been skipped.
|
||||
* Only periodic stats dumper triggered by options.stats_dump_period_sec will update stats interval. Ones triggered by DB::GetProperty() will not update stats interval and will report based on an interval since the last time stats dump period.
|
||||
|
||||
### Public API changes
|
||||
* Make kXXH3 checksum the new default, because it is faster on common hardware, especially with kCRC32c affected by a performance bug in some versions of clang (https://github.com/facebook/rocksdb/issues/9891). DBs written with this new setting can be read by RocksDB 6.27 and newer.
|
||||
* Refactor the classes, APIs and data structures for block cache tracing to allow a user provided trace writer to be used. Introduced an abstract BlockCacheTraceWriter class that takes a structured BlockCacheTraceRecord. The BlockCacheTraceWriter implementation can then format and log the record in whatever way it sees fit. The default BlockCacheTraceWriterImpl does file tracing using a user provided TraceWriter. More details in rocksdb/includb/block_cache_trace_writer.h.
|
||||
|
||||
## 7.7.0 (09/18/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a hang when an operation such as `GetLiveFiles` or `CreateNewBackup` is asked to trigger and wait for memtable flush on a read-only DB. Such indirect requests for memtable flush are now ignored on a read-only DB.
|
||||
* Fixed bug where `FlushWAL(true /* sync */)` (used by `GetLiveFilesStorageInfo()`, which is used by checkpoint and backup) could cause parallel writes at the tail of a WAL file to never be synced.
|
||||
* Fix periodic_task unable to re-register the same task type, which may cause `SetOptions()` fail to update periodical_task time like: `stats_dump_period_sec`, `stats_persist_period_sec`.
|
||||
* Fixed a bug in the rocksdb.prefetched.bytes.discarded stat. It was counting the prefetch buffer size, rather than the actual number of bytes discarded from the buffer.
|
||||
* Fix bug where the directory containing CURRENT can left unsynced after CURRENT is updated to point to the latest MANIFEST, which leads to risk of unsync data loss of CURRENT.
|
||||
* Update rocksdb.multiget.io.batch.size stat in non-async MultiGet as well.
|
||||
* Fix a bug in key range overlap checking with concurrent compactions when user-defined timestamp is enabled. User-defined timestamps should be EXCLUDED when checking if two ranges overlap.
|
||||
* Fixed a bug where the blob cache prepopulating logic did not consider the secondary cache (see #10603).
|
||||
* Fixed the rocksdb.num.sst.read.per.level, rocksdb.num.index.and.filter.blocks.read.per.level and rocksdb.num.level.read.per.multiget stats in the MultiGet coroutines
|
||||
|
||||
### Public API changes
|
||||
* Add `rocksdb_column_family_handle_get_id`, `rocksdb_column_family_handle_get_name` to get name, id of column family in C API
|
||||
* Add a new stat rocksdb.async.prefetch.abort.micros to measure time spent waiting for async prefetch reads to abort
|
||||
|
||||
### Java API Changes
|
||||
* Add CompactionPriority.RoundRobin.
|
||||
* Revert to using the default metadata charge policy when creating an LRU cache via the Java API.
|
||||
|
||||
### Behavior Change
|
||||
* DBOptions::verify_sst_unique_id_in_manifest is now an on-by-default feature that verifies SST file identity whenever they are opened by a DB, rather than only at DB::Open time.
|
||||
* Right now, when the option migration tool (OptionChangeMigration()) migrates to FIFO compaction, it compacts all the data into one single SST file and move to L0. This might create a problem for some users: the giant file may be soon deleted to satisfy max_table_files_size, and might cayse the DB to be almost empty. We change the behavior so that the files are cut to be smaller, but these files might not follow the data insertion order. With the change, after the migration, migrated data might not be dropped by insertion order by FIFO compaction.
|
||||
* When a block is firstly found from `CompressedSecondaryCache`, we just insert a dummy block into the primary cache and don’t erase the block from `CompressedSecondaryCache`. A standalone handle is returned to the caller. Only if the block is found again from `CompressedSecondaryCache` before the dummy block is evicted, we erase the block from `CompressedSecondaryCache` and insert it into the primary cache.
|
||||
* When a block is firstly evicted from the primary cache to `CompressedSecondaryCache`, we just insert a dummy block in `CompressedSecondaryCache`. Only if it is evicted again before the dummy block is evicted from the cache, it is treated as a hot block and is inserted into `CompressedSecondaryCache`.
|
||||
* Improved the estimation of memory used by cached blobs by taking into account the size of the object owning the blob value and also the allocator overhead if `malloc_usable_size` is available (see #10583).
|
||||
* Blob values now have their own category in the cache occupancy statistics, as opposed to being lumped into the "Misc" bucket (see #10601).
|
||||
* Change the optimize_multiget_for_io experimental ReadOptions flag to default on.
|
||||
|
||||
### New Features
|
||||
* RocksDB does internal auto prefetching if it notices 2 sequential reads if readahead_size is not specified. New option `num_file_reads_for_auto_readahead` is added in BlockBasedTableOptions which indicates after how many sequential reads internal auto prefetching should be start (default is 2).
|
||||
* Added new perf context counters `block_cache_standalone_handle_count`, `block_cache_real_handle_count`,`compressed_sec_cache_insert_real_count`, `compressed_sec_cache_insert_dummy_count`, `compressed_sec_cache_uncompressed_bytes`, and `compressed_sec_cache_compressed_bytes`.
|
||||
* Memory for blobs which are to be inserted into the blob cache is now allocated using the cache's allocator (see #10628 and #10647).
|
||||
* HyperClockCache is an experimental, lock-free Cache alternative for block cache that offers much improved CPU efficiency under high parallel load or high contention, with some caveats. As much as 4.5x higher ops/sec vs. LRUCache has been seen in db_bench under high parallel load.
|
||||
* `CompressedSecondaryCacheOptions::enable_custom_split_merge` is added for enabling the custom split and merge feature, which split the compressed value into chunks so that they may better fit jemalloc bins.
|
||||
|
||||
### Performance Improvements
|
||||
* Iterator performance is improved for `DeleteRange()` users. Internally, iterator will skip to the end of a range tombstone when possible, instead of looping through each key and check individually if a key is range deleted.
|
||||
* Eliminated some allocations and copies in the blob read path. Also, `PinnableSlice` now only points to the blob value and pins the backing resource (cache entry or buffer) in all cases, instead of containing a copy of the blob value. See #10625 and #10647.
|
||||
* In case of scans with async_io enabled, few optimizations have been added to issue more asynchronous requests in parallel in order to avoid synchronous prefetching.
|
||||
* `DeleteRange()` users should see improvement in get/iterator performance from mutable memtable (see #10547).
|
||||
|
||||
## 7.6.0 (08/19/2022)
|
||||
### New Features
|
||||
* Added `prepopulate_blob_cache` to ColumnFamilyOptions. If enabled, prepopulate warm/hot blobs which are already in memory into blob cache at the time of flush. On a flush, the blob that is in memory (in memtables) get flushed to the device. If using Direct IO, additional IO is incurred to read this blob back into memory again, which is avoided by enabling this option. This further helps if the workload exhibits high temporal locality, where most of the reads go to recently written data. This also helps in case of the remote file system since it involves network traffic and higher latencies.
|
||||
* Support using secondary cache with the blob cache. When creating a blob cache, the user can set a secondary blob cache by configuring `secondary_cache` in LRUCacheOptions.
|
||||
* Charge memory usage of blob cache when the backing cache of the blob cache and the block cache are different. If an operation reserving memory for blob cache exceeds the avaible space left in the block cache at some point (i.e, causing a cache full under `LRUCacheOptions::strict_capacity_limit` = true), creation will fail with `Status::MemoryLimit()`. To opt in this feature, enable charging `CacheEntryRole::kBlobCache` in `BlockBasedTableOptions::cache_usage_options`.
|
||||
* Improve subcompaction range partition so that it is likely to be more even. More evenly distribution of subcompaction will improve compaction throughput for some workloads. All input files' index blocks to sample some anchor key points from which we pick positions to partition the input range. This would introduce some CPU overhead in compaction preparation phase, if subcompaction is enabled, but it should be a small fraction of the CPU usage of the whole compaction process. This also brings a behavier change: subcompaction number is much more likely to maxed out than before.
|
||||
* Add CompactionPri::kRoundRobin, a compaction picking mode that cycles through all the files with a compact cursor in a round-robin manner. This feature is available since 7.5.
|
||||
* Provide support for subcompactions for user_defined_timestamp.
|
||||
* Added an option `memtable_protection_bytes_per_key` that turns on memtable per key-value checksum protection. Each memtable entry will be suffixed by a checksum that is computed during writes, and verified in reads/compaction. Detected corruption will be logged and with corruption status returned to user.
|
||||
* Added a blob-specific cache priority level - bottom level. Blobs are typically lower-value targets for caching than data blocks, since 1) with BlobDB, data blocks containing blob references conceptually form an index structure which has to be consulted before we can read the blob value, and 2) cached blobs represent only a single key-value, while cached data blocks generally contain multiple KVs. The user can specify the new option `low_pri_pool_ratio` in `LRUCacheOptions` to configure the ratio of capacity reserved for low priority cache entries (and therefore the remaining ratio is the space reserved for the bottom level), or configuring the new argument `low_pri_pool_ratio` in `NewLRUCache()` to achieve the same effect.
|
||||
|
||||
### Public API changes
|
||||
* Removed Customizable support for RateLimiter and removed its CreateFromString() and Type() functions.
|
||||
* `CompactRangeOptions::exclusive_manual_compaction` is now false by default. This ensures RocksDB does not introduce artificial parallelism limitations by default.
|
||||
* Tiered Storage: change `bottommost_temperture` to `last_level_temperture`. The old option name is kept only for migration, please use the new option. The behavior is changed to apply temperature for the `last_level` SST files only.
|
||||
* Added a new experimental ReadOption flag called optimize_multiget_for_io, which when set attempts to reduce MultiGet latency by spawning coroutines for keys in multiple levels.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug starting in 7.4.0 in which some fsync operations might be skipped in a DB after any DropColumnFamily on that DB, until it is re-opened. This can lead to data loss on power loss. (For custom FileSystem implementations, this could lead to `FSDirectory::Fsync` or `FSDirectory::Close` after the first `FSDirectory::Close`; Also, valgrind could report call to `close()` with `fd=-1`.)
|
||||
* Fix a bug where `GenericRateLimiter` could revert the bandwidth set dynamically using `SetBytesPerSecond()` when a user configures a structure enclosing it, e.g., using `GetOptionsFromString()` to configure an `Options` that references an existing `RateLimiter` object.
|
||||
* Fix race conditions in `GenericRateLimiter`.
|
||||
* Fix a bug in `FIFOCompactionPicker::PickTTLCompaction` where total_size calculating might cause underflow
|
||||
* Fix data race bug in hash linked list memtable. With this bug, read request might temporarily miss an old record in the memtable in a race condition to the hash bucket.
|
||||
* Fix a bug that `best_efforts_recovery` may fail to open the db with mmap read.
|
||||
* Fixed a bug where blobs read during compaction would pollute the cache.
|
||||
* Fixed a data race in LRUCache when used with a secondary_cache.
|
||||
* Fixed a bug where blobs read by iterators would be inserted into the cache even with the `fill_cache` read option set to false.
|
||||
* Fixed the segfault caused by `AllocateData()` in `CompressedSecondaryCache::SplitValueIntoChunks()` and `MergeChunksIntoValueTest`.
|
||||
* Fixed a bug in BlobDB where a mix of inlined and blob values could result in an incorrect value being passed to the compaction filter (see #10391).
|
||||
* Fixed a memory leak bug in stress tests caused by `FaultInjectionSecondaryCache`.
|
||||
|
||||
### Behavior Change
|
||||
* Added checksum handshake during the copying of decompressed WAL fragment. This together with #9875, #10037, #10212, #10114 and #10319 provides end-to-end integrity protection for write batch during recovery.
|
||||
* To minimize the internal fragmentation caused by the variable size of the compressed blocks in `CompressedSecondaryCache`, the original block is split according to the jemalloc bin size in `Insert()` and then merged back in `Lookup()`.
|
||||
* PosixLogger is removed and by default EnvLogger will be used for info logging. The behavior of the two loggers should be very similar when using the default Posix Env.
|
||||
* Remove [min|max]_timestamp from VersionEdit for now since they are not tracked in MANIFEST anyway but consume two empty std::string (up to 64 bytes) for each file. Should they be added back in the future, we should store them more compactly.
|
||||
* Improve universal tiered storage compaction picker to avoid extra major compaction triggered by size amplification. If `preclude_last_level_data_seconds` is enabled, the size amplification is calculated within non last_level data only which skip the last level and use the penultimate level as the size base.
|
||||
* If an error is hit when writing to a file (append, sync, etc), RocksDB is more strict with not issuing more operations to it, except closing the file, with exceptions of some WAL file operations in error recovery path.
|
||||
* A `WriteBufferManager` constructed with `allow_stall == false` will no longer trigger write stall implicitly by thrashing until memtable count limit is reached. Instead, a column family can continue accumulating writes while that CF is flushing, which means memory may increase. Users who prefer stalling writes must now explicitly set `allow_stall == true`.
|
||||
* Add `CompressedSecondaryCache` into the stress tests.
|
||||
* Block cache keys have changed, which will cause any persistent caches to miss between versions.
|
||||
|
||||
### Performance Improvements
|
||||
* Instead of constructing `FragmentedRangeTombstoneList` during every read operation, it is now constructed once and stored in immutable memtables. This improves speed of querying range tombstones from immutable memtables.
|
||||
* When using iterators with the integrated BlobDB implementation, blob cache handles are now released immediately when the iterator's position changes.
|
||||
* MultiGet can now do more IO in parallel by reading data blocks from SST files in multiple levels, if the optimize_multiget_for_io ReadOption flag is set.
|
||||
|
||||
## 7.5.0 (07/15/2022)
|
||||
### New Features
|
||||
* Mempurge option flag `experimental_mempurge_threshold` is now a ColumnFamilyOptions and can now be dynamically configured using `SetOptions()`.
|
||||
* Support backward iteration when `ReadOptions::iter_start_ts` is set.
|
||||
* Provide support for ReadOptions.async_io with direct_io to improve Seek latency by using async IO to parallelize child iterator seek and doing asynchronous prefetching on sequential scans.
|
||||
* Added support for blob caching in order to cache frequently used blobs for BlobDB.
|
||||
* User can configure the new ColumnFamilyOptions `blob_cache` to enable/disable blob caching.
|
||||
* Either sharing the backend cache with the block cache or using a completely separate cache is supported.
|
||||
* A new abstraction interface called `BlobSource` for blob read logic gives all users access to blobs, whether they are in the blob cache, secondary cache, or (remote) storage. Blobs can be potentially read both while handling user reads (`Get`, `MultiGet`, or iterator) and during compaction (while dealing with compaction filters, Merges, or garbage collection) but eventually all blob reads go through `Version::GetBlob` or, for MultiGet, `Version::MultiGetBlob` (and then get dispatched to the interface -- `BlobSource`).
|
||||
* Add experimental tiered compaction feature `AdvancedColumnFamilyOptions::preclude_last_level_data_seconds`, which makes sure the new data inserted within preclude_last_level_data_seconds won't be placed on cold tier (the feature is not complete).
|
||||
|
||||
### Public API changes
|
||||
* Add metadata related structs and functions in C API, including
|
||||
* `rocksdb_get_column_family_metadata()` and `rocksdb_get_column_family_metadata_cf()` to obtain `rocksdb_column_family_metadata_t`.
|
||||
* `rocksdb_column_family_metadata_t` and its get functions & destroy function.
|
||||
* `rocksdb_level_metadata_t` and its and its get functions & destroy function.
|
||||
* `rocksdb_file_metadata_t` and its and get functions & destroy functions.
|
||||
* Add suggest_compact_range() and suggest_compact_range_cf() to C API.
|
||||
* When using block cache strict capacity limit (`LRUCache` with `strict_capacity_limit=true`), DB operations now fail with Status code `kAborted` subcode `kMemoryLimit` (`IsMemoryLimit()`) instead of `kIncomplete` (`IsIncomplete()`) when the capacity limit is reached, because Incomplete can mean other specific things for some operations. In more detail, `Cache::Insert()` now returns the updated Status code and this usually propagates through RocksDB to the user on failure.
|
||||
* NewClockCache calls temporarily return an LRUCache (with similar characteristics as the desired ClockCache). This is because ClockCache is being replaced by a new version (the old one had unknown bugs) but this is still under development.
|
||||
* Add two functions `int ReserveThreads(int threads_to_be_reserved)` and `int ReleaseThreads(threads_to_be_released)` into `Env` class. In the default implementation, both return 0. Newly added `xxxEnv` class that inherits `Env` should implement these two functions for thread reservation/releasing features.
|
||||
* Add `rocksdb_options_get_prepopulate_blob_cache` and `rocksdb_options_set_prepopulate_blob_cache` to C API.
|
||||
* Add `prepopulateBlobCache` and `setPrepopulateBlobCache` to Java API.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in which backup/checkpoint can include a WAL deleted by RocksDB.
|
||||
* Fix a bug where concurrent compactions might cause unnecessary further write stalling. In some cases, this might cause write rate to drop to minimum.
|
||||
* Fix a bug in Logger where if dbname and db_log_dir are on different filesystems, dbname creation would fail wrt to db_log_dir path returning an error and fails to open the DB.
|
||||
* Fix a CPU and memory efficiency issue introduce by https://github.com/facebook/rocksdb/pull/8336 which made InternalKeyComparator configurable as an unintended side effect.
|
||||
|
||||
## Behavior Change
|
||||
* In leveled compaction with dynamic levelling, level multiplier is not anymore adjusted due to oversized L0. Instead, compaction score is adjusted by increasing size level target by adding incoming bytes from upper levels. This would deprioritize compactions from upper levels if more data from L0 is coming. This is to fix some unnecessary full stalling due to drastic change of level targets, while not wasting write bandwidth for compaction while writes are overloaded.
|
||||
* For track_and_verify_wals_in_manifest, revert to the original behavior before #10087: syncing of live WAL file is not tracked, and we track only the synced sizes of **closed** WALs. (PR #10330).
|
||||
* WAL compression now computes/verifies checksum during compression/decompression.
|
||||
|
||||
### Performance Improvements
|
||||
* Rather than doing total sort against all files in a level, SortFileByOverlappingRatio() to only find the top 50 files based on score. This can improve write throughput for the use cases where data is loaded in increasing key order and there are a lot of files in one LSM-tree, where applying compaction results is the bottleneck.
|
||||
* In leveled compaction, L0->L1 trivial move will allow more than one file to be moved in one compaction. This would allow L0 files to be moved down faster when data is loaded in sequential order, making slowdown or stop condition harder to hit. Also seek L0->L1 trivial move when only some files qualify.
|
||||
* In leveled compaction, try to trivial move more than one files if possible, up to 4 files or max_compaction_bytes. This is to allow higher write throughput for some use cases where data is loaded in sequential order, where appying compaction results is the bottleneck.
|
||||
|
||||
## 7.4.0 (06/19/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a bug in calculating key-value integrity protection for users of in-place memtable updates. In particular, the affected users would be those who configure `protection_bytes_per_key > 0` on `WriteBatch` or `WriteOptions`, and configure `inplace_callback != nullptr`.
|
||||
* Fixed a bug where a snapshot taken during SST file ingestion would be unstable.
|
||||
* Fixed a bug for non-TransactionDB with avoid_flush_during_recovery = true and TransactionDB where in case of crash, min_log_number_to_keep may not change on recovery and persisting a new MANIFEST with advanced log_numbers for some column families, results in "column family inconsistency" error on second recovery. As a solution, RocksDB will persist the new MANIFEST after successfully syncing the new WAL. If a future recovery starts from the new MANIFEST, then it means the new WAL is successfully synced. Due to the sentinel empty write batch at the beginning, kPointInTimeRecovery of WAL is guaranteed to go after this point. If future recovery starts from the old MANIFEST, it means the writing the new MANIFEST failed. We won't have the "SST ahead of WAL" error.
|
||||
* Fixed a bug where RocksDB DB::Open() may creates and writes to two new MANIFEST files even before recovery succeeds. Now writes to MANIFEST are persisted only after recovery is successful.
|
||||
* Fix a race condition in WAL size tracking which is caused by an unsafe iterator access after container is changed.
|
||||
* Fix unprotected concurrent accesses to `WritableFileWriter::filesize_` by `DB::SyncWAL()` and `DB::Put()` in two write queue mode.
|
||||
* Fix a bug in WAL tracking. Before this PR (#10087), calling `SyncWAL()` on the only WAL file of the db will not log the event in MANIFEST, thus allowing a subsequent `DB::Open` even if the WAL file is missing or corrupted.
|
||||
* Fix a bug that could return wrong results with `index_type=kHashSearch` and using `SetOptions` to change the `prefix_extractor`.
|
||||
* Fixed a bug in WAL tracking with wal_compression. WAL compression writes a kSetCompressionType record which is not associated with any sequence number. As result, WalManager::GetSortedWalsOfType() will skip these WALs and not return them to caller, e.g. Checkpoint, Backup, causing the operations to fail.
|
||||
* Avoid a crash if the IDENTITY file is accidentally truncated to empty. A new DB ID will be written and generated on Open.
|
||||
* Fixed a possible corruption for users of `manual_wal_flush` and/or `FlushWAL(true /* sync */)`, together with `track_and_verify_wals_in_manifest == true`. For those users, losing unsynced data (e.g., due to power loss) could make future DB opens fail with a `Status::Corruption` complaining about missing WAL data.
|
||||
* Fixed a bug in `WriteBatchInternal::Append()` where WAL termination point in write batch was not considered and the function appends an incorrect number of checksums.
|
||||
* Fixed a crash bug introduced in 7.3.0 affecting users of MultiGet with `kDataBlockBinaryAndHash`.
|
||||
|
||||
### Public API changes
|
||||
* Add new API GetUnixTime in Snapshot class which returns the unix time at which Snapshot is taken.
|
||||
* Add transaction `get_pinned` and `multi_get` to C API.
|
||||
* Add two-phase commit support to C API.
|
||||
* Add `rocksdb_transaction_get_writebatch_wi` and `rocksdb_transaction_rebuild_from_writebatch` to C API.
|
||||
* Add `rocksdb_options_get_blob_file_starting_level` and `rocksdb_options_set_blob_file_starting_level` to C API.
|
||||
* Add `blobFileStartingLevel` and `setBlobFileStartingLevel` to Java API.
|
||||
* Add SingleDelete for DB in C API
|
||||
* Add User Defined Timestamp in C API.
|
||||
* `rocksdb_comparator_with_ts_create` to create timestamp aware comparator
|
||||
* Put, Get, Delete, SingleDelete, MultiGet APIs has corresponding timestamp aware APIs with suffix `with_ts`
|
||||
* And Add C API's for Transaction, SstFileWriter, Compaction as mentioned [here](https://github.com/facebook/rocksdb/wiki/User-defined-Timestamp-(Experimental))
|
||||
* The contract for implementations of Comparator::IsSameLengthImmediateSuccessor has been updated to work around a design bug in `auto_prefix_mode`.
|
||||
* The API documentation for `auto_prefix_mode` now notes some corner cases in which it returns different results than `total_order_seek`, due to design bugs that are not easily fixed. Users using built-in comparators and keys at least the size of a fixed prefix length are not affected.
|
||||
* Obsoleted the NUM_DATA_BLOCKS_READ_PER_LEVEL stat and introduced the NUM_LEVEL_READ_PER_MULTIGET and MULTIGET_COROUTINE_COUNT stats
|
||||
* Introduced `WriteOptions::protection_bytes_per_key`, which can be used to enable key-value integrity protection for live updates.
|
||||
|
||||
### New Features
|
||||
* Add FileSystem::ReadAsync API in io_tracing
|
||||
* Add blob garbage collection parameters `blob_garbage_collection_policy` and `blob_garbage_collection_age_cutoff` to both force-enable and force-disable GC, as well as selectively override age cutoff when using CompactRange.
|
||||
* Add an extra sanity check in `GetSortedWalFiles()` (also used by `GetLiveFilesStorageInfo()`, `BackupEngine`, and `Checkpoint`) to reduce risk of successfully created backup or checkpoint failing to open because of missing WAL file.
|
||||
* Add a new column family option `blob_file_starting_level` to enable writing blob files during flushes and compactions starting from the specified LSM tree level.
|
||||
* Add support for timestamped snapshots (#9879)
|
||||
* Provide support for AbortIO in posix to cancel submitted asynchronous requests using io_uring.
|
||||
* Add support for rate-limiting batched `MultiGet()` APIs
|
||||
* Added several new tickers, perf context statistics, and DB properties to BlobDB
|
||||
* Added new DB properties "rocksdb.blob-cache-capacity", "rocksdb.blob-cache-usage", "rocksdb.blob-cache-pinned-usage" to show blob cache usage.
|
||||
* Added new perf context statistics `blob_cache_hit_count`, `blob_read_count`, `blob_read_byte`, `blob_read_time`, `blob_checksum_time` and `blob_decompress_time`.
|
||||
* Added new tickers `BLOB_DB_CACHE_MISS`, `BLOB_DB_CACHE_HIT`, `BLOB_DB_CACHE_ADD`, `BLOB_DB_CACHE_ADD_FAILURES`, `BLOB_DB_CACHE_BYTES_READ` and `BLOB_DB_CACHE_BYTES_WRITE`.
|
||||
|
||||
### Behavior changes
|
||||
* DB::Open(), DB::OpenAsSecondary() will fail if a Logger cannot be created (#9984)
|
||||
* DB::Write does not hold global `mutex_` if this db instance does not need to switch wal and mem-table (#7516).
|
||||
* Removed support for reading Bloom filters using obsolete block-based filter format. (Support for writing such filters was dropped in 7.0.) For good read performance on old DBs using these filters, a full compaction is required.
|
||||
* Per KV checksum in write batch is verified before a write batch is written to WAL to detect any corruption to the write batch (#10114).
|
||||
|
||||
### Performance Improvements
|
||||
* When compiled with folly (Meta-internal integration; experimental in open source build), improve the locking performance (CPU efficiency) of LRUCache by using folly DistributedMutex in place of standard mutex.
|
||||
|
||||
## 7.3.0 (05/20/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a bug where manual flush would block forever even though flush options had wait=false.
|
||||
* Fixed a bug where RocksDB could corrupt DBs with `avoid_flush_during_recovery == true` by removing valid WALs, leading to `Status::Corruption` with message like "SST file is ahead of WALs" when attempting to reopen.
|
||||
* Fixed a bug in async_io path where incorrect length of data is read by FilePrefetchBuffer if data is consumed from two populated buffers and request for more data is sent.
|
||||
* Fixed a CompactionFilter bug. Compaction filter used to use `Delete` to remove keys, even if the keys should be removed with `SingleDelete`. Mixing `Delete` and `SingleDelete` may cause undefined behavior.
|
||||
* Fixed a bug in `WritableFileWriter::WriteDirect` and `WritableFileWriter::WriteDirectWithChecksum`. The rate_limiter_priority specified in ReadOptions was not passed to the RateLimiter when requesting a token.
|
||||
* Fixed a bug which might cause process crash when I/O error happens when reading an index block in MultiGet().
|
||||
|
||||
### New Features
|
||||
* DB::GetLiveFilesStorageInfo is ready for production use.
|
||||
* Add new stats PREFETCHED_BYTES_DISCARDED which records number of prefetched bytes discarded by RocksDB FilePrefetchBuffer on destruction and POLL_WAIT_MICROS records wait time for FS::Poll API completion.
|
||||
* RemoteCompaction supports table_properties_collector_factories override on compaction worker.
|
||||
* Start tracking SST unique id in MANIFEST, which will be used to verify with SST properties during DB open to make sure the SST file is not overwritten or misplaced. A db option `verify_sst_unique_id_in_manifest` is introduced to enable/disable the verification, if enabled all SST files will be opened during DB-open to verify the unique id (default is false), so it's recommended to use it with `max_open_files = -1` to pre-open the files.
|
||||
* Added the ability to concurrently read data blocks from multiple files in a level in batched MultiGet. This can be enabled by setting the async_io option in ReadOptions. Using this feature requires a FileSystem that supports ReadAsync (PosixFileSystem is not supported yet for this), and for RocksDB to be compiled with folly and c++20.
|
||||
* Charge memory usage of file metadata. RocksDB holds one file metadata structure in-memory per on-disk table file. If an operation reserving memory for file metadata exceeds the avaible space left in the block
|
||||
cache at some point (i.e, causing a cache full under `LRUCacheOptions::strict_capacity_limit` = true), creation will fail with `Status::MemoryLimit()`. To opt in this feature, enable charging `CacheEntryRole::kFileMetadata` in `BlockBasedTableOptions::cache_usage_options`.
|
||||
|
||||
### Public API changes
|
||||
* Add rollback_deletion_type_callback to TransactionDBOptions so that write-prepared transactions know whether to issue a Delete or SingleDelete to cancel a previous key written during prior prepare phase. The PR aims to prevent mixing SingleDeletes and Deletes for the same key that can lead to undefined behaviors for write-prepared transactions.
|
||||
* EXPERIMENTAL: Add new API AbortIO in file_system to abort the read requests submitted asynchronously.
|
||||
* CompactionFilter::Decision has a new value: kRemoveWithSingleDelete. If CompactionFilter returns this decision, then CompactionIterator will use `SingleDelete` to mark a key as removed.
|
||||
* Renamed CompactionFilter::Decision::kRemoveWithSingleDelete to kPurge since the latter sounds more general and hides the implementation details of how compaction iterator handles keys.
|
||||
* Added ability to specify functions for Prepare and Validate to OptionsTypeInfo. Added methods to OptionTypeInfo to set the functions via an API. These methods are intended for RocksDB plugin developers for configuration management.
|
||||
* Added a new immutable db options, enforce_single_del_contracts. If set to false (default is true), compaction will NOT fail due to a single delete followed by a delete for the same key. The purpose of this temporay option is to help existing use cases migrate.
|
||||
* Introduce `BlockBasedTableOptions::cache_usage_options` and use that to replace `BlockBasedTableOptions::reserve_table_builder_memory` and `BlockBasedTableOptions::reserve_table_reader_memory`.
|
||||
* Changed `GetUniqueIdFromTableProperties` to return a 128-bit unique identifier, which will be the standard size now. The old functionality (192-bit) is available from `GetExtendedUniqueIdFromTableProperties`. Both functions are no longer "experimental" and are ready for production use.
|
||||
* In IOOptions, mark `prio` as deprecated for future removal.
|
||||
* In `file_system.h`, mark `IOPriority` as deprecated for future removal.
|
||||
* Add an option, `CompressionOptions::use_zstd_dict_trainer`, to indicate whether zstd dictionary trainer should be used for generating zstd compression dictionaries. The default value of this option is true for backward compatibility. When this option is set to false, zstd API `ZDICT_finalizeDictionary` is used to generate compression dictionaries.
|
||||
* Seek API which positions itself every LevelIterator on the correct data block in the correct SST file which can be parallelized if ReadOptions.async_io option is enabled.
|
||||
* Add new stat number_async_seek in PerfContext that indicates number of async calls made by seek to prefetch data.
|
||||
* Add support for user-defined timestamps to read only DB.
|
||||
|
||||
### Bug Fixes
|
||||
* RocksDB calls FileSystem::Poll API during FilePrefetchBuffer destruction which impacts performance as it waits for read requets completion which is not needed anymore. Calling FileSystem::AbortIO to abort those requests instead fixes that performance issue.
|
||||
* Fixed unnecessary block cache contention when queries within a MultiGet batch and across parallel batches access the same data block, which previously could cause severely degraded performance in this unusual case. (In more typical MultiGet cases, this fix is expected to yield a small or negligible performance improvement.)
|
||||
|
||||
### Behavior changes
|
||||
* Enforce the existing contract of SingleDelete so that SingleDelete cannot be mixed with Delete because it leads to undefined behavior. Fix a number of unit tests that violate the contract but happen to pass.
|
||||
* ldb `--try_load_options` default to true if `--db` is specified and not creating a new DB, the user can still explicitly disable that by `--try_load_options=false` (or explicitly enable that by `--try_load_options`).
|
||||
* During Flush write or Compaction write/read, the WriteController is used to determine whether DB writes are stalled or slowed down. The priority (Env::IOPriority) can then be determined accordingly and be passed in IOOptions to the file system.
|
||||
|
||||
### Performance Improvements
|
||||
* Avoid calling malloc_usable_size() in LRU Cache's mutex.
|
||||
* Reduce DB mutex holding time when finding obsolete files to delete. When a file is trivial moved to another level, the internal files will be referenced twice internally and sometimes opened twice too. If a deletion candidate file is not the last reference, we need to destroy the reference and close the file but not deleting the file. Right now we determine it by building a set of all live files. With the improvement, we check the file against all live LSM-tree versions instead.
|
||||
|
||||
## 7.2.0 (04/15/2022)
|
||||
### Bug Fixes
|
||||
* Fixed bug which caused rocksdb failure in the situation when rocksdb was accessible using UNC path
|
||||
* Fixed a race condition when 2PC is disabled and WAL tracking in the MANIFEST is enabled. The race condition is between two background flush threads trying to install flush results, causing a WAL deletion not tracked in the MANIFEST. A future DB open may fail.
|
||||
* Fixed a heap use-after-free race with DropColumnFamily.
|
||||
* Fixed a bug that `rocksdb.read.block.compaction.micros` cannot track compaction stats (#9722).
|
||||
* Fixed `file_type`, `relative_filename` and `directory` fields returned by `GetLiveFilesMetaData()`, which were added in inheriting from `FileStorageInfo`.
|
||||
* Fixed a bug affecting `track_and_verify_wals_in_manifest`. Without the fix, application may see "open error: Corruption: Missing WAL with log number" while trying to open the db. The corruption is a false alarm but prevents DB open (#9766).
|
||||
* Fix segfault in FilePrefetchBuffer with async_io as it doesn't wait for pending jobs to complete on destruction.
|
||||
* Fix ERROR_HANDLER_AUTORESUME_RETRY_COUNT stat whose value was set wrong in portal.h
|
||||
* Fixed a bug for non-TransactionDB with avoid_flush_during_recovery = true and TransactionDB where in case of crash, min_log_number_to_keep may not change on recovery and persisting a new MANIFEST with advanced log_numbers for some column families, results in "column family inconsistency" error on second recovery. As a solution the corrupted WALs whose numbers are larger than the corrupted wal and smaller than the new WAL will be moved to archive folder.
|
||||
* Fixed a bug in RocksDB DB::Open() which may creates and writes to two new MANIFEST files even before recovery succeeds. Now writes to MANIFEST are persisted only after recovery is successful.
|
||||
|
||||
### New Features
|
||||
* For db_bench when --seed=0 or --seed is not set then it uses the current time as the seed value. Previously it used the value 1000.
|
||||
* For db_bench when --benchmark lists multiple tests and each test uses a seed for a RNG then the seeds across tests will no longer be repeated.
|
||||
* Added an option to dynamically charge an updating estimated memory usage of block-based table reader to block cache if block cache available. To enable this feature, set `BlockBasedTableOptions::reserve_table_reader_memory = true`.
|
||||
* Add new stat ASYNC_READ_BYTES that calculates number of bytes read during async read call and users can check if async code path is being called by RocksDB internal automatic prefetching for sequential reads.
|
||||
* Enable async prefetching if ReadOptions.readahead_size is set along with ReadOptions.async_io in FilePrefetchBuffer.
|
||||
* Add event listener support on remote compaction compactor side.
|
||||
* Added a dedicated integer DB property `rocksdb.live-blob-file-garbage-size` that exposes the total amount of garbage in the blob files in the current version.
|
||||
* RocksDB does internal auto prefetching if it notices sequential reads. It starts with readahead size `initial_auto_readahead_size` which now can be configured through BlockBasedTableOptions.
|
||||
* Add a merge operator that allows users to register specific aggregation function so that they can does aggregation using different aggregation types for different keys. See comments in include/rocksdb/utilities/agg_merge.h for actual usage. The feature is experimental and the format is subject to change and we won't provide a migration tool.
|
||||
* Meta-internal / Experimental: Improve CPU performance by replacing many uses of std::unordered_map with folly::F14FastMap when RocksDB is compiled together with Folly.
|
||||
* Experimental: Add CompressedSecondaryCache, a concrete implementation of rocksdb::SecondaryCache, that integrates with compression libraries (e.g. LZ4) to hold compressed blocks.
|
||||
|
||||
### Behavior changes
|
||||
* Disallow usage of commit-time-write-batch for write-prepared/write-unprepared transactions if TransactionOptions::use_only_the_last_commit_time_batch_for_recovery is false to prevent two (or more) uncommitted versions of the same key in the database. Otherwise, bottommost compaction may violate the internal key uniqueness invariant of SSTs if the sequence numbers of both internal keys are zeroed out (#9794).
|
||||
* Make DB::GetUpdatesSince() return NotSupported early for write-prepared/write-unprepared transactions, as the API contract indicates.
|
||||
|
||||
### Public API changes
|
||||
* Exposed APIs to examine results of block cache stats collections in a structured way. In particular, users of `GetMapProperty()` with property `kBlockCacheEntryStats` can now use the functions in `BlockCacheEntryStatsMapKeys` to find stats in the map.
|
||||
* Add `fail_if_not_bottommost_level` to IngestExternalFileOptions so that ingestion will fail if the file(s) cannot be ingested to the bottommost level.
|
||||
* Add output parameter `is_in_sec_cache` to `SecondaryCache::Lookup()`. It is to indicate whether the handle is possibly erased from the secondary cache after the Lookup.
|
||||
|
||||
## 7.1.0 (03/23/2022)
|
||||
### New Features
|
||||
* Allow WriteBatchWithIndex to index a WriteBatch that includes keys with user-defined timestamps. The index itself does not have timestamp.
|
||||
@@ -2389,7 +1658,7 @@ if set to something > 0 user will see 2 changes in iterators behavior 1) only ke
|
||||
* Added a new way to report QPS from db_bench (check out --report_file and --report_interval_seconds)
|
||||
* Added a cache for individual rows. See DBOptions::row_cache for more info.
|
||||
* Several new features on EventListener (see include/rocksdb/listener.h):
|
||||
- OnCompactionCompleted() now returns per-compaction job statistics, defined in include/rocksdb/compaction_job_stats.h.
|
||||
- OnCompationCompleted() now returns per-compaction job statistics, defined in include/rocksdb/compaction_job_stats.h.
|
||||
- Added OnTableFileCreated() and OnTableFileDeleted().
|
||||
* Add compaction_options_universal.enable_trivial_move to true, to allow trivial move while performing universal compaction. Trivial move will happen only when all the input files are non overlapping.
|
||||
|
||||
|
||||
+15
-27
@@ -17,18 +17,15 @@ There are few options when compiling RocksDB:
|
||||
* `make check` will compile and run all the unit tests. `make check` will compile RocksDB in debug mode.
|
||||
|
||||
* `make all` will compile our static library, and all our tools and unit tests. Our tools
|
||||
depend on gflags 2.2.0 or newer. You will need to have gflags installed to run `make all`. This will compile RocksDB in debug mode. Don't
|
||||
depend on gflags. You will need to have gflags installed to run `make all`. This will compile RocksDB in debug mode. Don't
|
||||
use binaries compiled by `make all` in production.
|
||||
|
||||
* By default the binary we produce is optimized for the CPU you're compiling on
|
||||
(`-march=native` or the equivalent). To build a binary compatible with the most
|
||||
general architecture supported by your CPU and compiler, set `PORTABLE=1` for
|
||||
the build, but performance will suffer as many operations benefit from newer
|
||||
and wider instructions. In addition to `PORTABLE=0` (default) and `PORTABLE=1`,
|
||||
it can be set to an architecture name recognized by your compiler. For example,
|
||||
on 64-bit x86, a reasonable compromise is `PORTABLE=haswell` which supports
|
||||
many or most of the available optimizations while still being compatible with
|
||||
most processors made since roughly 2013.
|
||||
* By default the binary we produce is optimized for the platform you're compiling on
|
||||
(`-march=native` or the equivalent). SSE4.2 will thus be enabled automatically if your
|
||||
CPU supports it. To print a warning if your CPU does not support SSE4.2, build with
|
||||
`USE_SSE=1 make static_lib` or, if using CMake, `cmake -DFORCE_SSE42=ON`. If you want
|
||||
to build a portable binary, add `PORTABLE=1` before your make commands, like this:
|
||||
`PORTABLE=1 make static_lib`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -50,12 +47,7 @@ most processors made since roughly 2013.
|
||||
|
||||
* If you wish to build the RocksJava static target, then cmake is required for building Snappy.
|
||||
|
||||
* If you wish to run microbench (e.g, `make microbench`, `make ribbon_bench` or `cmake -DWITH_BENCHMARK=1`), Google benchmark >= 1.6.0 is needed.
|
||||
* You can do the following to install Google benchmark. These commands are copied from `./build_tools/ubuntu20_image/Dockerfile`:
|
||||
|
||||
`$ 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`
|
||||
* If you wish to run microbench (e.g, `make microbench`, `make ribbon_bench` or `cmake -DWITH_BENCHMARK=1`), Google benchmark < 1.6.0 is needed.
|
||||
|
||||
## Supported platforms
|
||||
|
||||
@@ -77,7 +69,7 @@ most processors made since roughly 2013.
|
||||
|
||||
git clone https://github.com/gflags/gflags.git
|
||||
cd gflags
|
||||
git checkout v2.2.0
|
||||
git checkout v2.0
|
||||
./configure && make && sudo make install
|
||||
|
||||
**Notice**: Once installed, please add the include path for gflags to your `CPATH` environment variable and the
|
||||
@@ -169,31 +161,27 @@ most processors made since roughly 2013.
|
||||
|
||||
* Install the dependencies for RocksDB:
|
||||
|
||||
`pkg_add gmake gflags snappy bzip2 lz4 zstd git bash findutils gnuwatch`
|
||||
pkg_add gmake gflags snappy bzip2 lz4 zstd git jdk bash findutils gnuwatch
|
||||
|
||||
* Build RocksDB from source:
|
||||
|
||||
```bash
|
||||
cd ~
|
||||
git clone https://github.com/facebook/rocksdb.git
|
||||
cd rocksdb
|
||||
gmake static_lib
|
||||
```
|
||||
|
||||
* Build RocksJava from source (optional):
|
||||
* In OpenBSD, JDK depends on XWindows system, so please check that you installed OpenBSD with `xbase` package.
|
||||
* Install dependencies : `pkg_add -v jdk%1.8`
|
||||
```bash
|
||||
|
||||
cd rocksdb
|
||||
export JAVA_HOME=/usr/local/jdk-1.8.0
|
||||
export PATH=$PATH:/usr/local/jdk-1.8.0/bin
|
||||
gmake rocksdbjava SHA256_CMD='sha256 -q'
|
||||
```
|
||||
gmake rocksdbjava
|
||||
|
||||
* **iOS**:
|
||||
* Run: `TARGET_OS=IOS make static_lib`. When building the project which uses rocksdb iOS library, make sure to define an important pre-processing macros: `IOS_CROSS_COMPILE`.
|
||||
* Run: `TARGET_OS=IOS make static_lib`. When building the project which uses rocksdb iOS library, make sure to define two important pre-processing macros: `ROCKSDB_LITE` and `IOS_CROSS_COMPILE`.
|
||||
|
||||
* **Windows** (Visual Studio 2017 to up):
|
||||
* **Windows**:
|
||||
* For building with MS Visual Studio 13 you will need Update 4 installed.
|
||||
* Read and follow the instructions at CMakeLists.txt
|
||||
* Or install via [vcpkg](https://github.com/microsoft/vcpkg)
|
||||
* run `vcpkg install rocksdb:x64-windows`
|
||||
|
||||
@@ -6,9 +6,7 @@ This is the list of all known third-party language bindings for RocksDB. If some
|
||||
* http://pyrocksdb.readthedocs.org/en/latest/ (unmaintained)
|
||||
* Perl - https://metacpan.org/pod/RocksDB
|
||||
* Node.js - https://npmjs.org/package/rocksdb
|
||||
* Go
|
||||
* https://github.com/linxGnu/grocksdb
|
||||
* https://github.com/tecbot/gorocksdb (unmaintained)
|
||||
* Go - https://github.com/tecbot/gorocksdb
|
||||
* Ruby - http://rubygems.org/gems/rocksdb-ruby
|
||||
* Haskell - https://hackage.haskell.org/package/rocksdb-haskell
|
||||
* PHP - https://github.com/Photonios/rocksdb-php
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
BASH_EXISTS := $(shell which bash)
|
||||
SHELL := $(shell which bash)
|
||||
include common.mk
|
||||
include python.mk
|
||||
|
||||
CLEAN_FILES = # deliberately empty, so we can append below.
|
||||
CFLAGS += ${EXTRA_CFLAGS}
|
||||
@@ -44,6 +44,13 @@ quoted_perl_command = $(subst ','\'',$(perl_command))
|
||||
# Set the default DEBUG_LEVEL to 1
|
||||
DEBUG_LEVEL?=1
|
||||
|
||||
# LIB_MODE says whether or not to use/build "shared" or "static" libraries.
|
||||
# Mode "static" means to link against static libraries (.a)
|
||||
# Mode "shared" means to link against shared libraries (.so, .sl, .dylib, etc)
|
||||
#
|
||||
# Set the default LIB_MODE to static
|
||||
LIB_MODE?=static
|
||||
|
||||
# OBJ_DIR is where the object files reside. Default to the current directory
|
||||
OBJ_DIR?=.
|
||||
|
||||
@@ -74,42 +81,29 @@ else ifneq ($(filter jtest rocksdbjava%, $(MAKECMDGOALS)),)
|
||||
endif
|
||||
endif
|
||||
|
||||
# LIB_MODE says whether or not to use/build "shared" or "static" libraries.
|
||||
# Mode "static" means to link against static libraries (.a)
|
||||
# Mode "shared" means to link against shared libraries (.so, .sl, .dylib, etc)
|
||||
#
|
||||
ifeq ($(DEBUG_LEVEL), 0)
|
||||
# For optimized, set the default LIB_MODE to static for code size/efficiency
|
||||
LIB_MODE?=static
|
||||
else
|
||||
# For debug, set the default LIB_MODE to shared for efficient `make check` etc.
|
||||
LIB_MODE?=shared
|
||||
$(info $$DEBUG_LEVEL is ${DEBUG_LEVEL})
|
||||
|
||||
# Lite build flag.
|
||||
LITE ?= 0
|
||||
ifeq ($(LITE), 0)
|
||||
ifneq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
# Be backward compatible and support older format where OPT=-DROCKSDB_LITE is
|
||||
# specified instead of LITE=1 on the command line.
|
||||
LITE=1
|
||||
endif
|
||||
else ifeq ($(LITE), 1)
|
||||
ifeq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
OPT += -DROCKSDB_LITE
|
||||
endif
|
||||
endif
|
||||
|
||||
$(info $$DEBUG_LEVEL is $(DEBUG_LEVEL), $$LIB_MODE is $(LIB_MODE))
|
||||
|
||||
# Detect what platform we're building on.
|
||||
# Export some common variables that might have been passed as Make variables
|
||||
# instead of environment variables.
|
||||
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
|
||||
export CXXFLAGS="$(EXTRA_CXXFLAGS)"; \
|
||||
export LDFLAGS="$(EXTRA_LDFLAGS)"; \
|
||||
export COMPILE_WITH_ASAN="$(COMPILE_WITH_ASAN)"; \
|
||||
export COMPILE_WITH_TSAN="$(COMPILE_WITH_TSAN)"; \
|
||||
export COMPILE_WITH_UBSAN="$(COMPILE_WITH_UBSAN)"; \
|
||||
export PORTABLE="$(PORTABLE)"; \
|
||||
export ROCKSDB_NO_FBCODE="$(ROCKSDB_NO_FBCODE)"; \
|
||||
export USE_CLANG="$(USE_CLANG)"; \
|
||||
export LIB_MODE="$(LIB_MODE)"; \
|
||||
export ROCKSDB_CXX_STANDARD="$(ROCKSDB_CXX_STANDARD)"; \
|
||||
export USE_FOLLY="$(USE_FOLLY)"; \
|
||||
"$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
# this file is generated by the previous line to set build flags and sources
|
||||
include make_config.mk
|
||||
|
||||
# Figure out optimize level.
|
||||
ifneq ($(DEBUG_LEVEL), 2)
|
||||
ifeq ($(LITE), 0)
|
||||
OPTIMIZE_LEVEL ?= -O2
|
||||
else
|
||||
OPTIMIZE_LEVEL ?= -Os
|
||||
endif
|
||||
endif
|
||||
# `OPTIMIZE_LEVEL` is empty when the user does not set it and `DEBUG_LEVEL=2`.
|
||||
# In that case, the compiler default (`-O0` for gcc and clang) will be used.
|
||||
@@ -142,19 +136,6 @@ CXXFLAGS += $(PLATFORM_SHARED_CFLAGS) -DROCKSDB_DLL
|
||||
CFLAGS += $(PLATFORM_SHARED_CFLAGS) -DROCKSDB_DLL
|
||||
endif
|
||||
|
||||
GIT_COMMAND ?= git
|
||||
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
|
||||
|
||||
# if we're compiling for release, compile without debug code (-DNDEBUG)
|
||||
ifeq ($(DEBUG_LEVEL),0)
|
||||
OPT += -DNDEBUG
|
||||
@@ -196,16 +177,6 @@ ifeq ($(USE_LTO), 1)
|
||||
LDFLAGS += -flto -fuse-linker-plugin
|
||||
endif
|
||||
|
||||
# `COERCE_CONTEXT_SWITCH=1` will inject spurious wakeup and
|
||||
# random length of sleep or context switch at critical
|
||||
# points (e.g, before acquring db mutex) in RocksDB.
|
||||
# In this way, it coerces as many excution orders as possible in the hope of
|
||||
# exposing the problematic excution order
|
||||
COERCE_CONTEXT_SWITCH ?= 0
|
||||
ifeq ($(COERCE_CONTEXT_SWITCH), 1)
|
||||
OPT += -DCOERCE_CONTEXT_SWITCH
|
||||
endif
|
||||
|
||||
#-----------------------------------------------
|
||||
include src.mk
|
||||
|
||||
@@ -242,23 +213,33 @@ am__v_AR_1 =
|
||||
AM_LINK = $(AM_V_CCLD)$(CXX) -L. $(patsubst lib%.a, -l%, $(patsubst lib%.$(PLATFORM_SHARED_EXT), -l%, $^)) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
AM_SHARE = $(AM_V_CCLD) $(CXX) $(PLATFORM_SHARED_LDFLAGS)$@ -L. $(patsubst lib%.$(PLATFORM_SHARED_EXT), -l%, $^) $(EXEC_LDFLAGS) $(LDFLAGS) -o $@
|
||||
|
||||
# Detect what platform we're building on.
|
||||
# Export some common variables that might have been passed as Make variables
|
||||
# instead of environment variables.
|
||||
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
|
||||
export CXXFLAGS="$(EXTRA_CXXFLAGS)"; \
|
||||
export LDFLAGS="$(EXTRA_LDFLAGS)"; \
|
||||
export COMPILE_WITH_ASAN="$(COMPILE_WITH_ASAN)"; \
|
||||
export COMPILE_WITH_TSAN="$(COMPILE_WITH_TSAN)"; \
|
||||
export COMPILE_WITH_UBSAN="$(COMPILE_WITH_UBSAN)"; \
|
||||
export PORTABLE="$(PORTABLE)"; \
|
||||
export ROCKSDB_NO_FBCODE="$(ROCKSDB_NO_FBCODE)"; \
|
||||
export USE_CLANG="$(USE_CLANG)"; \
|
||||
export LIB_MODE="$(LIB_MODE)"; \
|
||||
"$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
# this file is generated by the previous line to set build flags and sources
|
||||
include make_config.mk
|
||||
|
||||
ROCKSDB_PLUGIN_MKS = $(foreach plugin, $(ROCKSDB_PLUGINS), plugin/$(plugin)/*.mk)
|
||||
include $(ROCKSDB_PLUGIN_MKS)
|
||||
ROCKSDB_PLUGIN_PROTO =ROCKSDB_NAMESPACE::ObjectLibrary\&, const std::string\&
|
||||
ROCKSDB_PLUGIN_SOURCES = $(foreach p, $(ROCKSDB_PLUGINS), $(foreach source, $($(p)_SOURCES), plugin/$(p)/$(source)))
|
||||
ROCKSDB_PLUGIN_HEADERS = $(foreach p, $(ROCKSDB_PLUGINS), $(foreach header, $($(p)_HEADERS), plugin/$(p)/$(header)))
|
||||
ROCKSDB_PLUGIN_LIBS = $(foreach p, $(ROCKSDB_PLUGINS), $(foreach lib, $($(p)_LIBS), -l$(lib)))
|
||||
ROCKSDB_PLUGIN_W_FUNCS = $(foreach p, $(ROCKSDB_PLUGINS), $(if $($(p)_FUNC), $(p)))
|
||||
ROCKSDB_PLUGIN_EXTERNS = $(foreach p, $(ROCKSDB_PLUGIN_W_FUNCS), int $($(p)_FUNC)($(ROCKSDB_PLUGIN_PROTO));)
|
||||
ROCKSDB_PLUGIN_BUILTINS = $(foreach p, $(ROCKSDB_PLUGIN_W_FUNCS), {\"$(p)\"\, $($(p)_FUNC)}\,)
|
||||
ROCKSDB_PLUGIN_LDFLAGS = $(foreach plugin, $(ROCKSDB_PLUGINS), $($(plugin)_LDFLAGS))
|
||||
ROCKSDB_PLUGIN_SOURCES = $(foreach plugin, $(ROCKSDB_PLUGINS), $(foreach source, $($(plugin)_SOURCES), plugin/$(plugin)/$(source)))
|
||||
ROCKSDB_PLUGIN_HEADERS = $(foreach plugin, $(ROCKSDB_PLUGINS), $(foreach header, $($(plugin)_HEADERS), plugin/$(plugin)/$(header)))
|
||||
ROCKSDB_PLUGIN_PKGCONFIG_REQUIRES = $(foreach plugin, $(ROCKSDB_PLUGINS), $($(plugin)_PKGCONFIG_REQUIRES))
|
||||
ROCKSDB_PLUGIN_TESTS = $(foreach p, $(ROCKSDB_PLUGINS), $(foreach test, $($(p)_TESTS), plugin/$(p)/$(test)))
|
||||
|
||||
CXXFLAGS += $(foreach plugin, $(ROCKSDB_PLUGINS), $($(plugin)_CXXFLAGS))
|
||||
PLATFORM_LDFLAGS += $(ROCKSDB_PLUGIN_LDFLAGS)
|
||||
|
||||
# Patch up the link flags for JNI from the plugins
|
||||
ROCKSDB_PLUGIN_LDFLAGS = $(foreach plugin, $(ROCKSDB_PLUGINS), $($(plugin)_LDFLAGS))
|
||||
PLATFORM_LDFLAGS += $(ROCKSDB_PLUGIN_LDFLAGS)
|
||||
JAVA_LDFLAGS += $(ROCKSDB_PLUGIN_LDFLAGS)
|
||||
JAVA_STATIC_LDFLAGS += $(ROCKSDB_PLUGIN_LDFLAGS)
|
||||
|
||||
@@ -301,7 +282,7 @@ missing_make_config_paths := $(shell \
|
||||
grep "\./\S*\|/\S*" -o $(CURDIR)/make_config.mk | \
|
||||
while read path; \
|
||||
do [ -e $$path ] || echo $$path; \
|
||||
done | sort | uniq | grep -v "/DOES/NOT/EXIST")
|
||||
done | sort | uniq)
|
||||
|
||||
$(foreach path, $(missing_make_config_paths), \
|
||||
$(warning Warning: $(path) does not exist))
|
||||
@@ -324,6 +305,13 @@ endif
|
||||
ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
PLATFORM_CXXFLAGS += -D _GLIBCXX_USE_C99
|
||||
endif
|
||||
ifneq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
# found
|
||||
CFLAGS += -fno-exceptions
|
||||
CXXFLAGS += -fno-exceptions
|
||||
# LUA is not supported under ROCKSDB_LITE
|
||||
LUA_PATH =
|
||||
endif
|
||||
|
||||
ifeq ($(LIB_MODE),shared)
|
||||
# So that binaries are executable from build location, in addition to install location
|
||||
@@ -337,8 +325,8 @@ ifneq ($(MACHINE), arm64)
|
||||
# linking with jemalloc (as it won't be arm64-compatible) and remove some other options
|
||||
# set during platform detection
|
||||
DISABLE_JEMALLOC=1
|
||||
PLATFORM_CCFLAGS := $(filter-out -march=native, $(PLATFORM_CCFLAGS))
|
||||
PLATFORM_CXXFLAGS := $(filter-out -march=native, $(PLATFORM_CXXFLAGS))
|
||||
PLATFORM_CFLAGS := $(filter-out -march=native -DHAVE_SSE42 -DHAVE_AVX2, $(PLATFORM_CFLAGS))
|
||||
PLATFORM_CXXFLAGS := $(filter-out -march=native -DHAVE_SSE42 -DHAVE_AVX2, $(PLATFORM_CXXFLAGS))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -346,8 +334,6 @@ endif
|
||||
# ASAN doesn't work well with jemalloc. If we're compiling with ASAN, we should use regular malloc.
|
||||
ifdef COMPILE_WITH_ASAN
|
||||
DISABLE_JEMALLOC=1
|
||||
ASAN_OPTIONS?=detect_stack_use_after_return=1
|
||||
export ASAN_OPTIONS
|
||||
EXEC_LDFLAGS += -fsanitize=address
|
||||
PLATFORM_CCFLAGS += -fsanitize=address
|
||||
PLATFORM_CXXFLAGS += -fsanitize=address
|
||||
@@ -408,14 +394,6 @@ ifndef DISABLE_JEMALLOC
|
||||
ifdef JEMALLOC
|
||||
PLATFORM_CXXFLAGS += -DROCKSDB_JEMALLOC -DJEMALLOC_NO_DEMANGLE
|
||||
PLATFORM_CCFLAGS += -DROCKSDB_JEMALLOC -DJEMALLOC_NO_DEMANGLE
|
||||
ifeq ($(USE_FOLLY),1)
|
||||
PLATFORM_CXXFLAGS += -DUSE_JEMALLOC
|
||||
PLATFORM_CCFLAGS += -DUSE_JEMALLOC
|
||||
endif
|
||||
ifeq ($(USE_FOLLY_LITE),1)
|
||||
PLATFORM_CXXFLAGS += -DUSE_JEMALLOC
|
||||
PLATFORM_CCFLAGS += -DUSE_JEMALLOC
|
||||
endif
|
||||
endif
|
||||
ifdef WITH_JEMALLOC_FLAG
|
||||
PLATFORM_LDFLAGS += -ljemalloc
|
||||
@@ -426,8 +404,8 @@ ifndef DISABLE_JEMALLOC
|
||||
PLATFORM_CCFLAGS += $(JEMALLOC_INCLUDE)
|
||||
endif
|
||||
|
||||
ifndef USE_FOLLY
|
||||
USE_FOLLY=0
|
||||
ifndef USE_FOLLY_DISTRIBUTED_MUTEX
|
||||
USE_FOLLY_DISTRIBUTED_MUTEX=0
|
||||
endif
|
||||
|
||||
ifndef GTEST_THROW_ON_FAILURE
|
||||
@@ -447,58 +425,7 @@ else
|
||||
PLATFORM_CXXFLAGS += -isystem $(GTEST_DIR)
|
||||
endif
|
||||
|
||||
# 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
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
FOLLY_DIR = ./third-party/folly
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
@@ -508,10 +435,6 @@ ifeq ($(USE_FOLLY_LITE),1)
|
||||
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
|
||||
@@ -539,8 +462,7 @@ endif
|
||||
|
||||
ifdef USE_CLANG
|
||||
# Used by some teams in Facebook
|
||||
WARNING_FLAGS += -Wshift-sign-overflow -Wambiguous-reversed-operator \
|
||||
-Wimplicit-fallthrough -Wreinterpret-base-class -Wundefined-reinterpret-cast
|
||||
WARNING_FLAGS += -Wshift-sign-overflow
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
@@ -599,7 +521,7 @@ LIB_OBJECTS += $(patsubst %.c, $(OBJ_DIR)/%.o, $(LIB_SOURCES_C))
|
||||
LIB_OBJECTS += $(patsubst %.S, $(OBJ_DIR)/%.o, $(LIB_SOURCES_ASM))
|
||||
endif
|
||||
|
||||
ifeq ($(USE_FOLLY_LITE),1)
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
LIB_OBJECTS += $(patsubst %.cpp, $(OBJ_DIR)/%.o, $(FOLLY_SOURCES))
|
||||
endif
|
||||
|
||||
@@ -629,12 +551,15 @@ STRESS_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(STRESS_LIB_SOURCES))
|
||||
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) $(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)
|
||||
ALL_SOURCES += $(ROCKSDB_PLUGIN_SOURCES)
|
||||
|
||||
PLUGIN_TESTS = $(patsubst %.cc, %, $(notdir $(ROCKSDB_PLUGIN_TESTS)))
|
||||
TESTS = $(patsubst %.cc, %, $(notdir $(TEST_MAIN_SOURCES)))
|
||||
TESTS += $(patsubst %.c, %, $(notdir $(TEST_MAIN_SOURCES_C)))
|
||||
TESTS += $(PLUGIN_TESTS)
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
TESTS += folly_synchronization_distributed_mutex_test
|
||||
ALL_SOURCES += third-party/folly/folly/synchronization/test/DistributedMutexTest.cc
|
||||
endif
|
||||
|
||||
# `make check-headers` to very that each header file includes its own
|
||||
# dependencies
|
||||
@@ -642,7 +567,7 @@ 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 := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|lua/|range_tree/')
|
||||
DEV_HEADERS := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | egrep -v 'port/|plugin/|lua/|range_tree/')
|
||||
else
|
||||
DEV_HEADERS :=
|
||||
endif
|
||||
@@ -660,6 +585,9 @@ am__v_CCH_1 =
|
||||
check-headers: $(HEADER_OK_FILES)
|
||||
|
||||
# options_settable_test doesn't pass with UBSAN as we use hack in the test
|
||||
ifdef COMPILE_WITH_UBSAN
|
||||
TESTS := $(shell echo $(TESTS) | sed 's/\boptions_settable_test\b//g')
|
||||
endif
|
||||
ifdef ASSERT_STATUS_CHECKED
|
||||
# TODO: finish fixing all tests to pass this check
|
||||
TESTS_FAILING_ASC = \
|
||||
@@ -679,14 +607,10 @@ ROCKSDBTESTS_SUBSET ?= $(TESTS)
|
||||
# env_test - suspicious use of test::TmpDir
|
||||
# deletefile_test - serial because it generates giant temporary files in
|
||||
# its various tests. Parallel can fill up your /dev/shm
|
||||
# db_bloom_filter_test - serial because excessive space usage by instances
|
||||
# of DBFilterConstructionReserveMemoryTestWithParam can fill up /dev/shm
|
||||
NON_PARALLEL_TEST = \
|
||||
c_test \
|
||||
env_test \
|
||||
deletefile_test \
|
||||
db_bloom_filter_test \
|
||||
$(PLUGIN_TESTS) \
|
||||
|
||||
PARALLEL_TEST = $(filter-out $(NON_PARALLEL_TEST), $(TESTS))
|
||||
|
||||
@@ -695,6 +619,7 @@ TESTS_PLATFORM_DEPENDENT := \
|
||||
db_basic_test \
|
||||
db_blob_basic_test \
|
||||
db_encryption_test \
|
||||
db_test2 \
|
||||
external_sst_file_basic_test \
|
||||
auto_roll_logger_test \
|
||||
bloom_test \
|
||||
@@ -716,6 +641,7 @@ TESTS_PLATFORM_DEPENDENT := \
|
||||
rate_limiter_test \
|
||||
perf_context_test \
|
||||
iostats_context_test \
|
||||
db_wal_test \
|
||||
|
||||
# Sort ROCKSDBTESTS_SUBSET for filtering, except db_test is special (expensive)
|
||||
# so is placed first (out-of-order)
|
||||
@@ -780,9 +706,9 @@ TOOLS_LIBRARY=$(STATIC_TOOLS_LIBRARY)
|
||||
endif
|
||||
STRESS_LIBRARY=$(STATIC_STRESS_LIBRARY)
|
||||
|
||||
ROCKSDB_MAJOR = $(shell grep -E "ROCKSDB_MAJOR.[0-9]" include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
ROCKSDB_MINOR = $(shell grep -E "ROCKSDB_MINOR.[0-9]" include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
ROCKSDB_PATCH = $(shell grep -E "ROCKSDB_PATCH.[0-9]" include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
ROCKSDB_MAJOR = $(shell egrep "ROCKSDB_MAJOR.[0-9]" include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
ROCKSDB_MINOR = $(shell egrep "ROCKSDB_MINOR.[0-9]" include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
ROCKSDB_PATCH = $(shell egrep "ROCKSDB_PATCH.[0-9]" include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
|
||||
# If NO_UPDATE_BUILD_VERSION is set we don't update util/build_version.cc, but
|
||||
# the file needs to already exist or else the build will fail
|
||||
@@ -802,7 +728,7 @@ else
|
||||
git_mod := $(shell git diff-index HEAD --quiet 2>/dev/null; echo $$?)
|
||||
git_date := $(shell git log -1 --date=format:"%Y-%m-%d %T" --format="%ad" 2>/dev/null)
|
||||
endif
|
||||
gen_build_version = sed -e s/@GIT_SHA@/$(git_sha)/ -e s:@GIT_TAG@:"$(git_tag)": -e s/@GIT_MOD@/"$(git_mod)"/ -e s/@BUILD_DATE@/"$(build_date)"/ -e s/@GIT_DATE@/"$(git_date)"/ -e s/@ROCKSDB_PLUGIN_BUILTINS@/'$(ROCKSDB_PLUGIN_BUILTINS)'/ -e s/@ROCKSDB_PLUGIN_EXTERNS@/"$(ROCKSDB_PLUGIN_EXTERNS)"/ util/build_version.cc.in
|
||||
gen_build_version = sed -e s/@GIT_SHA@/$(git_sha)/ -e s:@GIT_TAG@:"$(git_tag)": -e s/@GIT_MOD@/"$(git_mod)"/ -e s/@BUILD_DATE@/"$(build_date)"/ -e s/@GIT_DATE@/"$(git_date)"/ util/build_version.cc.in
|
||||
|
||||
# Record the version of the source that we are compiling.
|
||||
# We keep a record of the git revision in this file. It is then built
|
||||
@@ -856,10 +782,17 @@ $(SHARED4): $(LIB_OBJECTS)
|
||||
$(AM_V_CCLD) $(CXX) $(PLATFORM_SHARED_LDFLAGS)$(SHARED3) $(LIB_OBJECTS) $(LDFLAGS) -o $@
|
||||
endif # PLATFORM_SHARED_EXT
|
||||
|
||||
.PHONY: check clean coverage ldb_tests package dbg gen-pc build_size \
|
||||
release tags tags0 valgrind_check format static_lib shared_lib all \
|
||||
rocksdbjavastatic rocksdbjava install install-static install-shared \
|
||||
uninstall analyze tools tools_lib check-headers checkout_folly
|
||||
.PHONY: blackbox_crash_test check clean coverage crash_test ldb_tests package \
|
||||
release tags tags0 valgrind_check whitebox_crash_test format static_lib shared_lib all \
|
||||
dbg rocksdbjavastatic rocksdbjava gen-pc install install-static install-shared uninstall \
|
||||
analyze tools tools_lib check-headers \
|
||||
blackbox_crash_test_with_atomic_flush whitebox_crash_test_with_atomic_flush \
|
||||
blackbox_crash_test_with_txn whitebox_crash_test_with_txn \
|
||||
blackbox_crash_test_with_best_efforts_recovery \
|
||||
blackbox_crash_test_with_ts whitebox_crash_test_with_ts \
|
||||
blackbox_crash_test_with_multiops_wc_txn \
|
||||
blackbox_crash_test_with_multiops_wp_txn
|
||||
|
||||
|
||||
all: $(LIBRARY) $(BENCHMARKS) tools tools_lib test_libs $(TESTS)
|
||||
|
||||
@@ -893,8 +826,20 @@ release: clean
|
||||
coverage: clean
|
||||
COVERAGEFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS+="-lgcov" $(MAKE) J=1 all check
|
||||
cd coverage && ./coverage_test.sh
|
||||
# Delete intermediate files
|
||||
$(FIND) . -type f \( -name "*.gcda" -o -name "*.gcno" \) -exec rm -f {} \;
|
||||
# Delete intermediate files
|
||||
$(FIND) . -type f -regex ".*\.\(\(gcda\)\|\(gcno\)\)" -exec rm -f {} \;
|
||||
|
||||
ifneq (,$(filter check parallel_check,$(MAKECMDGOALS)),)
|
||||
# Use /dev/shm if it has the sticky bit set (otherwise, /tmp),
|
||||
# and create a randomly-named rocksdb.XXXX directory therein.
|
||||
# We'll use that directory in the "make check" rules.
|
||||
ifeq ($(TMPD),)
|
||||
TMPDIR := $(shell echo $${TMPDIR:-/tmp})
|
||||
TMPD := $(shell f=/dev/shm; test -k $$f || f=$(TMPDIR); \
|
||||
perl -le 'use File::Temp "tempdir";' \
|
||||
-e 'print tempdir("'$$f'/rocksdb.XXXX", CLEANUP => 0)')
|
||||
endif
|
||||
endif
|
||||
|
||||
# Run all tests in parallel, accumulating per-test logs in t/log-*.
|
||||
#
|
||||
@@ -935,7 +880,7 @@ $(parallel_tests):
|
||||
TEST_SCRIPT=t/run-$$TEST_BINARY-$${TEST_NAME//\//-}; \
|
||||
printf '%s\n' \
|
||||
'#!/bin/sh' \
|
||||
"d=\$(TEST_TMPDIR)$$TEST_SCRIPT" \
|
||||
"d=\$(TMPD)$$TEST_SCRIPT" \
|
||||
'mkdir -p $$d' \
|
||||
"TEST_TMPDIR=\$$d $(DRIVER) ./$$TEST_BINARY --gtest_filter=$$TEST_NAME" \
|
||||
> $$TEST_SCRIPT; \
|
||||
@@ -995,7 +940,8 @@ endif
|
||||
|
||||
.PHONY: check_0
|
||||
check_0:
|
||||
@printf '%s\n' '' \
|
||||
$(AM_V_GEN)export TEST_TMPDIR=$(TMPD); \
|
||||
printf '%s\n' '' \
|
||||
'To monitor subtest <duration,pass/fail,name>,' \
|
||||
' run "make watch-log" in a separate window' ''; \
|
||||
{ \
|
||||
@@ -1005,8 +951,7 @@ check_0:
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| grep -E -v '$(EXCLUDE_TESTS_REGEX)' \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG --eta --gnu \
|
||||
--tmpdir=$(TEST_TMPDIR) '{} $(parallel_redir)' ; \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG --eta --gnu '{} $(parallel_redir)' ; \
|
||||
parallel_retcode=$$? ; \
|
||||
awk '{ if ($$7 != 0 || $$8 != 0) { if ($$7 == "Exitval") { h = $$0; } else { if (!f) print h; print; f = 1 } } } END { if(f) exit 1; }' < LOG ; \
|
||||
awk_retcode=$$?; \
|
||||
@@ -1017,7 +962,8 @@ valgrind-exclude-regexp = InlineSkipTest.ConcurrentInsert|TransactionStressTest.
|
||||
.PHONY: valgrind_check_0
|
||||
valgrind_check_0: test_log_prefix := valgrind_
|
||||
valgrind_check_0:
|
||||
@printf '%s\n' '' \
|
||||
$(AM_V_GEN)export TEST_TMPDIR=$(TMPD); \
|
||||
printf '%s\n' '' \
|
||||
'To monitor subtest <duration,pass/fail,name>,' \
|
||||
' run "make watch-log" in a separate window' ''; \
|
||||
{ \
|
||||
@@ -1028,11 +974,10 @@ valgrind_check_0:
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| grep -E -v '$(valgrind-exclude-regexp)' \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG --eta --gnu \
|
||||
--tmpdir=$(TEST_TMPDIR) \
|
||||
'(if [[ "{}" == "./"* ]] ; then $(DRIVER) {}; else {}; fi) \
|
||||
'(if [[ "{}" == "./"* ]] ; then $(DRIVER) {}; else {}; fi) \
|
||||
$(parallel_redir)' \
|
||||
|
||||
CLEAN_FILES += t LOG $(TEST_TMPDIR)
|
||||
CLEAN_FILES += t LOG $(TMPD)
|
||||
|
||||
# When running parallel "make check", you can monitor its progress
|
||||
# from another window.
|
||||
@@ -1055,19 +1000,21 @@ check: all
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
$(MAKE) T="$$t" check_0; \
|
||||
$(MAKE) T="$$t" TMPD=$(TMPD) check_0; \
|
||||
else \
|
||||
for t in $(TESTS); do \
|
||||
echo "===== Running $$t (`date`)"; ./$$t || exit 1; done; \
|
||||
fi
|
||||
rm -rf $(TEST_TMPDIR)
|
||||
rm -rf $(TMPD)
|
||||
ifneq ($(PLATFORM), OS_AIX)
|
||||
$(PYTHON) tools/check_all_python.py
|
||||
ifeq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
ifndef ASSERT_STATUS_CHECKED # not yet working with these tests
|
||||
$(PYTHON) tools/ldb_test.py
|
||||
sh tools/rocksdb_dump_test.sh
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
ifndef SKIP_FORMAT_BUCK_CHECKS
|
||||
$(MAKE) check-format
|
||||
$(MAKE) check-buck-targets
|
||||
@@ -1085,31 +1032,31 @@ ldb_tests: ldb
|
||||
include crash_test.mk
|
||||
|
||||
asan_check: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) check -j32
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) check -j32
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
whitebox_asan_crash_test: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) whitebox_crash_test
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) whitebox_crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
blackbox_asan_crash_test: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) blackbox_crash_test
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) blackbox_crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_atomic_flush: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_atomic_flush
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_atomic_flush
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_txn: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_txn
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_txn
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_best_efforts_recovery: clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_best_efforts_recovery
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_best_efforts_recovery
|
||||
$(MAKE) clean
|
||||
|
||||
ubsan_check: clean
|
||||
@@ -1155,11 +1102,11 @@ valgrind_test_some:
|
||||
valgrind_check: $(TESTS)
|
||||
$(MAKE) DRIVER="$(VALGRIND_VER) $(VALGRIND_OPTS)" gen_parallel_tests
|
||||
$(AM_V_GEN)if test "$(J)" != 1 \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
$(MAKE) \
|
||||
DRIVER="$(VALGRIND_VER) $(VALGRIND_OPTS)" valgrind_check_0; \
|
||||
$(MAKE) TMPD=$(TMPD) \
|
||||
DRIVER="$(VALGRIND_VER) $(VALGRIND_OPTS)" valgrind_check_0; \
|
||||
else \
|
||||
for t in $(filter-out %skiplist_test options_settable_test,$(TESTS)); do \
|
||||
$(VALGRIND_VER) $(VALGRIND_OPTS) ./$$t; \
|
||||
@@ -1179,6 +1126,27 @@ valgrind_check_some: $(ROCKSDBTESTS_SUBSET)
|
||||
fi; \
|
||||
done
|
||||
|
||||
ifneq ($(PAR_TEST),)
|
||||
parloop:
|
||||
ret_bad=0; \
|
||||
for t in $(PAR_TEST); do \
|
||||
echo "===== Running $$t in parallel $(NUM_PAR) (`date`)";\
|
||||
if [ $(db_test) -eq 1 ]; then \
|
||||
seq $(J) | v="$$t" build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{}; export TEST_TMPDIR=$$s;' \
|
||||
'timeout 2m ./db_test --gtest_filter=$$v >> $$s/log-{} 2>1'; \
|
||||
else\
|
||||
seq $(J) | v="./$$t" build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{};' \
|
||||
'export TEST_TMPDIR=$$s; timeout 10m $$v >> $$s/log-{} 2>1'; \
|
||||
fi; \
|
||||
ret_code=$$?; \
|
||||
if [ $$ret_code -ne 0 ]; then \
|
||||
ret_bad=$$ret_code; \
|
||||
echo $$t exited with $$ret_code; \
|
||||
fi; \
|
||||
done; \
|
||||
exit $$ret_bad;
|
||||
endif
|
||||
|
||||
test_names = \
|
||||
./db_test --gtest_list_tests \
|
||||
| perl -n \
|
||||
@@ -1186,6 +1154,24 @@ test_names = \
|
||||
-e '/^(\s*)(\S+)/; !$$1 and do {$$p=$$2; break};' \
|
||||
-e 'print qq! $$p$$2!'
|
||||
|
||||
parallel_check: $(TESTS)
|
||||
$(AM_V_GEN)if test "$(J)" > 1 \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
echo Running in parallel $(J); \
|
||||
else \
|
||||
echo "Need to have GNU Parallel and J > 1"; exit 1; \
|
||||
fi; \
|
||||
ret_bad=0; \
|
||||
echo $(J);\
|
||||
echo Test Dir: $(TMPD); \
|
||||
seq $(J) | build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{}; rm -rf $$s; mkdir $$s'; \
|
||||
$(MAKE) PAR_TEST="$(shell $(test_names))" TMPD=$(TMPD) \
|
||||
J=$(J) db_test=1 parloop; \
|
||||
$(MAKE) PAR_TEST="$(filter-out db_test, $(TESTS))" \
|
||||
TMPD=$(TMPD) J=$(J) db_test=0 parloop;
|
||||
|
||||
analyze: clean
|
||||
USE_CLANG=1 $(MAKE) analyze_incremental
|
||||
|
||||
@@ -1223,14 +1209,14 @@ clean: clean-ext-libraries-all clean-rocks clean-rocksjava
|
||||
clean-not-downloaded: clean-ext-libraries-bin clean-rocks clean-not-downloaded-rocksjava
|
||||
|
||||
clean-rocks:
|
||||
# Not practical to exactly match all versions/variants in naming (e.g. debug or not)
|
||||
rm -f ${LIBNAME}*.so* ${LIBNAME}*.a
|
||||
rm -f $(BENCHMARKS) $(TOOLS) $(TESTS) $(PARALLEL_TEST) $(MICROBENCHS)
|
||||
echo shared=$(ALL_SHARED_LIBS)
|
||||
echo static=$(ALL_STATIC_LIBS)
|
||||
rm -f $(BENCHMARKS) $(TOOLS) $(TESTS) $(PARALLEL_TEST) $(ALL_STATIC_LIBS) $(ALL_SHARED_LIBS) $(MICROBENCHS)
|
||||
rm -rf $(CLEAN_FILES) ios-x86 ios-arm scan_build_report
|
||||
$(FIND) . -name "*.[oda]" -exec rm -f {} \;
|
||||
$(FIND) . -type f \( -name "*.gcda" -o -name "*.gcno" \) -exec rm -f {} \;
|
||||
$(FIND) . -type f -regex ".*\.\(\(gcda\)\|\(gcno\)\)" -exec rm -f {} \;
|
||||
|
||||
clean-rocksjava: clean-rocks
|
||||
clean-rocksjava:
|
||||
rm -rf jl jls
|
||||
cd java && $(MAKE) clean
|
||||
|
||||
@@ -1314,6 +1300,11 @@ trace_analyzer: $(OBJ_DIR)/tools/trace_analyzer.o $(ANALYZE_OBJECTS) $(TOOLS_LIB
|
||||
block_cache_trace_analyzer: $(OBJ_DIR)/tools/block_cache_analyzer/block_cache_trace_analyzer_tool.o $(ANALYZE_OBJECTS) $(TOOLS_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
folly_synchronization_distributed_mutex_test: $(OBJ_DIR)/third-party/folly/folly/synchronization/test/DistributedMutexTest.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
endif
|
||||
|
||||
cache_bench: $(OBJ_DIR)/cache/cache_bench.o $(CACHE_BENCH_OBJECTS) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1338,14 +1329,6 @@ db_sanity_test: $(OBJ_DIR)/tools/db_sanity_test.o $(LIBRARY)
|
||||
db_repl_stress: $(OBJ_DIR)/tools/db_repl_stress.o $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
define MakeTestRule
|
||||
$(notdir $(1:%.cc=%)): $(1:%.cc=$$(OBJ_DIR)/%.o) $$(TEST_LIBRARY) $$(LIBRARY)
|
||||
$$(AM_LINK)
|
||||
endef
|
||||
|
||||
# For each PLUGIN test, create a rule to generate the test executable
|
||||
$(foreach test, $(ROCKSDB_PLUGIN_TESTS), $(eval $(call MakeTestRule, $(test))))
|
||||
|
||||
arena_test: $(OBJ_DIR)/memory/arena_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1388,9 +1371,6 @@ ribbon_test: $(OBJ_DIR)/util/ribbon_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
option_change_migration_test: $(OBJ_DIR)/utilities/option_change_migration/option_change_migration_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
agg_merge_test: $(OBJ_DIR)/utilities/agg_merge/agg_merge_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
stringappend_test: $(OBJ_DIR)/utilities/merge_operators/string_append/stringappend_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1418,9 +1398,6 @@ thread_local_test: $(OBJ_DIR)/util/thread_local_test.o $(TEST_LIBRARY) $(LIBRARY
|
||||
work_queue_test: $(OBJ_DIR)/util/work_queue_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
udt_util_test: $(OBJ_DIR)/util/udt_util_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
corruption_test: $(OBJ_DIR)/db/corruption_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1442,12 +1419,6 @@ db_blob_basic_test: $(OBJ_DIR)/db/blob/db_blob_basic_test.o $(TEST_LIBRARY) $(LI
|
||||
db_blob_compaction_test: $(OBJ_DIR)/db/blob/db_blob_compaction_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_readonly_with_timestamp_test: $(OBJ_DIR)/db/db_readonly_with_timestamp_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_wide_basic_test: $(OBJ_DIR)/db/wide/db_wide_basic_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_with_timestamp_basic_test: $(OBJ_DIR)/db/db_with_timestamp_basic_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1484,9 +1455,6 @@ db_compaction_filter_test: $(OBJ_DIR)/db/db_compaction_filter_test.o $(TEST_LIBR
|
||||
db_compaction_test: $(OBJ_DIR)/db/db_compaction_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_clip_test: $(OBJ_DIR)/db/db_clip_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_dynamic_level_test: $(OBJ_DIR)/db/db_dynamic_level_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1568,9 +1536,6 @@ db_table_properties_test: $(OBJ_DIR)/db/db_table_properties_test.o $(TEST_LIBRAR
|
||||
log_write_bench: $(OBJ_DIR)/util/log_write_bench.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK) $(PROFILING_FLAGS)
|
||||
|
||||
seqno_time_test: $(OBJ_DIR)/db/seqno_time_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
plain_table_db_test: $(OBJ_DIR)/db/plain_table_db_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1586,7 +1551,7 @@ perf_context_test: $(OBJ_DIR)/db/perf_context_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
prefix_test: $(OBJ_DIR)/db/prefix_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
backup_engine_test: $(OBJ_DIR)/utilities/backup/backup_engine_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
backupable_db_test: $(OBJ_DIR)/utilities/backupable/backupable_db_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
checkpoint_test: $(OBJ_DIR)/utilities/checkpoint/checkpoint_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
@@ -1667,6 +1632,9 @@ random_access_file_reader_test: $(OBJ_DIR)/file/random_access_file_reader_test.o
|
||||
file_reader_writer_test: $(OBJ_DIR)/util/file_reader_writer_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
block_based_filter_block_test: $(OBJ_DIR)/table/block_based/block_based_filter_block_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
block_based_table_reader_test: table/block_based/block_based_table_reader_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1769,9 +1737,6 @@ cuckoo_table_db_test: $(OBJ_DIR)/db/cuckoo_table_db_test.o $(TEST_LIBRARY) $(LIB
|
||||
listener_test: $(OBJ_DIR)/db/listener_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
string_util_test: $(OBJ_DIR)/util/string_util_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
thread_list_test: $(OBJ_DIR)/util/thread_list_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1832,7 +1797,7 @@ memtable_list_test: $(OBJ_DIR)/db/memtable_list_test.o $(TEST_LIBRARY) $(LIBRARY
|
||||
write_callback_test: $(OBJ_DIR)/db/write_callback_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
heap_test: $(OBJ_DIR)/util/heap_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
heap_test: $(OBJ_DIR)/util/heap_test.o $(GTEST)
|
||||
$(AM_LINK)
|
||||
|
||||
point_lock_manager_test: utilities/transactions/lock/point/point_lock_manager_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
@@ -1850,12 +1815,6 @@ write_prepared_transaction_test: $(OBJ_DIR)/utilities/transactions/write_prepare
|
||||
write_unprepared_transaction_test: $(OBJ_DIR)/utilities/transactions/write_unprepared_transaction_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
timestamped_snapshot_test: $(OBJ_DIR)/utilities/transactions/timestamped_snapshot_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
tiered_compaction_test: $(OBJ_DIR)/db/compaction/tiered_compaction_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
sst_dump: $(OBJ_DIR)/tools/sst_dump.o $(TOOLS_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1883,15 +1842,12 @@ statistics_test: $(OBJ_DIR)/monitoring/statistics_test.o $(TEST_LIBRARY) $(LIBRA
|
||||
stats_history_test: $(OBJ_DIR)/monitoring/stats_history_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
compressed_secondary_cache_test: $(OBJ_DIR)/cache/compressed_secondary_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
lru_secondary_cache_test: $(OBJ_DIR)/cache/lru_secondary_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
lru_cache_test: $(OBJ_DIR)/cache/lru_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
tiered_secondary_cache_test: $(OBJ_DIR)/cache/tiered_secondary_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
range_del_aggregator_test: $(OBJ_DIR)/db/range_del_aggregator_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1943,16 +1899,13 @@ blob_file_garbage_test: $(OBJ_DIR)/db/blob/blob_file_garbage_test.o $(TEST_LIBRA
|
||||
blob_file_reader_test: $(OBJ_DIR)/db/blob/blob_file_reader_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_source_test: $(OBJ_DIR)/db/blob/blob_source_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_garbage_meter_test: $(OBJ_DIR)/db/blob/blob_garbage_meter_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
timer_test: $(OBJ_DIR)/util/timer_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
periodic_task_scheduler_test: $(OBJ_DIR)/db/periodic_task_scheduler_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
periodic_work_scheduler_test: $(OBJ_DIR)/db/periodic_work_scheduler_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
testutil_test: $(OBJ_DIR)/test_util/testutil_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
@@ -1961,7 +1914,7 @@ testutil_test: $(OBJ_DIR)/test_util/testutil_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
io_tracer_test: $(OBJ_DIR)/trace_replay/io_tracer_test.o $(OBJ_DIR)/trace_replay/io_tracer.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
prefetch_test: $(OBJ_DIR)/file/prefetch_test.o $(OBJ_DIR)/tools/io_tracer_parser_tool.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
prefetch_test: $(OBJ_DIR)/file/prefetch_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
io_tracer_parser_test: $(OBJ_DIR)/tools/io_tracer_parser_test.o $(OBJ_DIR)/tools/io_tracer_parser_tool.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
@@ -1987,13 +1940,6 @@ db_basic_bench: $(OBJ_DIR)/microbench/db_basic_bench.o $(LIBRARY)
|
||||
|
||||
cache_reservation_manager_test: $(OBJ_DIR)/cache/cache_reservation_manager_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
wide_column_serialization_test: $(OBJ_DIR)/db/wide/wide_column_serialization_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
wide_columns_helper_test: $(OBJ_DIR)/db/wide/wide_columns_helper_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
PREFIX ?= /usr/local
|
||||
@@ -2064,7 +2010,7 @@ JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
|
||||
ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
ARCH := $(shell isainfo -b)
|
||||
else ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
ifneq (,$(filter amd64 ppc64 ppc64le s390x arm64 aarch64 riscv64 sparc64 loongarch64, $(MACHINE)))
|
||||
ifneq (,$(filter amd64 ppc64 ppc64le s390x arm64 aarch64 sparc64, $(MACHINE)))
|
||||
ARCH := 64
|
||||
else
|
||||
ARCH := 32
|
||||
@@ -2085,7 +2031,7 @@ ifneq ($(origin JNI_LIBC), undefined)
|
||||
endif
|
||||
|
||||
ifeq (,$(ROCKSDBJNILIB))
|
||||
ifneq (,$(filter ppc% s390x arm64 aarch64 riscv64 sparc64 loongarch64, $(MACHINE)))
|
||||
ifneq (,$(filter ppc% s390x arm64 aarch64 sparc64, $(MACHINE)))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE)$(JNI_LIBC_POSTFIX).so
|
||||
else
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH)$(JNI_LIBC_POSTFIX).so
|
||||
@@ -2098,8 +2044,8 @@ ROCKSDB_JAVADOCS_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-javadoc.jar
|
||||
ROCKSDB_SOURCES_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-sources.jar
|
||||
SHA256_CMD = sha256sum
|
||||
|
||||
ZLIB_VER ?= 1.3.1
|
||||
ZLIB_SHA256 ?= 9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23
|
||||
ZLIB_VER ?= 1.2.11
|
||||
ZLIB_SHA256 ?= c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.8
|
||||
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
|
||||
@@ -2107,11 +2053,11 @@ BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
|
||||
SNAPPY_VER ?= 1.1.8
|
||||
SNAPPY_SHA256 ?= 16b677f07832a612b0836178db7f374e414f94657c138e6993cbfc5dcc58651f
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
LZ4_VER ?= 1.9.4
|
||||
LZ4_SHA256 ?= 0b0e3aa07c8c063ddf40b082bdf7e37a1562bda40a0ff5272957f3e987e0e54b
|
||||
LZ4_VER ?= 1.9.3
|
||||
LZ4_SHA256 ?= 030644df4611007ff7dc962d981f390361e6c97a34e5cbc393ddfbe019ffe2c1
|
||||
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
|
||||
ZSTD_VER ?= 1.5.5
|
||||
ZSTD_SHA256 ?= 98e9c3d949d1b924e28e01eccb7deed865eefebf25c2f21c702e5cd5b63b85e1
|
||||
ZSTD_VER ?= 1.4.9
|
||||
ZSTD_SHA256 ?= acf714d98e3db7b876e5b540cbf6dee298f60eb3c0723104f6d3f065cd60d6a8
|
||||
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
CURL_SSL_OPTS ?= --tlsv1
|
||||
|
||||
@@ -2156,7 +2102,6 @@ ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
ROCKSDBJNILIB = librocksdbjni-openbsd$(ARCH).so
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-openbsd$(ARCH).jar
|
||||
endif
|
||||
export SHA256_CMD
|
||||
|
||||
zlib-$(ZLIB_VER).tar.gz:
|
||||
curl --fail --output zlib-$(ZLIB_VER).tar.gz --location ${ZLIB_DOWNLOAD_BASE}/zlib-$(ZLIB_VER).tar.gz
|
||||
@@ -2289,7 +2234,7 @@ JAR_CMD := jar
|
||||
endif
|
||||
endif
|
||||
rocksdbjavastatic_javalib:
|
||||
cd java; $(MAKE) javalib
|
||||
cd java; SHA256_CMD='$(SHA256_CMD)' $(MAKE) javalib
|
||||
rm -f java/target/$(ROCKSDBJNILIB)
|
||||
$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC \
|
||||
-o ./java/target/$(ROCKSDBJNILIB) $(ALL_JNI_NATIVE_SOURCES) \
|
||||
@@ -2350,10 +2295,6 @@ rocksdbjavastaticdockers390x:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_s390x-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerriscv64:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_riscv64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerx86musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x86-musl-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
@@ -2407,7 +2348,7 @@ rocksdbjava: $(LIB_OBJECTS)
|
||||
ifeq ($(JAVA_HOME),)
|
||||
$(error JAVA_HOME is not set)
|
||||
endif
|
||||
$(AM_V_GEN)cd java; $(MAKE) javalib;
|
||||
$(AM_V_GEN)cd java; SHA256_CMD='$(SHA256_CMD)' $(MAKE) javalib;
|
||||
$(AM_V_at)rm -f ./java/target/$(ROCKSDBJNILIB)
|
||||
$(AM_V_at)$(CXX) $(CXXFLAGS) -I./java/. -I./java/rocksjni $(JAVA_INCLUDE) $(ROCKSDB_PLUGIN_JNI_CXX_INCLUDEFLAGS) -shared -fPIC -o ./java/target/$(ROCKSDBJNILIB) $(ALL_JNI_NATIVE_SOURCES) $(LIB_OBJECTS) $(JAVA_LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_V_at)cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
@@ -2419,79 +2360,22 @@ jclean:
|
||||
cd java;$(MAKE) clean;
|
||||
|
||||
jtest_compile: rocksdbjava
|
||||
cd java;$(MAKE) java_test
|
||||
cd java; SHA256_CMD='$(SHA256_CMD)' $(MAKE) java_test
|
||||
|
||||
jtest_run:
|
||||
cd java;$(MAKE) run_test
|
||||
|
||||
jtest: rocksdbjava
|
||||
cd java;$(MAKE) sample test
|
||||
|
||||
jpmd: rocksdbjava rocksdbjavageneratepom
|
||||
cd java;$(MAKE) pmd
|
||||
cd java;$(MAKE) sample; SHA256_CMD='$(SHA256_CMD)' $(MAKE) test;
|
||||
$(PYTHON) tools/check_all_python.py # TODO peterd: find a better place for this check in CI targets
|
||||
|
||||
jdb_bench:
|
||||
cd java;$(MAKE) db_bench;
|
||||
|
||||
commit_prereq:
|
||||
echo "TODO: bring this back using parts of old precommit_checker.py and rocksdb-lego-determinator"
|
||||
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
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
REPORT_BUILD_STATISTIC?=echo STATISTIC:
|
||||
|
||||
build_size:
|
||||
# === normal build, static ===
|
||||
$(MAKE) clean
|
||||
$(MAKE) static_lib
|
||||
$(REPORT_BUILD_STATISTIC) rocksdb.build_size.static_lib $$(stat --printf="%s" librocksdb.a)
|
||||
strip librocksdb.a
|
||||
$(REPORT_BUILD_STATISTIC) rocksdb.build_size.static_lib_stripped $$(stat --printf="%s" librocksdb.a)
|
||||
# === normal build, shared ===
|
||||
$(MAKE) clean
|
||||
$(MAKE) shared_lib
|
||||
$(REPORT_BUILD_STATISTIC) rocksdb.build_size.shared_lib $$(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
strip `readlink -f librocksdb.so`
|
||||
$(REPORT_BUILD_STATISTIC) rocksdb.build_size.shared_lib_stripped $$(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
commit_prereq: build_tools/rocksdb-lego-determinator \
|
||||
build_tools/precommit_checker.py
|
||||
J=$(J) build_tools/precommit_checker.py unit unit_481 clang_unit release release_481 clang_release tsan asan ubsan lite unit_non_shm
|
||||
$(MAKE) clean && $(MAKE) jclean && $(MAKE) rocksdbjava;
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Platform-specific compilation
|
||||
@@ -2545,7 +2429,7 @@ endif
|
||||
ifneq ($(SKIP_DEPENDS), 1)
|
||||
DEPFILES = $(patsubst %.cc, $(OBJ_DIR)/%.cc.d, $(ALL_SOURCES))
|
||||
DEPFILES+ = $(patsubst %.c, $(OBJ_DIR)/%.c.d, $(LIB_SOURCES_C) $(TEST_MAIN_SOURCES_C))
|
||||
ifeq ($(USE_FOLLY_LITE),1)
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
DEPFILES +=$(patsubst %.cpp, $(OBJ_DIR)/%.cpp.d, $(FOLLY_SOURCES))
|
||||
endif
|
||||
endif
|
||||
@@ -2593,7 +2477,7 @@ list_all_tests:
|
||||
|
||||
# Remove the rules for which dependencies should not be generated and see if any are left.
|
||||
#If so, include the dependencies; if not, do not include the dependency files
|
||||
ROCKS_DEP_RULES=$(filter-out clean format check-format check-buck-targets check-headers check-sources jclean jtest package analyze tags rocksdbjavastatic% unity.% unity_test checkout_folly, $(MAKECMDGOALS))
|
||||
ROCKS_DEP_RULES=$(filter-out clean format check-format check-buck-targets check-headers check-sources jclean jtest package analyze tags rocksdbjavastatic% unity.% unity_test, $(MAKECMDGOALS))
|
||||
ifneq ("$(ROCKS_DEP_RULES)", "")
|
||||
-include $(DEPFILES)
|
||||
endif
|
||||
|
||||
@@ -4,6 +4,3 @@ This is the list of all known third-party plugins for RocksDB. If something is m
|
||||
* [HDFS](https://github.com/riversand963/rocksdb-hdfs-env): an Env used for interacting with HDFS. Migrated from main RocksDB repo
|
||||
* [ZenFS](https://github.com/westerndigitalcorporation/zenfs): a file system for zoned block devices
|
||||
* [RADOS](https://github.com/riversand963/rocksdb-rados-env): an Env used for interacting with RADOS. Migrated from RocksDB main repo.
|
||||
* [PMEM](https://github.com/pmem/pmem-rocksdb-plugin): a collection of plugins to enable Persistent Memory on RocksDB.
|
||||
* [IPPCP](https://github.com/intel/ippcp-plugin-rocksdb): a plugin to enable encryption on RocksDB based on Intel optimized open source IPP-Crypto library.
|
||||
* [encfs](https://github.com/pegasus-kv/encfs): a plugin to enable encryption on RocksDB based on OpenSSL library.
|
||||
@@ -1,6 +1,9 @@
|
||||
## RocksDB: A Persistent Key-Value Store for Flash and RAM Storage
|
||||
|
||||
[](https://circleci.com/gh/facebook/rocksdb)
|
||||
[](https://travis-ci.com/github/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/main)
|
||||
[](http://140-211-168-68-openstack.osuosl.org:8080/job/rocksdb)
|
||||
|
||||
RocksDB is developed and maintained by Facebook Database Engineering Team.
|
||||
It is built on earlier work on [LevelDB](https://github.com/google/leveldb) by Sanjay Ghemawat (sanjay@google.com)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# RocksDBLite
|
||||
|
||||
RocksDBLite is a project focused on mobile use cases, which don't need a lot of fancy things we've built for server workloads and they are very sensitive to binary size. For that reason, we added a compile flag ROCKSDB_LITE that comments out a lot of the nonessential code and keeps the binary lean.
|
||||
|
||||
Some examples of the features disabled by ROCKSDB_LITE:
|
||||
* compiled-in support for LDB tool
|
||||
* No backupable DB
|
||||
* No support for replication (which we provide in form of TransactionalIterator)
|
||||
* No advanced monitoring tools
|
||||
* No special-purpose memtables that are highly optimized for specific use cases
|
||||
* No Transactions
|
||||
|
||||
When adding a new big feature to RocksDB, please add ROCKSDB_LITE compile guard if:
|
||||
* Nobody from mobile really needs your feature,
|
||||
* Your feature is adding a lot of weight to the binary.
|
||||
|
||||
Don't add ROCKSDB_LITE compile guard if:
|
||||
* It would introduce a lot of code complexity. Compile guards make code harder to read. It's a trade-off.
|
||||
* Your feature is not adding a lot of weight.
|
||||
|
||||
If unsure, ask. :)
|
||||
@@ -15,28 +15,6 @@ At Facebook, we use RocksDB as storage engines in multiple data management servi
|
||||
|
||||
[2] https://code.facebook.com/posts/357056558062811/logdevice-a-distributed-data-store-for-logs/
|
||||
|
||||
## Bilibili
|
||||
[Bilibili](bilibili.com) [uses](https://www.alluxio.io/blog/when-ai-meets-alluxio-at-bilibili-building-an-efficient-ai-platform-for-data-preprocessing-and-model-training/) Alluxio to speed up its ML training workloads, and Alluxio uses RocksDB to store its filesystem metadata, so Bilibili uses RocksDB.
|
||||
|
||||
Bilibili's [real-time platform](https://www.alibabacloud.com/blog/architecture-and-practices-of-bilibilis-real-time-platform_596676) uses Flink, and uses RocksDB as Flink's state store.
|
||||
|
||||
## TikTok
|
||||
TikTok, or its parent company ByteDance, uses RocksDB as the storage engine for some storage systems, such as its distributed graph database [ByteGraph](https://vldb.org/pvldb/vol15/p3306-li.pdf).
|
||||
|
||||
Also, TikTok uses [Alluxio](alluxio.io) to [speed up Presto queries](https://www.alluxio.io/resources/videos/improving-presto-performance-with-alluxio-at-tiktok/), and Alluxio stores the files' metadata in RocksDB.
|
||||
|
||||
## FoundationDB
|
||||
[FoundationDB](https://www.foundationdb.org/) [uses](https://github.com/apple/foundationdb/blob/377f1f692da6ab2fe5bdac57035651db3e5fb66d/fdbserver/KeyValueStoreRocksDB.actor.cpp) RocksDB to implement a [key-value store interface](https://github.com/apple/foundationdb/blob/377f1f692da6ab2fe5bdac57035651db3e5fb66d/fdbserver/KeyValueStoreRocksDB.actor.cpp#L1127) in its server backend.
|
||||
|
||||
## Apple
|
||||
Apple [uses](https://opensource.apple.com/projects/foundationdb/) FoundationDB, so it also uses RocksDB.
|
||||
|
||||
## Snowflake
|
||||
Snowflake [uses](https://www.snowflake.com/blog/how-foundationdb-powers-snowflake-metadata-forward/) FoundationDB, so it also uses RocksDB.
|
||||
|
||||
## Microsoft
|
||||
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
|
||||
Two different use cases at Linkedin are using RocksDB as a storage engine:
|
||||
|
||||
@@ -48,9 +26,6 @@ Learn more about those use cases in a Tech Talk by Ankit Gupta and Naveen Somasu
|
||||
## 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
|
||||
|
||||
## Tencent
|
||||
[PaxosStore](https://github.com/Tencent/paxosstore) is a distributed database supporting WeChat. It uses RocksDB as its storage engine.
|
||||
|
||||
## Baidu
|
||||
[Apache Doris](http://doris.apache.org/master/en/) is a MPP analytical database engine released by Baidu. It [uses RocksDB](http://doris.apache.org/master/en/administrator-guide/operation/tablet-meta-tool.html) to manage its tablet's metadata.
|
||||
|
||||
@@ -104,18 +79,6 @@ quasardb uses a heavily tuned RocksDB as its persistence layer.
|
||||
## TiKV
|
||||
[TiKV](https://github.com/pingcap/tikv) is a GEO-replicated, high-performance, distributed, transactional key-value database. TiKV is powered by Rust and Raft. TiKV uses RocksDB as its persistence layer.
|
||||
|
||||
## TiDB
|
||||
[TiDB](https://github.com/pingcap/tidb) uses the TiKV distributed key-value database, so it uses RocksDB.
|
||||
|
||||
## PingCAP
|
||||
[PingCAP](https://www.pingcap.com/) is the company behind TiDB, its cloud database service uses RocksDB.
|
||||
|
||||
## Apache Spark
|
||||
[Spark Structured Streaming](https://docs.databricks.com/structured-streaming/rocksdb-state-store.html) uses RocksDB as the local state store.
|
||||
|
||||
## Databricks
|
||||
[Databricks](https://www.databricks.com/) [replaces AWS RDS with TiDB](https://www.pingcap.com/case-study/how-databricks-tackles-the-scalability-limit-with-a-mysql-alternative/) for scalability, so it uses RocksDB.
|
||||
|
||||
## Apache Flink
|
||||
[Apache Flink](https://flink.apache.org/news/2016/03/08/release-1.0.0.html) uses RocksDB to store state locally on a machine.
|
||||
|
||||
@@ -152,9 +115,6 @@ LzLabs is using RocksDB as a storage engine in their multi-database distributed
|
||||
## ArangoDB
|
||||
[ArangoDB](https://www.arangodb.com/) is a native multi-model database with flexible data models for documents, graphs, and key-values, for building high performance applications using a convenient SQL-like query language or JavaScript extensions. It uses RocksDB as its storage engine.
|
||||
|
||||
## Qdrant
|
||||
[Qdrant](https://qdrant.tech/) is an open source vector database, it [uses](https://qdrant.tech/documentation/concepts/storage/) RocksDB as its persistent storage.
|
||||
|
||||
## Milvus
|
||||
[Milvus](https://milvus.io/) is an open source vector database for unstructured data. It uses RocksDB not only as one of the supported kv storage engines, but also as a message queue.
|
||||
|
||||
@@ -164,9 +124,5 @@ LzLabs is using RocksDB as a storage engine in their multi-database distributed
|
||||
## Solana Labs
|
||||
[Solana](https://github.com/solana-labs/solana) is a fast, secure, scalable, and decentralized blockchain. It uses RocksDB as the underlying storage for its ledger store.
|
||||
|
||||
## Apache Kvrocks
|
||||
|
||||
[Apache Kvrocks](https://github.com/apache/kvrocks) is an open-source distributed key-value NoSQL database built on top of RocksDB. It serves as a cost-saving and capacity-increasing alternative drop-in replacement for Redis.
|
||||
|
||||
## Others
|
||||
More databases using RocksDB can be found at [dbdb.io](https://dbdb.io/browse?embeds=rocksdb).
|
||||
|
||||
+6394
-6148
File diff suppressed because it is too large
Load Diff
+1651
-1579
File diff suppressed because it is too large
Load Diff
Executable → Regular
+65
-99
@@ -1,18 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
try:
|
||||
from builtins import str
|
||||
except ImportError:
|
||||
from __builtin__ import str
|
||||
import fnmatch
|
||||
from targets_builder import TARGETSBuilder
|
||||
import json
|
||||
import os
|
||||
import fnmatch
|
||||
import sys
|
||||
|
||||
from targets_builder import TARGETSBuilder
|
||||
|
||||
from util import ColorString
|
||||
|
||||
# This script generates TARGETS file for Buck.
|
||||
@@ -26,7 +26,7 @@ from util import ColorString
|
||||
# $python3 buckifier/buckify_rocksdb.py \
|
||||
# '{"fake": {
|
||||
# "extra_deps": [":test_dep", "//fakes/module:mock1"],
|
||||
# "extra_compiler_flags": ["-DFOO_BAR", "-Os"]
|
||||
# "extra_compiler_flags": ["-DROCKSDB_LITE", "-Os"]
|
||||
# }
|
||||
# }'
|
||||
# (Generated TARGETS file has test_dep and mock1 as dependencies for RocksDB
|
||||
@@ -43,13 +43,13 @@ def parse_src_mk(repo_path):
|
||||
src_files = {}
|
||||
for line in open(src_mk):
|
||||
line = line.strip()
|
||||
if len(line) == 0 or line[0] == "#":
|
||||
if len(line) == 0 or line[0] == '#':
|
||||
continue
|
||||
if "=" in line:
|
||||
current_src = line.split("=")[0].strip()
|
||||
if '=' in line:
|
||||
current_src = line.split('=')[0].strip()
|
||||
src_files[current_src] = []
|
||||
elif ".c" in line:
|
||||
src_path = line.split("\\")[0].strip()
|
||||
elif '.c' in line:
|
||||
src_path = line.split('\\')[0].strip()
|
||||
src_files[current_src].append(src_path)
|
||||
return src_files
|
||||
|
||||
@@ -57,16 +57,14 @@ def parse_src_mk(repo_path):
|
||||
# get all .cc / .c files
|
||||
def get_cc_files(repo_path):
|
||||
cc_files = []
|
||||
for root, _dirnames, filenames in os.walk(
|
||||
repo_path
|
||||
): # noqa: B007 T25377293 Grandfathered in
|
||||
root = root[(len(repo_path) + 1) :]
|
||||
for root, dirnames, filenames in os.walk(repo_path): # noqa: B007 T25377293 Grandfathered in
|
||||
root = root[(len(repo_path) + 1):]
|
||||
if "java" in root:
|
||||
# Skip java
|
||||
continue
|
||||
for filename in fnmatch.filter(filenames, "*.cc"):
|
||||
for filename in fnmatch.filter(filenames, '*.cc'):
|
||||
cc_files.append(os.path.join(root, filename))
|
||||
for filename in fnmatch.filter(filenames, "*.c"):
|
||||
for filename in fnmatch.filter(filenames, '*.c'):
|
||||
cc_files.append(os.path.join(root, filename))
|
||||
return cc_files
|
||||
|
||||
@@ -94,10 +92,14 @@ def get_non_parallel_tests(repo_path):
|
||||
|
||||
return s
|
||||
|
||||
|
||||
# Parse extra dependencies passed by user from command line
|
||||
def get_dependencies():
|
||||
deps_map = {"": {"extra_deps": [], "extra_compiler_flags": []}}
|
||||
deps_map = {
|
||||
'': {
|
||||
'extra_deps': [],
|
||||
'extra_compiler_flags': []
|
||||
}
|
||||
}
|
||||
if len(sys.argv) < 2:
|
||||
return deps_map
|
||||
|
||||
@@ -108,7 +110,6 @@ def get_dependencies():
|
||||
v = encode_dict(v)
|
||||
rv[k] = v
|
||||
return rv
|
||||
|
||||
extra_deps = json.loads(sys.argv[1], object_hook=encode_dict)
|
||||
for target_alias, deps in extra_deps.items():
|
||||
deps_map[target_alias] = deps
|
||||
@@ -141,70 +142,56 @@ def generate_targets(repo_path, deps_map):
|
||||
"rocksdb_lib",
|
||||
src_mk["LIB_SOURCES"] +
|
||||
# always add range_tree, it's only excluded on ppc64, which we don't use internally
|
||||
src_mk["RANGE_TREE_SOURCES"] + src_mk["TOOL_LIB_SOURCES"],
|
||||
deps=[
|
||||
"//folly/container:f14_hash",
|
||||
"//folly/experimental/coro:blocking_wait",
|
||||
"//folly/experimental/coro:collect",
|
||||
"//folly/experimental/coro:coroutine",
|
||||
"//folly/experimental/coro:task",
|
||||
"//folly/synchronization:distributed_mutex",
|
||||
],
|
||||
)
|
||||
src_mk["RANGE_TREE_SOURCES"] +
|
||||
src_mk["TOOL_LIB_SOURCES"])
|
||||
# rocksdb_whole_archive_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_whole_archive_lib",
|
||||
[],
|
||||
deps=[
|
||||
":rocksdb_lib",
|
||||
],
|
||||
src_mk["LIB_SOURCES"] +
|
||||
# always add range_tree, it's only excluded on ppc64, which we don't use internally
|
||||
src_mk["RANGE_TREE_SOURCES"] +
|
||||
src_mk["TOOL_LIB_SOURCES"],
|
||||
deps=None,
|
||||
headers=None,
|
||||
extra_external_deps="",
|
||||
link_whole=True,
|
||||
)
|
||||
link_whole=True)
|
||||
# rocksdb_test_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_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", []),
|
||||
src_mk.get("MOCK_LIB_SOURCES", []) +
|
||||
src_mk.get("TEST_LIB_SOURCES", []) +
|
||||
src_mk.get("EXP_LIB_SOURCES", []) +
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", []),
|
||||
[":rocksdb_lib"],
|
||||
extra_test_libs=True,
|
||||
)
|
||||
extra_test_libs=True
|
||||
)
|
||||
# rocksdb_tools_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_tools_lib",
|
||||
src_mk.get("BENCH_LIB_SOURCES", [])
|
||||
+ src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
+ ["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"],
|
||||
)
|
||||
src_mk.get("BENCH_LIB_SOURCES", []) +
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", []) +
|
||||
["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"])
|
||||
# rocksdb_cache_bench_tools_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_cache_bench_tools_lib",
|
||||
src_mk.get("CACHE_BENCH_LIB_SOURCES", []),
|
||||
[":rocksdb_lib"],
|
||||
)
|
||||
[":rocksdb_lib"])
|
||||
# rocksdb_stress_lib
|
||||
TARGETS.add_rocksdb_library(
|
||||
"rocksdb_stress_lib",
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
+ src_mk.get("STRESS_LIB_SOURCES", [])
|
||||
+ ["test_util/testutil.cc"],
|
||||
)
|
||||
# db_stress binary
|
||||
TARGETS.add_binary(
|
||||
"db_stress", ["db_stress_tool/db_stress.cc"], [":rocksdb_stress_lib"]
|
||||
)
|
||||
# cache_bench binary
|
||||
TARGETS.add_binary(
|
||||
"cache_bench", ["cache/cache_bench.cc"], [":rocksdb_cache_bench_tools_lib"]
|
||||
)
|
||||
+ src_mk.get('STRESS_LIB_SOURCES', [])
|
||||
+ ["test_util/testutil.cc"])
|
||||
# bench binaries
|
||||
for src in src_mk.get("MICROBENCH_SOURCES", []):
|
||||
name = src.rsplit("/", 1)[1].split(".")[0] if "/" in src else src.split(".")[0]
|
||||
TARGETS.add_binary(name, [src], [], extra_bench_libs=True)
|
||||
name = src.rsplit('/',1)[1].split('.')[0] if '/' in src else src.split('.')[0]
|
||||
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
|
||||
@@ -214,7 +201,7 @@ def generate_targets(repo_path, deps_map):
|
||||
# are more than one .c test file, we need to extend
|
||||
# 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":
|
||||
if test_src != 'db/c_test.c':
|
||||
print("Don't know how to deal with " + test_src)
|
||||
return False
|
||||
TARGETS.add_c_test()
|
||||
@@ -224,7 +211,7 @@ def generate_targets(repo_path, deps_map):
|
||||
fast_fancy_bench_config_list = json.load(json_file)
|
||||
for config_dict in fast_fancy_bench_config_list:
|
||||
clean_benchmarks = {}
|
||||
benchmarks = config_dict["benchmarks"]
|
||||
benchmarks = config_dict['benchmarks']
|
||||
for binary, benchmark_dict in benchmarks.items():
|
||||
clean_benchmarks[binary] = {}
|
||||
for benchmark, overloaded_metric_list in benchmark_dict.items():
|
||||
@@ -232,20 +219,13 @@ def generate_targets(repo_path, deps_map):
|
||||
for metric in overloaded_metric_list:
|
||||
if not isinstance(metric, dict):
|
||||
clean_benchmarks[binary][benchmark].append(metric)
|
||||
TARGETS.add_fancy_bench_config(
|
||||
config_dict["name"],
|
||||
clean_benchmarks,
|
||||
False,
|
||||
config_dict["expected_runtime_one_iter"],
|
||||
config_dict["sl_iterations"],
|
||||
config_dict["regression_threshold"],
|
||||
)
|
||||
TARGETS.add_fancy_bench_config(config_dict['name'], clean_benchmarks, False, config_dict['expected_runtime_one_iter'], config_dict['sl_iterations'], config_dict['regression_threshold'])
|
||||
|
||||
with open(f"{repo_path}/buckifier/bench-slow.json") as json_file:
|
||||
slow_fancy_bench_config_list = json.load(json_file)
|
||||
for config_dict in slow_fancy_bench_config_list:
|
||||
clean_benchmarks = {}
|
||||
benchmarks = config_dict["benchmarks"]
|
||||
benchmarks = config_dict['benchmarks']
|
||||
for binary, benchmark_dict in benchmarks.items():
|
||||
clean_benchmarks[binary] = {}
|
||||
for benchmark, overloaded_metric_list in benchmark_dict.items():
|
||||
@@ -254,14 +234,7 @@ def generate_targets(repo_path, deps_map):
|
||||
if not isinstance(metric, dict):
|
||||
clean_benchmarks[binary][benchmark].append(metric)
|
||||
for config_dict in slow_fancy_bench_config_list:
|
||||
TARGETS.add_fancy_bench_config(
|
||||
config_dict["name"] + "_slow",
|
||||
clean_benchmarks,
|
||||
True,
|
||||
config_dict["expected_runtime_one_iter"],
|
||||
config_dict["sl_iterations"],
|
||||
config_dict["regression_threshold"],
|
||||
)
|
||||
TARGETS.add_fancy_bench_config(config_dict['name']+"_slow", clean_benchmarks, True, config_dict['expected_runtime_one_iter'], config_dict['sl_iterations'], config_dict['regression_threshold'])
|
||||
# it is better servicelab experiments break
|
||||
# than rocksdb github ci
|
||||
except Exception:
|
||||
@@ -270,7 +243,7 @@ def generate_targets(repo_path, deps_map):
|
||||
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 = test_src.split('.c')[0].strip().split('/')[-1].strip()
|
||||
test_source_map[test] = test_src
|
||||
print("" + test + " " + test_src)
|
||||
|
||||
@@ -280,30 +253,23 @@ def generate_targets(repo_path, deps_map):
|
||||
print(ColorString.warning("Failed to get test name for %s" % test_src))
|
||||
continue
|
||||
|
||||
test_target_name = test if not target_alias else test + "_" + target_alias
|
||||
test_target_name = \
|
||||
test if not target_alias else test + "_" + target_alias
|
||||
|
||||
if test in _EXPORTED_TEST_LIBS:
|
||||
test_library = "%s_lib" % test_target_name
|
||||
TARGETS.add_library(
|
||||
test_library,
|
||||
[test_src],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_test_libs=True,
|
||||
)
|
||||
TARGETS.add_library(test_library, [test_src], deps=[":rocksdb_test_lib"], extra_test_libs=True)
|
||||
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"]),
|
||||
)
|
||||
deps = json.dumps(deps['extra_deps'] + [':'+test_library]),
|
||||
extra_compiler_flags = json.dumps(deps['extra_compiler_flags']))
|
||||
else:
|
||||
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")
|
||||
deps = json.dumps(deps['extra_deps'] + [":rocksdb_test_lib"] ),
|
||||
extra_compiler_flags = json.dumps(deps['extra_compiler_flags']))
|
||||
|
||||
print(ColorString.info("Generated TARGETS Summary:"))
|
||||
print(ColorString.info("- %d libs" % TARGETS.total_lib))
|
||||
@@ -316,7 +282,8 @@ def get_rocksdb_path():
|
||||
# rocksdb = {script_dir}/..
|
||||
script_dir = os.path.dirname(sys.argv[0])
|
||||
script_dir = os.path.abspath(script_dir)
|
||||
rocksdb_path = os.path.abspath(os.path.join(script_dir, "../"))
|
||||
rocksdb_path = os.path.abspath(
|
||||
os.path.join(script_dir, "../"))
|
||||
|
||||
return rocksdb_path
|
||||
|
||||
@@ -333,6 +300,5 @@ def main():
|
||||
if not ok:
|
||||
exit_with_error("Failed to generate TARGETS files")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
+68
-111
@@ -1,156 +1,113 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
try:
|
||||
from builtins import object, str
|
||||
from builtins import object
|
||||
from builtins import str
|
||||
except ImportError:
|
||||
from __builtin__ import object, str
|
||||
import pprint
|
||||
|
||||
from __builtin__ import object
|
||||
from __builtin__ import str
|
||||
import targets_cfg
|
||||
|
||||
import pprint
|
||||
|
||||
def pretty_list(lst, indent=8):
|
||||
if lst is None or len(lst) == 0:
|
||||
return ""
|
||||
|
||||
if len(lst) == 1:
|
||||
return '"%s"' % lst[0]
|
||||
return "\"%s\"" % lst[0]
|
||||
|
||||
separator = '",\n%s"' % (" " * indent)
|
||||
separator = "\",\n%s\"" % (" " * indent)
|
||||
res = separator.join(sorted(lst))
|
||||
res = "\n" + (" " * indent) + '"' + res + '",\n' + (" " * (indent - 4))
|
||||
res = "\n" + (" " * indent) + "\"" + res + "\",\n" + (" " * (indent - 4))
|
||||
return res
|
||||
|
||||
|
||||
class TARGETSBuilder:
|
||||
class TARGETSBuilder(object):
|
||||
def __init__(self, path, extra_argv):
|
||||
self.path = path
|
||||
self.targets_file = open(path, 'wb')
|
||||
header = targets_cfg.rocksdb_target_header_template.format(
|
||||
extra_argv=extra_argv
|
||||
)
|
||||
with open(path, "wb") as targets_file:
|
||||
targets_file.write(header.encode("utf-8"))
|
||||
extra_argv=extra_argv)
|
||||
self.targets_file.write(header.encode("utf-8"))
|
||||
self.total_lib = 0
|
||||
self.total_bin = 0
|
||||
self.total_test = 0
|
||||
self.tests_cfg = ""
|
||||
|
||||
def add_library(
|
||||
self,
|
||||
name,
|
||||
srcs,
|
||||
deps=None,
|
||||
headers=None,
|
||||
extra_external_deps="",
|
||||
link_whole=False,
|
||||
external_dependencies=None,
|
||||
extra_test_libs=False,
|
||||
):
|
||||
def __del__(self):
|
||||
self.targets_file.close()
|
||||
|
||||
def add_library(self, name, srcs, deps=None, headers=None,
|
||||
extra_external_deps="", link_whole=False,
|
||||
external_dependencies=None, extra_test_libs=False):
|
||||
if headers is not None:
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.library_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
headers=headers,
|
||||
deps=pretty_list(deps),
|
||||
extra_external_deps=extra_external_deps,
|
||||
link_whole=link_whole,
|
||||
external_dependencies=pretty_list(external_dependencies),
|
||||
extra_test_libs=extra_test_libs,
|
||||
).encode("utf-8")
|
||||
self.targets_file.write(targets_cfg.library_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
headers=headers,
|
||||
deps=pretty_list(deps),
|
||||
extra_external_deps=extra_external_deps,
|
||||
link_whole=link_whole,
|
||||
external_dependencies=pretty_list(external_dependencies),
|
||||
extra_test_libs=extra_test_libs
|
||||
).encode("utf-8"))
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_rocksdb_library(self, name, srcs, headers=None,
|
||||
external_dependencies=None):
|
||||
if headers is not None:
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
self.targets_file.write(targets_cfg.rocksdb_library_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
headers=headers,
|
||||
external_dependencies=pretty_list(external_dependencies)
|
||||
).encode("utf-8")
|
||||
)
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_rocksdb_library(self, name, srcs, headers=None, external_dependencies=None):
|
||||
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(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
headers=headers,
|
||||
external_dependencies=pretty_list(external_dependencies),
|
||||
).encode("utf-8")
|
||||
)
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_binary(
|
||||
self,
|
||||
name,
|
||||
srcs,
|
||||
deps=None,
|
||||
extra_preprocessor_flags=None,
|
||||
extra_bench_libs=False,
|
||||
):
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.binary_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
deps=pretty_list(deps),
|
||||
extra_preprocessor_flags=pretty_list(extra_preprocessor_flags),
|
||||
extra_bench_libs=extra_bench_libs,
|
||||
).encode("utf-8")
|
||||
)
|
||||
def add_binary(self, name, srcs, deps=None, extra_preprocessor_flags=None,extra_bench_libs=False):
|
||||
self.targets_file.write(targets_cfg.binary_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
deps=pretty_list(deps),
|
||||
extra_preprocessor_flags=pretty_list(extra_preprocessor_flags),
|
||||
extra_bench_libs=extra_bench_libs,
|
||||
).encode("utf-8"))
|
||||
self.total_bin = self.total_bin + 1
|
||||
|
||||
def add_c_test(self):
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
b"""
|
||||
self.targets_file.write(b"""
|
||||
add_c_test_wrapper()
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
def add_test_header(self):
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
b"""
|
||||
self.targets_file.write(b"""
|
||||
# Generate a test rule for each entry in ROCKS_TESTS
|
||||
# Do not build the tests in opt mode, since SyncPoint and other test code
|
||||
# will not be included.
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
def add_fancy_bench_config(
|
||||
self,
|
||||
name,
|
||||
bench_config,
|
||||
slow,
|
||||
expected_runtime,
|
||||
sl_iterations,
|
||||
regression_threshold,
|
||||
):
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.fancy_bench_template.format(
|
||||
def add_fancy_bench_config(self, name, bench_config, slow, expected_runtime, sl_iterations, regression_threshold):
|
||||
self.targets_file.write(targets_cfg.fancy_bench_template.format(
|
||||
name=name,
|
||||
bench_config=pprint.pformat(bench_config),
|
||||
slow=slow,
|
||||
expected_runtime=expected_runtime,
|
||||
sl_iterations=sl_iterations,
|
||||
regression_threshold=regression_threshold,
|
||||
).encode("utf-8")
|
||||
)
|
||||
regression_threshold=regression_threshold
|
||||
).encode("utf-8"))
|
||||
|
||||
def register_test(self, test_name, src, deps, extra_compiler_flags):
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.unittests_template.format(
|
||||
test_name=test_name,
|
||||
test_cc=str(src),
|
||||
deps=deps,
|
||||
extra_compiler_flags=extra_compiler_flags,
|
||||
).encode("utf-8")
|
||||
)
|
||||
def register_test(self,
|
||||
test_name,
|
||||
src,
|
||||
deps,
|
||||
extra_compiler_flags):
|
||||
|
||||
self.targets_file.write(targets_cfg.unittests_template.format(test_name=test_name,test_cc=str(src),deps=deps,
|
||||
extra_compiler_flags=extra_compiler_flags).encode("utf-8"))
|
||||
self.total_test = self.total_test + 1
|
||||
|
||||
def export_file(self, name):
|
||||
with open(self.path, "a") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.export_file_template.format(name=name)
|
||||
)
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
rocksdb_target_header_template = """# This file \100generated by:
|
||||
rocksdb_target_header_template = \
|
||||
"""# This file \100generated by:
|
||||
#$ python3 buckifier/buckify_rocksdb.py{extra_argv}
|
||||
# --> DO NOT EDIT MANUALLY <--
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
#
|
||||
# @noautodeps @nocodemods
|
||||
load("//rocks/buckifier:defs.bzl", "cpp_library_wrapper","rocks_cpp_library_wrapper","cpp_binary_wrapper","cpp_unittest_wrapper","fancy_bench_wrapper","add_c_test_wrapper")
|
||||
|
||||
"""
|
||||
@@ -21,6 +27,7 @@ rocks_cpp_library_wrapper(name="{name}", srcs=[{srcs}], headers={headers})
|
||||
"""
|
||||
|
||||
|
||||
|
||||
binary_template = """
|
||||
cpp_binary_wrapper(name="{name}", srcs=[{srcs}], deps=[{deps}], extra_preprocessor_flags=[{extra_preprocessor_flags}], extra_bench_libs={extra_bench_libs})
|
||||
"""
|
||||
@@ -37,7 +44,3 @@ fancy_bench_template = """
|
||||
fancy_bench_wrapper(suite_name="{name}", binary_to_bench_to_metric_list_map={bench_config}, slow={slow}, expected_runtime={expected_runtime}, sl_iterations={sl_iterations}, regression_threshold={regression_threshold})
|
||||
|
||||
"""
|
||||
|
||||
export_file_template = """
|
||||
export_file(name = "{name}")
|
||||
"""
|
||||
|
||||
+30
-29
@@ -2,35 +2,37 @@
|
||||
"""
|
||||
This module keeps commonly used components.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
try:
|
||||
from builtins import object
|
||||
except ImportError:
|
||||
from __builtin__ import object
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
|
||||
class ColorString:
|
||||
"""Generate colorful strings on terminal"""
|
||||
|
||||
HEADER = "\033[95m"
|
||||
BLUE = "\033[94m"
|
||||
GREEN = "\033[92m"
|
||||
WARNING = "\033[93m"
|
||||
FAIL = "\033[91m"
|
||||
ENDC = "\033[0m"
|
||||
class ColorString(object):
|
||||
""" Generate colorful strings on terminal """
|
||||
HEADER = '\033[95m'
|
||||
BLUE = '\033[94m'
|
||||
GREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
|
||||
@staticmethod
|
||||
def _make_color_str(text, color):
|
||||
# In Python2, default encoding for unicode string is ASCII
|
||||
if sys.version_info.major <= 2:
|
||||
return "".join([color, text.encode("utf-8"), ColorString.ENDC])
|
||||
return "".join(
|
||||
[color, text.encode('utf-8'), ColorString.ENDC])
|
||||
# From Python3, default encoding for unicode string is UTF-8
|
||||
return "".join([color, text, ColorString.ENDC])
|
||||
return "".join(
|
||||
[color, text, ColorString.ENDC])
|
||||
|
||||
@staticmethod
|
||||
def ok(text):
|
||||
@@ -66,38 +68,37 @@ class ColorString:
|
||||
|
||||
|
||||
def run_shell_command(shell_cmd, cmd_dir=None):
|
||||
"""Run a single shell command.
|
||||
@returns a tuple of shell command return code, stdout, stderr"""
|
||||
""" Run a single shell command.
|
||||
@returns a tuple of shell command return code, stdout, stderr """
|
||||
|
||||
if cmd_dir is not None and not os.path.exists(cmd_dir):
|
||||
run_shell_command("mkdir -p %s" % cmd_dir)
|
||||
|
||||
start = time.time()
|
||||
print("\t>>> Running: " + shell_cmd)
|
||||
p = subprocess.Popen( # noqa
|
||||
shell_cmd,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
cwd=cmd_dir,
|
||||
)
|
||||
p = subprocess.Popen(shell_cmd,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
cwd=cmd_dir)
|
||||
stdout, stderr = p.communicate()
|
||||
end = time.time()
|
||||
|
||||
# Report time if we spent more than 5 minutes executing a command
|
||||
execution_time = end - start
|
||||
if execution_time > (60 * 5):
|
||||
mins = execution_time / 60
|
||||
secs = execution_time % 60
|
||||
mins = (execution_time / 60)
|
||||
secs = (execution_time % 60)
|
||||
print("\t>time spent: %d minutes %d seconds" % (mins, secs))
|
||||
|
||||
|
||||
return p.returncode, stdout, stderr
|
||||
|
||||
|
||||
def run_shell_commands(shell_cmds, cmd_dir=None, verbose=False):
|
||||
"""Execute a sequence of shell commands, which is equivalent to
|
||||
running `cmd1 && cmd2 && cmd3`
|
||||
@returns boolean indication if all commands succeeds.
|
||||
""" Execute a sequence of shell commands, which is equivalent to
|
||||
running `cmd1 && cmd2 && cmd3`
|
||||
@returns boolean indication if all commands succeeds.
|
||||
"""
|
||||
|
||||
if cmd_dir:
|
||||
|
||||
+15
-72
@@ -28,15 +28,14 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
from os import path
|
||||
import re
|
||||
import sys
|
||||
from os import path
|
||||
|
||||
include_re = re.compile('^[ \t]*#include[ \t]+"(.*)"[ \t]*$')
|
||||
included = set()
|
||||
excluded = set()
|
||||
|
||||
|
||||
def find_header(name, abs_path, include_paths):
|
||||
samedir = path.join(path.dirname(abs_path), name)
|
||||
if path.exists(samedir):
|
||||
@@ -47,31 +46,17 @@ def find_header(name, abs_path, include_paths):
|
||||
return include_path
|
||||
return None
|
||||
|
||||
|
||||
def expand_include(
|
||||
include_path,
|
||||
f,
|
||||
abs_path,
|
||||
source_out,
|
||||
header_out,
|
||||
include_paths,
|
||||
public_include_paths,
|
||||
):
|
||||
def expand_include(include_path, f, abs_path, source_out, header_out, include_paths, public_include_paths):
|
||||
if include_path in included:
|
||||
return False
|
||||
|
||||
included.add(include_path)
|
||||
with open(include_path) as f:
|
||||
print('#line 1 "{}"'.format(include_path), file=source_out)
|
||||
process_file(
|
||||
f, include_path, source_out, header_out, include_paths, public_include_paths
|
||||
)
|
||||
process_file(f, include_path, source_out, header_out, include_paths, public_include_paths)
|
||||
return True
|
||||
|
||||
|
||||
def process_file(
|
||||
f, abs_path, source_out, header_out, include_paths, public_include_paths
|
||||
):
|
||||
def process_file(f, abs_path, source_out, header_out, include_paths, public_include_paths):
|
||||
for (line, text) in enumerate(f):
|
||||
m = include_re.match(text)
|
||||
if m:
|
||||
@@ -83,15 +68,7 @@ def process_file(
|
||||
source_out.write(text)
|
||||
expanded = False
|
||||
else:
|
||||
expanded = expand_include(
|
||||
include_path,
|
||||
f,
|
||||
abs_path,
|
||||
source_out,
|
||||
header_out,
|
||||
include_paths,
|
||||
public_include_paths,
|
||||
)
|
||||
expanded = expand_include(include_path, f, abs_path, source_out, header_out, include_paths, public_include_paths)
|
||||
else:
|
||||
# now try public headers
|
||||
include_path = find_header(filename, abs_path, public_include_paths)
|
||||
@@ -101,52 +78,23 @@ def process_file(
|
||||
if include_path in excluded:
|
||||
source_out.write(text)
|
||||
else:
|
||||
expand_include(
|
||||
include_path,
|
||||
f,
|
||||
abs_path,
|
||||
header_out,
|
||||
None,
|
||||
public_include_paths,
|
||||
[],
|
||||
)
|
||||
expand_include(include_path, f, abs_path, header_out, None, public_include_paths, [])
|
||||
else:
|
||||
sys.exit(
|
||||
"unable to find {}, included in {} on line {}".format(
|
||||
filename, abs_path, line
|
||||
)
|
||||
)
|
||||
sys.exit("unable to find {}, included in {} on line {}".format(filename, abs_path, line))
|
||||
|
||||
if expanded:
|
||||
print('#line {} "{}"'.format(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)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Transform a unity build into an amalgamation"
|
||||
)
|
||||
parser = argparse.ArgumentParser(description="Transform a unity build into an amalgamation")
|
||||
parser.add_argument("source", help="source file")
|
||||
parser.add_argument(
|
||||
"-I",
|
||||
action="append",
|
||||
dest="include_paths",
|
||||
help="include paths for private headers",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
action="append",
|
||||
dest="public_include_paths",
|
||||
help="include paths for public headers",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-x", action="append", dest="excluded", help="excluded header files"
|
||||
)
|
||||
parser.add_argument("-I", action="append", dest="include_paths", help="include paths for private headers")
|
||||
parser.add_argument("-i", action="append", dest="public_include_paths", help="include paths for public headers")
|
||||
parser.add_argument("-x", action="append", dest="excluded", help="excluded header files")
|
||||
parser.add_argument("-o", dest="source_out", help="output C++ file", required=True)
|
||||
parser.add_argument(
|
||||
"-H", dest="header_out", help="output C++ header file", required=True
|
||||
)
|
||||
parser.add_argument("-H", dest="header_out", help="output C++ header file", required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
include_paths = list(map(path.abspath, args.include_paths or []))
|
||||
@@ -154,15 +102,10 @@ def main():
|
||||
excluded.update(map(path.abspath, args.excluded or []))
|
||||
filename = args.source
|
||||
abs_path = path.abspath(filename)
|
||||
with open(filename) as f, open(args.source_out, "w") as source_out, open(
|
||||
args.header_out, "w"
|
||||
) as header_out:
|
||||
with open(filename) as f, open(args.source_out, 'w') as source_out, open(args.header_out, 'w') as header_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
|
||||
)
|
||||
|
||||
process_file(f, abs_path, source_out, header_out, include_paths, public_include_paths)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
# This source code is licensed under both the GPLv2 (found in the
|
||||
# COPYING file in the root directory) and Apache 2.0 License
|
||||
# (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
"""Access the results of benchmark runs
|
||||
Send these results on to OpenSearch graphing service
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import requests
|
||||
from dateutil import parser
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
|
||||
class Configuration:
|
||||
opensearch_user = os.environ["ES_USER"]
|
||||
opensearch_pass = os.environ["ES_PASS"]
|
||||
|
||||
|
||||
class BenchmarkResultException(Exception):
|
||||
def __init__(self, message, content):
|
||||
super().__init__(self, message)
|
||||
self.content = content
|
||||
|
||||
|
||||
class BenchmarkUtils:
|
||||
|
||||
expected_keys = [
|
||||
"ops_sec",
|
||||
"mb_sec",
|
||||
"lsm_sz",
|
||||
"blob_sz",
|
||||
"c_wgb",
|
||||
"w_amp",
|
||||
"c_mbps",
|
||||
"c_wsecs",
|
||||
"c_csecs",
|
||||
"b_rgb",
|
||||
"b_wgb",
|
||||
"usec_op",
|
||||
"p50",
|
||||
"p99",
|
||||
"p99.9",
|
||||
"p99.99",
|
||||
"pmax",
|
||||
"uptime",
|
||||
"stall%",
|
||||
"Nstall",
|
||||
"u_cpu",
|
||||
"s_cpu",
|
||||
"rss",
|
||||
"test",
|
||||
"date",
|
||||
"version",
|
||||
"job_id",
|
||||
]
|
||||
|
||||
def sanity_check(row):
|
||||
if "test" not in row:
|
||||
logging.debug(f"not 'test' in row: {row}")
|
||||
return False
|
||||
if row["test"] == "":
|
||||
logging.debug(f"row['test'] == '': {row}")
|
||||
return False
|
||||
if "date" not in row:
|
||||
logging.debug(f"not 'date' in row: {row}")
|
||||
return False
|
||||
if "ops_sec" not in row:
|
||||
logging.debug(f"not 'ops_sec' in row: {row}")
|
||||
return False
|
||||
try:
|
||||
_ = int(row["ops_sec"])
|
||||
except (ValueError, TypeError):
|
||||
logging.debug(f"int(row['ops_sec']): {row}")
|
||||
return False
|
||||
try:
|
||||
(_, _) = parser.parse(row["date"], fuzzy_with_tokens=True)
|
||||
except (parser.ParserError):
|
||||
logging.error(
|
||||
f"parser.parse((row['date']): not a valid format for date in row: {row}"
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
def conform_opensearch(row):
|
||||
(dt, _) = parser.parse(row["date"], fuzzy_with_tokens=True)
|
||||
# create a test_date field, which was previously what was expected
|
||||
# repair the date field, which has what can be a WRONG ISO FORMAT, (no leading 0 on single-digit day-of-month)
|
||||
# e.g. 2022-07-1T00:14:55 should be 2022-07-01T00:14:55
|
||||
row["test_date"] = dt.isoformat()
|
||||
row["date"] = dt.isoformat()
|
||||
return {key.replace(".", "_"): value for key, value in row.items()}
|
||||
|
||||
|
||||
class ResultParser:
|
||||
def __init__(self, field="(\w|[+-:.%])+", intrafield="(\s)+", separator="\t"):
|
||||
self.field = re.compile(field)
|
||||
self.intra = re.compile(intrafield)
|
||||
self.sep = re.compile(separator)
|
||||
|
||||
def ignore(self, l_in: str):
|
||||
if len(l_in) == 0:
|
||||
return True
|
||||
if l_in[0:1] == "#":
|
||||
return True
|
||||
return False
|
||||
|
||||
def line(self, line_in: str):
|
||||
"""Parse a line into items
|
||||
Being clever about separators
|
||||
"""
|
||||
line = line_in
|
||||
row = []
|
||||
while line != "":
|
||||
match_item = self.field.match(line)
|
||||
if match_item:
|
||||
item = match_item.group(0)
|
||||
row.append(item)
|
||||
line = line[len(item) :]
|
||||
else:
|
||||
match_intra = self.intra.match(line)
|
||||
if match_intra:
|
||||
intra = match_intra.group(0)
|
||||
# Count the separators
|
||||
# If there are >1 then generate extra blank fields
|
||||
# White space with no true separators fakes up a single separator
|
||||
tabbed = self.sep.split(intra)
|
||||
sep_count = len(tabbed) - 1
|
||||
if sep_count == 0:
|
||||
sep_count = 1
|
||||
for _ in range(sep_count - 1):
|
||||
row.append("")
|
||||
line = line[len(intra) :]
|
||||
else:
|
||||
raise BenchmarkResultException(
|
||||
"Invalid TSV line", f"{line_in} at {line}"
|
||||
)
|
||||
return row
|
||||
|
||||
def parse(self, lines):
|
||||
"""Parse something that iterates lines"""
|
||||
rows = [self.line(line) for line in lines if not self.ignore(line)]
|
||||
header = rows[0]
|
||||
width = len(header)
|
||||
records = [
|
||||
{k: v for (k, v) in itertools.zip_longest(header, row[:width])}
|
||||
for row in rows[1:]
|
||||
]
|
||||
return records
|
||||
|
||||
|
||||
def load_report_from_tsv(filename: str):
|
||||
file = open(filename, "r")
|
||||
contents = file.readlines()
|
||||
file.close()
|
||||
parser = ResultParser()
|
||||
report = parser.parse(contents)
|
||||
logging.debug(f"Loaded TSV Report: {report}")
|
||||
return report
|
||||
|
||||
|
||||
def push_report_to_opensearch(report, esdocument):
|
||||
sanitized = [
|
||||
BenchmarkUtils.conform_opensearch(row)
|
||||
for row in report
|
||||
if BenchmarkUtils.sanity_check(row)
|
||||
]
|
||||
logging.debug(
|
||||
f"upload {len(sanitized)} sane of {len(report)} benchmarks to opensearch"
|
||||
)
|
||||
for single_benchmark in sanitized:
|
||||
logging.debug(f"upload benchmark: {single_benchmark}")
|
||||
response = requests.post(
|
||||
esdocument,
|
||||
json=single_benchmark,
|
||||
auth=(os.environ["ES_USER"], os.environ["ES_PASS"]),
|
||||
)
|
||||
logging.debug(
|
||||
f"Sent to OpenSearch, status: {response.status_code}, result: {response.text}"
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
|
||||
def push_report_to_null(report):
|
||||
|
||||
for row in report:
|
||||
if BenchmarkUtils.sanity_check(row):
|
||||
logging.debug(f"row {row}")
|
||||
conformed = BenchmarkUtils.conform_opensearch(row)
|
||||
logging.debug(f"conformed row {conformed}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Tool for fetching, parsing and uploading benchmark results to OpenSearch / ElasticSearch
|
||||
This tool will
|
||||
|
||||
(1) Open a local tsv benchmark report file
|
||||
(2) Upload to OpenSearch document, via https/JSON
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(description="CircleCI benchmark scraper.")
|
||||
|
||||
# --tsvfile is the name of the file to read results from
|
||||
# --esdocument is the ElasticSearch document to push these results into
|
||||
#
|
||||
parser.add_argument(
|
||||
"--tsvfile",
|
||||
default="build_tools/circle_api_scraper_input.txt",
|
||||
help="File from which to read tsv report",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--esdocument",
|
||||
help="ElasticSearch/OpenSearch document URL to upload report into",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--upload", choices=["opensearch", "none"], default="opensearch"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
logging.debug(f"Arguments: {args}")
|
||||
reports = load_report_from_tsv(args.tsvfile)
|
||||
if args.upload == "opensearch":
|
||||
push_report_to_opensearch(reports, args.esdocument)
|
||||
else:
|
||||
push_report_to_null(reports)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -63,7 +63,18 @@ if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
|
||||
if [ "$LIB_MODE" == "shared" ]; then
|
||||
PIC_BUILD=1
|
||||
fi
|
||||
source "$PWD/build_tools/fbcode_config_platform010.sh"
|
||||
if [ -n "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_5xx" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_PLATFORM007" ]; then
|
||||
source "$PWD/build_tools/fbcode_config_platform007.sh"
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_PLATFORM009" ]; then
|
||||
source "$PWD/build_tools/fbcode_config_platform009.sh"
|
||||
else
|
||||
source "$PWD/build_tools/fbcode_config_platform009.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Delete existing output, if it exists
|
||||
@@ -148,7 +159,7 @@ case "$TARGET_OS" in
|
||||
;;
|
||||
IOS)
|
||||
PLATFORM=IOS
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DOS_MACOSX -DIOS_CROSS_COMPILE "
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DOS_MACOSX -DIOS_CROSS_COMPILE -DROCKSDB_LITE"
|
||||
PLATFORM_SHARED_EXT=dylib
|
||||
PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name "
|
||||
CROSS_COMPILE=true
|
||||
@@ -163,6 +174,24 @@ case "$TARGET_OS" in
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt -ldl"
|
||||
if test -z "$ROCKSDB_USE_IO_URING"; then
|
||||
ROCKSDB_USE_IO_URING=1
|
||||
fi
|
||||
if test "$ROCKSDB_USE_IO_URING" -ne 0; then
|
||||
# check for liburing
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o test.o 2>/dev/null <<EOF
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
io_uring_queue_init(1, &ring, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -luring"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_IOURING_PRESENT"
|
||||
fi
|
||||
fi
|
||||
# PORT_FILES=port/linux/linux_specific.cc
|
||||
;;
|
||||
SunOS)
|
||||
@@ -253,7 +282,6 @@ if [ "$CROSS_COMPILE" = "true" -o "$FBCODE_BUILD" = "true" ]; then
|
||||
if [ "$FBCODE_BUILD" = "true" ]; then
|
||||
# Enable backtrace on fbcode since the necessary libraries are present
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_BACKTRACE"
|
||||
FOLLY_DIR="third-party/folly"
|
||||
fi
|
||||
true
|
||||
else
|
||||
@@ -401,7 +429,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_JEMALLOC; then
|
||||
# Test whether jemalloc is available
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS $LDFLAGS -x c++ - -o test.o -ljemalloc \
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o -ljemalloc \
|
||||
2>/dev/null; then
|
||||
# This will enable some preprocessor identifiers in the Makefile
|
||||
JEMALLOC=1
|
||||
@@ -410,19 +438,12 @@ EOF
|
||||
WITH_JEMALLOC_FLAG=1
|
||||
# check for JEMALLOC installed with HomeBrew
|
||||
if [ "$PLATFORM" == "OS_MACOSX" ]; then
|
||||
if [ "$TARGET_ARCHITECTURE" = "arm64" ]; then
|
||||
# on M1 Macs, homebrew installs here instead of /usr/local
|
||||
JEMALLOC_PREFIX="/opt/homebrew"
|
||||
else
|
||||
JEMALLOC_PREFIX="/usr/local"
|
||||
fi
|
||||
if hash brew 2>/dev/null && brew ls --versions jemalloc > /dev/null; then
|
||||
JEMALLOC_VER=$(brew ls --versions jemalloc | tail -n 1 | cut -f 2 -d ' ')
|
||||
JEMALLOC_INCLUDE="-I${JEMALLOC_PREFIX}/Cellar/jemalloc/${JEMALLOC_VER}/include"
|
||||
JEMALLOC_LIB="${JEMALLOC_PREFIX}/Cellar/jemalloc/${JEMALLOC_VER}/lib/libjemalloc_pic.a"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -L${JEMALLOC_PREFIX}/lib $JEMALLOC_LIB"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -L${JEMALLOC_PREFIX}/lib $JEMALLOC_LIB"
|
||||
JAVA_STATIC_LDFLAGS="$JAVA_STATIC_LDFLAGS -L${JEMALLOC_PREFIX}/lib $JEMALLOC_LIB"
|
||||
JEMALLOC_INCLUDE="-I/usr/local/Cellar/jemalloc/${JEMALLOC_VER}/include"
|
||||
JEMALLOC_LIB="/usr/local/Cellar/jemalloc/${JEMALLOC_VER}/lib/libjemalloc_pic.a"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS $JEMALLOC_LIB"
|
||||
JAVA_STATIC_LDFLAGS="$JAVA_STATIC_LDFLAGS $JEMALLOC_LIB"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -453,7 +474,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_MEMKIND; then
|
||||
# Test whether memkind library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $LDFLAGS -x c++ - -o test.o -lmemkind 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -lmemkind -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <memkind.h>
|
||||
int main() {
|
||||
memkind_malloc(MEMKIND_DAX_KMEM, 1024);
|
||||
@@ -577,7 +598,7 @@ EOF
|
||||
fi
|
||||
if ! test $ROCKSDB_DISABLE_BENCHMARK; then
|
||||
# Test whether google benchmark is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null -lbenchmark -lpthread 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null -lbenchmark 2>/dev/null <<EOF
|
||||
#include <benchmark/benchmark.h>
|
||||
int main() {}
|
||||
EOF
|
||||
@@ -585,40 +606,11 @@ EOF
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lbenchmark"
|
||||
fi
|
||||
fi
|
||||
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>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" != 0 ]; then
|
||||
FOLLY_DIR="./third-party/folly"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$ROCKSDB_USE_IO_URING"; then
|
||||
ROCKSDB_USE_IO_URING=1
|
||||
fi
|
||||
if [ "$ROCKSDB_USE_IO_URING" -ne 0 -a "$PLATFORM" = OS_LINUX ]; then
|
||||
# check for liburing
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o test.o 2>/dev/null <<EOF
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
io_uring_queue_init(1, &ring, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -luring"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_IOURING_PRESENT"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# TODO(tec): Fix -Wshorten-64-to-32 errors on FreeBSD and enable the warning.
|
||||
# -Wshorten-64-to-32 breaks compilation on FreeBSD aarch64 and i386
|
||||
if ! { [ "$TARGET_OS" = FreeBSD -o "$TARGET_OS" = OpenBSD ] && [ "$TARGET_ARCHITECTURE" = arm64 -o "$TARGET_ARCHITECTURE" = i386 ]; }; then
|
||||
if ! { [ "$TARGET_OS" = FreeBSD ] && [ "$TARGET_ARCHITECTURE" = arm64 -o "$TARGET_ARCHITECTURE" = i386 ]; }; then
|
||||
# Test whether -Wshorten-64-to-32 is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o -Wshorten-64-to-32 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
@@ -628,7 +620,7 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$PORTABLE" == "" ] || [ "$PORTABLE" == 0 ]; then
|
||||
if test "0$PORTABLE" -eq 0; then
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^ppc64`"; then
|
||||
# Tune for this POWER processor, treating '+' models as base models
|
||||
POWER=`LD_SHOW_AUXV=1 /bin/true | grep AT_PLATFORM | grep -E -o power[0-9]+`
|
||||
@@ -647,41 +639,41 @@ if [ "$PORTABLE" == "" ] || [ "$PORTABLE" == 0 ]; then
|
||||
fi
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
|
||||
RISC_ISA=$(cat /proc/cpuinfo | grep -E '^isa\s*:' | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
|
||||
if [ -n "${RISCV_ISA}" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
|
||||
fi
|
||||
RISC_ISA=$(cat /proc/cpuinfo | grep isa | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
|
||||
elif [ "$TARGET_OS" == "IOS" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif [ "$TARGET_OS" == "AIX" ] || [ "$TARGET_OS" == "SunOS" ]; then
|
||||
# TODO: Not sure why we don't use -march=native on these OSes
|
||||
if test "$USE_SSE"; then
|
||||
TRY_SSE_ETC="1"
|
||||
fi
|
||||
else
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=native "
|
||||
fi
|
||||
else
|
||||
# PORTABLE specified
|
||||
if [ "$PORTABLE" == 1 ]; then
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=rv64gc"
|
||||
elif test "$USE_SSE"; then
|
||||
# USE_SSE is DEPRECATED
|
||||
# This is a rough approximation of the old USE_SSE behavior
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=haswell"
|
||||
fi
|
||||
# Other than those cases, not setting -march= here.
|
||||
else
|
||||
# Assume PORTABLE is a minimum assumed cpu type, e.g. PORTABLE=haswell
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${PORTABLE}"
|
||||
# PORTABLE=1
|
||||
if test "$USE_SSE"; then
|
||||
TRY_SSE_ETC="1"
|
||||
fi
|
||||
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
|
||||
fi
|
||||
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
|
||||
RISC_ISA=$(cat /proc/cpuinfo | grep isa | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
|
||||
fi
|
||||
|
||||
if [[ "${PLATFORM}" == "OS_MACOSX" ]]; then
|
||||
# For portability compile for macOS 10.14 (2018) or newer
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mmacosx-version-min=10.14"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -mmacosx-version-min=10.14"
|
||||
# For portability compile for macOS 10.12 (2016) or newer
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mmacosx-version-min=10.12"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -mmacosx-version-min=10.12"
|
||||
# -mmacosx-version-min must come first here.
|
||||
PLATFORM_SHARED_LDFLAGS="-mmacosx-version-min=10.14 $PLATFORM_SHARED_LDFLAGS"
|
||||
PLATFORM_CMAKE_FLAGS="-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14"
|
||||
JAVA_STATIC_DEPS_COMMON_FLAGS="-mmacosx-version-min=10.14"
|
||||
PLATFORM_SHARED_LDFLAGS="-mmacosx-version-min=10.12 $PLATFORM_SHARED_LDFLAGS"
|
||||
PLATFORM_CMAKE_FLAGS="-DCMAKE_OSX_DEPLOYMENT_TARGET=10.12"
|
||||
JAVA_STATIC_DEPS_COMMON_FLAGS="-mmacosx-version-min=10.12"
|
||||
JAVA_STATIC_DEPS_LDFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
JAVA_STATIC_DEPS_CCFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
JAVA_STATIC_DEPS_CXXFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
@@ -705,6 +697,101 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$TRY_SSE_ETC"; then
|
||||
# The USE_SSE flag now means "attempt to compile with widely-available
|
||||
# Intel architecture extensions utilized by specific optimizations in the
|
||||
# source code." It's a qualifier on PORTABLE=1 that means "mostly portable."
|
||||
# It doesn't even really check that your current CPU is compatible.
|
||||
#
|
||||
# SSE4.2 available since nehalem, ca. 2008-2010
|
||||
# Includes POPCNT for BitsSetToOne, BitParity
|
||||
TRY_SSE42="-msse4.2"
|
||||
# PCLMUL available since westmere, ca. 2010-2011
|
||||
TRY_PCLMUL="-mpclmul"
|
||||
# AVX2 available since haswell, ca. 2013-2015
|
||||
TRY_AVX2="-mavx2"
|
||||
# BMI available since haswell, ca. 2013-2015
|
||||
# Primarily for TZCNT for CountTrailingZeroBits
|
||||
TRY_BMI="-mbmi"
|
||||
# LZCNT available since haswell, ca. 2013-2015
|
||||
# For FloorLog2
|
||||
TRY_LZCNT="-mlzcnt"
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_SSE42 -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
int main() {
|
||||
volatile uint32_t x = _mm_crc32_u32(0, 0);
|
||||
(void)x;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_SSE42 -DHAVE_SSE42"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use SSE intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_PCLMUL -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <wmmintrin.h>
|
||||
int main() {
|
||||
const auto a = _mm_set_epi64x(0, 0);
|
||||
const auto b = _mm_set_epi64x(0, 0);
|
||||
const auto c = _mm_clmulepi64_si128(a, b, 0x00);
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
(void)d;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_PCLMUL -DHAVE_PCLMUL"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use PCLMUL intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_AVX2 -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <immintrin.h>
|
||||
int main() {
|
||||
const auto a = _mm256_setr_epi32(0, 1, 2, 3, 4, 7, 6, 5);
|
||||
const auto b = _mm256_permutevar8x32_epi32(a, a);
|
||||
(void)b;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_AVX2 -DHAVE_AVX2"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use AVX2 intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_BMI -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <immintrin.h>
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argv;
|
||||
return (int)_tzcnt_u64((uint64_t)argc);
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_BMI -DHAVE_BMI"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use BMI intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_LZCNT -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <immintrin.h>
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argv;
|
||||
return (int)_lzcnt_u64((uint64_t)argc);
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_LZCNT -DHAVE_LZCNT"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use LZCNT intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
int main() {
|
||||
@@ -718,6 +805,9 @@ if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_UINT128_EXTENSION"
|
||||
fi
|
||||
|
||||
# thread_local is part of C++11 and later (TODO: clean up this define)
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_SUPPORT_THREAD_LOCAL"
|
||||
|
||||
if [ "$FBCODE_BUILD" != "true" -a "$PLATFORM" = OS_LINUX ]; then
|
||||
$CXX $COMMON_FLAGS $PLATFORM_SHARED_CFLAGS -x c++ -c - -o test_dl.o 2>/dev/null <<EOF
|
||||
void dummy_func() {}
|
||||
@@ -745,13 +835,6 @@ fi
|
||||
|
||||
rm -f test.o test_dl.o
|
||||
|
||||
# Get the path for the folly installation dir
|
||||
if [ "$USE_FOLLY" ]; then
|
||||
if [ "$FOLLY_DIR" ]; then
|
||||
FOLLY_PATH=`cd $FOLLY_DIR && $PYTHON build/fbcode_builder/getdeps.py show-inst-dir folly`
|
||||
fi
|
||||
fi
|
||||
|
||||
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
|
||||
|
||||
@@ -791,8 +874,6 @@ echo "CLANG_ANALYZER=$CLANG_ANALYZER" >> "$OUTPUT"
|
||||
echo "PROFILING_FLAGS=$PROFILING_FLAGS" >> "$OUTPUT"
|
||||
echo "FIND=$FIND" >> "$OUTPUT"
|
||||
echo "WATCH=$WATCH" >> "$OUTPUT"
|
||||
echo "FOLLY_PATH=$FOLLY_PATH" >> "$OUTPUT"
|
||||
|
||||
# This will enable some related identifiers for the preprocessor
|
||||
if test -n "$JEMALLOC"; then
|
||||
echo "JEMALLOC=1" >> "$OUTPUT"
|
||||
@@ -804,8 +885,8 @@ if test -n "$WITH_JEMALLOC_FLAG"; then
|
||||
echo "WITH_JEMALLOC_FLAG=$WITH_JEMALLOC_FLAG" >> "$OUTPUT"
|
||||
fi
|
||||
echo "LUA_PATH=$LUA_PATH" >> "$OUTPUT"
|
||||
if test -n "$USE_FOLLY"; then
|
||||
echo "USE_FOLLY=$USE_FOLLY" >> "$OUTPUT"
|
||||
if test -n "$USE_FOLLY_DISTRIBUTED_MUTEX"; then
|
||||
echo "USE_FOLLY_DISTRIBUTED_MUTEX=$USE_FOLLY_DISTRIBUTED_MUTEX" >> "$OUTPUT"
|
||||
fi
|
||||
if test -n "$PPC_LIBC_IS_GNU"; then
|
||||
echo "PPC_LIBC_IS_GNU=$PPC_LIBC_IS_GNU" >> "$OUTPUT"
|
||||
|
||||
@@ -5,31 +5,25 @@
|
||||
|
||||
BAD=""
|
||||
|
||||
git grep -n 'namespace rocksdb' -- '*.[ch]*'
|
||||
git grep 'namespace rocksdb' -- '*.[ch]*'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo "^^^^^ Do not hardcode namespace rocksdb. Use ROCKSDB_NAMESPACE"
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n -i 'nocommit' -- ':!build_tools/check-sources.sh'
|
||||
git grep -i 'nocommit' -- ':!build_tools/check-sources.sh'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo "^^^^^ Code was not intended to be committed"
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n 'include <rocksdb/' -- ':!build_tools/check-sources.sh'
|
||||
git grep '<rocksdb/' -- ':!build_tools/check-sources.sh'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo '^^^^^ Use double-quotes as in #include "rocksdb/something.h"'
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n 'include "include/rocksdb/' -- ':!build_tools/check-sources.sh'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo '^^^^^ Use #include "rocksdb/something.h" instead of #include "include/rocksdb/something.h"'
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n 'using namespace' -- ':!build_tools' ':!docs' \
|
||||
git grep 'using namespace' -- ':!build_tools' ':!docs' \
|
||||
':!third-party/folly/folly/lang/Align.h' \
|
||||
':!third-party/gtest-1.8.1/fused-src/gtest/gtest.h'
|
||||
if [ "$?" != "1" ]; then
|
||||
@@ -37,12 +31,6 @@ if [ "$?" != "1" ]; then
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n -P "[\x80-\xFF]" -- ':!docs' ':!*.md'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo '^^^^ Use only ASCII characters in source files'
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
if [ "$BAD" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/7331085db891a2ef4a88a48a751d834e8d68f4cb/5.x/centos7-native/c447969
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/1bd23f9917738974ad0ff305aa23eb5f93f18305/9.0.0/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/6ace84e956873d53638c738b6f65f3f469cca74c/5.x/gcc-5-glibc-2.23/339d858
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/192b0f42d63dcf6210d6ceae387b49af049e6e0c/2.23/gcc-5-glibc-2.23/ca1d1c0
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/gcc-5-glibc-2.23/9bc6787
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2d9f0b9a4274cc21f61272a9e89bdb859bce8f1f/1.2.8/gcc-5-glibc-2.23/9bc6787
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/gcc-5-glibc-2.23/9bc6787
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0f607f8fc442ea7d6b876931b1898bb573d5e5da/1.9.1/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/ca22bc441a4eb709e9e0b1f9fec9750fed7b31c5/1.4.x/gcc-5-glibc-2.23/03859b5
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/gcc-5-glibc-2.23/9bc6787
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/c26f08f47ac35fc31da2633b7da92d6b863246eb/master/gcc-5-glibc-2.23/0c8f76d
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/3f3fb57a5ccc5fd21c66416c0b83e0aa76a05376/2.0.11/gcc-5-glibc-2.23/9bc6787
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/40c73d874898b386a71847f1b99115d93822d11f/1.4/gcc-5-glibc-2.23/b443de1
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/4ce8e8dba77cdbd81b75d6f0c32fd7a1b76a11ec/2018_U5/gcc-5-glibc-2.23/9bc6787
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/fb251ecd2f5ae16f8671f7014c246e52a748fe0b/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/2e3cb7d119b3cea5f1e738cc13a1ac69f49eb875/2.29.1/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d42d152a15636529b0861ec493927200ebebca8e/3.15.0/gcc-5-glibc-2.23/9bc6787
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.2.3/gcc-5-glibc-2.23/65372bd
|
||||
@@ -0,0 +1,20 @@
|
||||
# shellcheck disable=SC2148
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/cf7d14c625ce30bae1a4661c2319c5a283e4dd22/4.8.1/centos6-native/cc6c9dc
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/8598c375b0e94e1448182eb3df034704144a838d/stable/centos6-native/3f16ddd
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d6e0a7da6faba45f5e5b1638f9edd7afc2f34e7d/4.8.1/gcc-4.8.1-glibc-2.17/8aac7fc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/d282e6e8f3d20f4e40a516834847bdc038e07973/2.17/gcc-4.8.1-glibc-2.17/99df8fc
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/8c38a4c1e52b4c2cc8a9cdc31b9c947ed7dbfcb4/1.1.3/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/0882df3713c7a84f15abe368dc004581f20b39d7/1.2.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/740325875f6729f42d28deaa2147b0854f3a347e/1.0.6/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0e790b441e2d9acd68d51e1d2e028f88c6a79ddf/r131/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/9455f75ff7f4831dc9fda02a6a0f8c68922fad8f/1.0.0/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/f001a51b2854957676d07306ef3abf67186b5c8b/2.1.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/fc8a13ca1fffa4d0765c716c5a0b49f0c107518f/master/gcc-4.8.1-glibc-2.17/8d31e51
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/17c514c4d102a25ca15f4558be564eeed76f4b6a/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/ad576de2a1ea560c4d3434304f0fc4e079bede42/trunk/gcc-4.8.1-glibc-2.17/675d945
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/9d9a554877d0c5bef330fe818ab7178806dd316a/4.0_update2/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/7c111ff27e0c466235163f00f280a9d617c3d2ec/4.0.9-36_fbk5_2933_gd092e3f/gcc-4.8.1-glibc-2.17/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/b7fd454c4b10c6a81015d4524ed06cdeab558490/2.26/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d7f4d4d86674a57668e3a96f76f0e17dd0eb8765/3.8.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/61e4abf5813bbc39bc4f548757ccfcadde175a48/5.2.3/centos6-native/730f94e
|
||||
@@ -0,0 +1,20 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/7331085db891a2ef4a88a48a751d834e8d68f4cb/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/963d9aeda70cc4779885b1277484fe7544a04e3e/9.0.0/platform007/9e92d53/
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/6ace84e956873d53638c738b6f65f3f469cca74c/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/192b0f42d63dcf6210d6ceae387b49af049e6e0c/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2d9f0b9a4274cc21f61272a9e89bdb859bce8f1f/1.2.8/platform007/ca4da3d
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/platform007/ca4da3d
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0f607f8fc442ea7d6b876931b1898bb573d5e5da/1.9.1/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/ca22bc441a4eb709e9e0b1f9fec9750fed7b31c5/1.4.x/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/c26f08f47ac35fc31da2633b7da92d6b863246eb/master/platform007/c26c002
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/3f3fb57a5ccc5fd21c66416c0b83e0aa76a05376/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/40c73d874898b386a71847f1b99115d93822d11f/1.4/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/4ce8e8dba77cdbd81b75d6f0c32fd7a1b76a11ec/2018_U5/platform007/ca4da3d
|
||||
LIBURING_BASE=/mnt/gvfs/third-party2/liburing/79427253fd0d42677255aacfe6d13bfe63f752eb/20190828/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/fb251ecd2f5ae16f8671f7014c246e52a748fe0b/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/ab9f09bba370e7066cafd4eb59752db93f2e8312/2.29.1/platform007/15a3614
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d42d152a15636529b0861ec493927200ebebca8e/3.15.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
@@ -0,0 +1,21 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/1795efe5f06778c15a92c8f9a2aba5dc496d9d4d/9.x/centos7-native/3bed279
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/7318eaac22659b6ff2fe43918e4b69fd0772a8a7/9.0.0/platform009/651ee30
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/4959b39cfbe5965a37c861c4c327fa7c5c759b87/9.x/platform009/9202ce7
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/45ce3375cdc77ecb2520bbf8f0ecddd3f98efd7a/2.30/platform009/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/be4de3205e029101b18aa8103daa696c2bef3b19/1.1.3/platform009/7f3b187
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/3c160ac5c67e257501e24c6c1d00ad5e01d73db6/1.2.8/platform009/7f3b187
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/73a237ac5bc0a5f5d67b39b8d253cfebaab88684/1.0.6/platform009/7f3b187
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/ec6573523b0ce55ef6373a4801189027cf07bb2c/1.9.1/platform009/7f3b187
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/64c58a207d2495e83abc57a500a956df09b79a7c/1.4.x/platform009/ba86d1f
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/824d0a8a5abb5b121afd1b35fc3896407ea50092/2.2.0/platform009/7f3b187
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/d9aef9feb850b168a68736420f217b01cce11a89/master/platform009/c305944
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/0af65f71e23a67bf65dc91b11f95caa39325c432/2.0.11/platform009/7f3b187
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/02486dac347645d31dce116f44e1de3177315be2/1.4/platform009/5191652
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/2e0ec671e550bfca347300bf3f789d9c0fff24ad/2018_U5/platform009/7f3b187
|
||||
LIBURING_BASE=/mnt/gvfs/third-party2/liburing/70dbd9cfee63a25611417d09433a86d7711b3990/20200729/platform009/7f3b187
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/32b8a2407b634df3f8f948ba373fc4acc6a18296/fb/platform009/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/08634589372fa5f237bfd374e8c644a8364e78c1/2.32/platform009/ba86d1f/
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/6ae525939ad02e5e676855082fbbc7828dbafeac/3.15.0/platform009/7f3b187
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/162efd9561a3d21f6869f4814011e9cf1b3ff4dc/5.3.4/platform009/a6271c4
|
||||
BENCHMARK_BASE=/mnt/gvfs/third-party2/benchmark/bce8d9564eaf161700aa3a20b1051564acf555fb/1.5.5/platform009/7f3b187
|
||||
@@ -1,22 +0,0 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# The file is generated using update_dependencies.sh.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/62de5a92e5f23c661c3d4b9f322e04eb14e7a5bd/11.x/centos8-native/886b5eb
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/1f6edd1ff15c99c861afc8f3cd69054cd974dd64/15/platform010/72a2ff8
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/fed6e93d87571fb162734c86636119d45a398963/2.34/platform010/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/31a346126a1f3b64812c362511cb04cc1bd40855/1.1.8/platform010/76ebdda
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/0c65c05468b5a38cef1a106a1f526463e120c8dd/1.2.8/platform010/76ebdda
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/09703139cfc376bd8a82642385a0e97726b28287/1.0.6/platform010/76ebdda
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/ff23d17b932725cc1734a14896a8b67c518ba169/1.9.4/platform010/76ebdda
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/576397d8b1d9cea7306ad1e454d5e55caaa2ff1c/1.4.x/platform010/64091f4
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/fecac07861cb829f5e60dbeff0503d3272db73c0/2.2.0/platform010/76ebdda
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/0bb3f5756788ce26e2e16a1cb2f2af2c59b51abe/master/platform010/f57cc4a
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/5b602edd46fda54cdd7ea45f77dbe4061206e174/2.0.11/platform010/76ebdda
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/97cac22a149c2e202917e05d44e87e516b68216f/1.4/platform010/5074a48
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/53953ebc4e3eda85ad6fc3e429ba146035e97b90/2018_U5/platform010/76ebdda
|
||||
LIBURING_BASE=/mnt/gvfs/third-party2/liburing/a98e2d137007e3ebf7f33bd6f99c2c56bdaf8488/20210212/platform010/76ebdda
|
||||
BENCHMARK_BASE=/mnt/gvfs/third-party2/benchmark/780c7a0f9cf0967961e69ad08e61cddd85d61821/trunk/platform010/76ebdda
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/624a2f8f6c93c3c1df8aa4a6255d8202631a6c80/fb/platform010/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/39579e8603b48b3540f8b0633f43adf29acccb8b/2.37/centos8-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/cd9cc656d49ecb53797ce4d055e49fde29fd57ff/3.19.0/platform010/76ebdda
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/363787fa5cac2a8aa20638909210443278fa138e/5.3.4/platform010/9079c97
|
||||
+64
-68
@@ -3,38 +3,40 @@
|
||||
# COPYING file in the root directory) and Apache 2.0 License
|
||||
# (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
"""Filter for error messages in test output:
|
||||
'''Filter for error messages in test output:
|
||||
- Receives merged stdout/stderr from test on stdin
|
||||
- Finds patterns of known error messages for test name (first argument)
|
||||
- Prints those error messages to stdout
|
||||
"""
|
||||
'''
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
class ErrorParserBase:
|
||||
class ErrorParserBase(object):
|
||||
def parse_error(self, line):
|
||||
"""Parses a line of test output. If it contains an error, returns a
|
||||
'''Parses a line of test output. If it contains an error, returns a
|
||||
formatted message describing the error; otherwise, returns None.
|
||||
Subclasses must override this method.
|
||||
"""
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class GTestErrorParser(ErrorParserBase):
|
||||
"""A parser that remembers the last test that began running so it can print
|
||||
'''A parser that remembers the last test that began running so it can print
|
||||
that test's name upon detecting failure.
|
||||
"""
|
||||
|
||||
_GTEST_NAME_PATTERN = re.compile(r"\[ RUN \] (\S+)$")
|
||||
'''
|
||||
_GTEST_NAME_PATTERN = re.compile(r'\[ RUN \] (\S+)$')
|
||||
# format: '<filename or "unknown file">:<line #>: Failure'
|
||||
_GTEST_FAIL_PATTERN = re.compile(r"(unknown file|\S+:\d+): Failure$")
|
||||
_GTEST_FAIL_PATTERN = re.compile(r'(unknown file|\S+:\d+): Failure$')
|
||||
|
||||
def __init__(self):
|
||||
self._last_gtest_name = "Unknown test"
|
||||
self._last_gtest_name = 'Unknown test'
|
||||
|
||||
def parse_error(self, line):
|
||||
gtest_name_match = self._GTEST_NAME_PATTERN.match(line)
|
||||
@@ -43,13 +45,14 @@ class GTestErrorParser(ErrorParserBase):
|
||||
return None
|
||||
gtest_fail_match = self._GTEST_FAIL_PATTERN.match(line)
|
||||
if gtest_fail_match:
|
||||
return "%s failed: %s" % (self._last_gtest_name, gtest_fail_match.group(1))
|
||||
return '%s failed: %s' % (
|
||||
self._last_gtest_name, gtest_fail_match.group(1))
|
||||
return None
|
||||
|
||||
|
||||
class MatchErrorParser(ErrorParserBase):
|
||||
"""A simple parser that returns the whole line if it matches the pattern."""
|
||||
|
||||
'''A simple parser that returns the whole line if it matches the pattern.
|
||||
'''
|
||||
def __init__(self, pattern):
|
||||
self._pattern = re.compile(pattern)
|
||||
|
||||
@@ -66,104 +69,97 @@ class CompilerErrorParser(MatchErrorParser):
|
||||
# format (link error):
|
||||
# '<filename>:<line #>: error: <error msg>'
|
||||
# The below regex catches both
|
||||
super(CompilerErrorParser, self).__init__(r"\S+:\d+: error:")
|
||||
super(CompilerErrorParser, self).__init__(r'\S+:\d+: error:')
|
||||
|
||||
|
||||
class ScanBuildErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super(ScanBuildErrorParser, self).__init__(r"scan-build: \d+ bugs found.$")
|
||||
super(ScanBuildErrorParser, self).__init__(
|
||||
r'scan-build: \d+ bugs found.$')
|
||||
|
||||
|
||||
class DbCrashErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super(DbCrashErrorParser, self).__init__(r"\*\*\*.*\^$|TEST FAILED.")
|
||||
super(DbCrashErrorParser, self).__init__(r'\*\*\*.*\^$|TEST FAILED.')
|
||||
|
||||
|
||||
class WriteStressErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super(WriteStressErrorParser, self).__init__(
|
||||
r"ERROR: write_stress died with exitcode=\d+"
|
||||
)
|
||||
r'ERROR: write_stress died with exitcode=\d+')
|
||||
|
||||
|
||||
class AsanErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super(AsanErrorParser, self).__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(UbsanErrorParser, self).__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(ValgrindErrorParser, self).__init__(r"==\d+== ERROR SUMMARY:")
|
||||
super(ValgrindErrorParser, self).__init__(r'==\d+== ERROR SUMMARY:')
|
||||
|
||||
|
||||
class CompatErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super(CompatErrorParser, self).__init__(r"==== .*[Ee]rror.* ====$")
|
||||
super(CompatErrorParser, self).__init__(r'==== .*[Ee]rror.* ====$')
|
||||
|
||||
|
||||
class TsanErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super(TsanErrorParser, self).__init__(r"WARNING: ThreadSanitizer:")
|
||||
super(TsanErrorParser, self).__init__(r'WARNING: ThreadSanitizer:')
|
||||
|
||||
|
||||
_TEST_NAME_TO_PARSERS = {
|
||||
"punit": [CompilerErrorParser, GTestErrorParser],
|
||||
"unit": [CompilerErrorParser, GTestErrorParser],
|
||||
"release": [CompilerErrorParser, GTestErrorParser],
|
||||
"unit_481": [CompilerErrorParser, GTestErrorParser],
|
||||
"release_481": [CompilerErrorParser, GTestErrorParser],
|
||||
"clang_unit": [CompilerErrorParser, GTestErrorParser],
|
||||
"clang_release": [CompilerErrorParser, GTestErrorParser],
|
||||
"clang_analyze": [CompilerErrorParser, ScanBuildErrorParser],
|
||||
"code_cov": [CompilerErrorParser, GTestErrorParser],
|
||||
"unity": [CompilerErrorParser, GTestErrorParser],
|
||||
"lite": [CompilerErrorParser],
|
||||
"lite_test": [CompilerErrorParser, GTestErrorParser],
|
||||
"stress_crash": [CompilerErrorParser, DbCrashErrorParser],
|
||||
"stress_crash_with_atomic_flush": [CompilerErrorParser, DbCrashErrorParser],
|
||||
"stress_crash_with_txn": [CompilerErrorParser, DbCrashErrorParser],
|
||||
"write_stress": [CompilerErrorParser, WriteStressErrorParser],
|
||||
"asan": [CompilerErrorParser, GTestErrorParser, AsanErrorParser],
|
||||
"asan_crash": [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser],
|
||||
"asan_crash_with_atomic_flush": [
|
||||
CompilerErrorParser,
|
||||
AsanErrorParser,
|
||||
DbCrashErrorParser,
|
||||
],
|
||||
"asan_crash_with_txn": [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser],
|
||||
"ubsan": [CompilerErrorParser, GTestErrorParser, UbsanErrorParser],
|
||||
"ubsan_crash": [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser],
|
||||
"ubsan_crash_with_atomic_flush": [
|
||||
CompilerErrorParser,
|
||||
UbsanErrorParser,
|
||||
DbCrashErrorParser,
|
||||
],
|
||||
"ubsan_crash_with_txn": [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser],
|
||||
"valgrind": [CompilerErrorParser, GTestErrorParser, ValgrindErrorParser],
|
||||
"tsan": [CompilerErrorParser, GTestErrorParser, TsanErrorParser],
|
||||
"format_compatible": [CompilerErrorParser, CompatErrorParser],
|
||||
"run_format_compatible": [CompilerErrorParser, CompatErrorParser],
|
||||
"no_compression": [CompilerErrorParser, GTestErrorParser],
|
||||
"run_no_compression": [CompilerErrorParser, GTestErrorParser],
|
||||
"regression": [CompilerErrorParser],
|
||||
"run_regression": [CompilerErrorParser],
|
||||
'punit': [CompilerErrorParser, GTestErrorParser],
|
||||
'unit': [CompilerErrorParser, GTestErrorParser],
|
||||
'release': [CompilerErrorParser, GTestErrorParser],
|
||||
'unit_481': [CompilerErrorParser, GTestErrorParser],
|
||||
'release_481': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_unit': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_release': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_analyze': [CompilerErrorParser, ScanBuildErrorParser],
|
||||
'code_cov': [CompilerErrorParser, GTestErrorParser],
|
||||
'unity': [CompilerErrorParser, GTestErrorParser],
|
||||
'lite': [CompilerErrorParser],
|
||||
'lite_test': [CompilerErrorParser, GTestErrorParser],
|
||||
'stress_crash': [CompilerErrorParser, DbCrashErrorParser],
|
||||
'stress_crash_with_atomic_flush': [CompilerErrorParser, DbCrashErrorParser],
|
||||
'stress_crash_with_txn': [CompilerErrorParser, DbCrashErrorParser],
|
||||
'write_stress': [CompilerErrorParser, WriteStressErrorParser],
|
||||
'asan': [CompilerErrorParser, GTestErrorParser, AsanErrorParser],
|
||||
'asan_crash': [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser],
|
||||
'asan_crash_with_atomic_flush': [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser],
|
||||
'asan_crash_with_txn': [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser],
|
||||
'ubsan': [CompilerErrorParser, GTestErrorParser, UbsanErrorParser],
|
||||
'ubsan_crash': [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser],
|
||||
'ubsan_crash_with_atomic_flush': [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser],
|
||||
'ubsan_crash_with_txn': [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser],
|
||||
'valgrind': [CompilerErrorParser, GTestErrorParser, ValgrindErrorParser],
|
||||
'tsan': [CompilerErrorParser, GTestErrorParser, TsanErrorParser],
|
||||
'format_compatible': [CompilerErrorParser, CompatErrorParser],
|
||||
'run_format_compatible': [CompilerErrorParser, CompatErrorParser],
|
||||
'no_compression': [CompilerErrorParser, GTestErrorParser],
|
||||
'run_no_compression': [CompilerErrorParser, GTestErrorParser],
|
||||
'regression': [CompilerErrorParser],
|
||||
'run_regression': [CompilerErrorParser],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
return "Usage: %s <test name>" % sys.argv[0]
|
||||
return 'Usage: %s <test name>' % sys.argv[0]
|
||||
test_name = sys.argv[1]
|
||||
if test_name not in _TEST_NAME_TO_PARSERS:
|
||||
return "Unknown test name: %s" % test_name
|
||||
return 'Unknown test name: %s' % test_name
|
||||
|
||||
error_parsers = []
|
||||
for parser_cls in _TEST_NAME_TO_PARSERS[test_name]:
|
||||
@@ -177,5 +173,5 @@ def main():
|
||||
print(error_msg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
@@ -21,48 +21,38 @@ LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_SNAPPY; then
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
else
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
else
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
if ! test $ROCKSDB_DISABLE_ZLIB; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
fi
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_BZIP; then
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
fi
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_LZ4; then
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZSTD; then
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD -DZSTD_STATIC_LINKING_ONLY"
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD -DZSTD_STATIC_LINKING_ONLY"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
@@ -147,7 +137,7 @@ else
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
@@ -172,4 +162,6 @@ else
|
||||
LUA_LIB=" $LUA_PATH/lib/liblua_pic.a"
|
||||
fi
|
||||
|
||||
USE_FOLLY_DISTRIBUTED_MUTEX=1
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ compiler and also
|
||||
# uses jemalloc
|
||||
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies_4.8.1.sh"
|
||||
|
||||
# location of libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# location of glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
# location of snappy headers and libraries
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include"
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP2_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP2_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags.a"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include"
|
||||
JEMALLOC_LIB="$JEMALLOC_BASE/lib/libjemalloc.a"
|
||||
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
|
||||
# location of tbb
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
|
||||
test "$USE_SSE" || USE_SSE=1
|
||||
export USE_SSE
|
||||
test "$PORTABLE" || PORTABLE=1
|
||||
export PORTABLE
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP2_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
CXX="$GCC_BASE/bin/gcc-ar"
|
||||
|
||||
CFLAGS="-B$BINUTILS/gold -m64 -mtune=generic"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
CLANG_BIN="$CLANG_BASE/bin"
|
||||
CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_INCLUDE="$CLANG_LIB/clang/*/include"
|
||||
CC="$CLANG_BIN/clang"
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
AR="$CLANG_BIN/llvm-ar"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include/"
|
||||
|
||||
CFLAGS="-B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.8.1 "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.8.1/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2 -DLZ4 -DZSTD -DNUMA -DTBB"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP2_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP2_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
LUA_PATH="$LUA_BASE"
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE LUA_PATH
|
||||
@@ -0,0 +1,170 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ and clang compilers and also
|
||||
# uses jemalloc
|
||||
# Environment variables that change the behavior of this script:
|
||||
# PIC_BUILD -- if true, it will only take pic versions of libraries from fbcode. libraries that don't have pic variant will not be included
|
||||
|
||||
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies_platform007.sh"
|
||||
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/7.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
else
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags.a"
|
||||
else
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc.a"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
CFLAGS+=" -DNUMA"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
fi
|
||||
|
||||
# location of TBB
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
else
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# location of LIBURING
|
||||
LIBURING_INCLUDE=" -isystem $LIBURING_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
LIBURING_LIBS="$LIBURING_BASE/lib/liburing.a"
|
||||
else
|
||||
LIBURING_LIBS="$LIBURING_BASE/lib/liburing_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DLIBURING"
|
||||
|
||||
test "$USE_SSE" || USE_SSE=1
|
||||
export USE_SSE
|
||||
test "$PORTABLE" || PORTABLE=1
|
||||
export PORTABLE
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE $LIBURING_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
CLANG_BIN="$CLANG_BASE/bin"
|
||||
CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/tools/clang/tools/scan-build/bin/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
AR="$GCC_BASE/bin/gcc-ar"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
CLANG_INCLUDE="$CLANG_LIB/clang/stable/include"
|
||||
CC="$CLANG_BIN/clang"
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
AR="$CLANG_BIN/llvm-ar"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CFLAGS+=" -Wno-expansion-to-defined "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42 -DROCKSDB_IOURING_PRESENT"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS $LIBURING_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform007/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS $LIBURING_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
# lua not supported because it's on track for deprecation, I think
|
||||
LUA_PATH=
|
||||
LUA_LIB=
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
+36
-54
@@ -9,19 +9,17 @@
|
||||
|
||||
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies_platform010.sh"
|
||||
source "$BASEDIR/dependencies_platform009.sh"
|
||||
|
||||
# Disallow using libraries from default locations as they might not be compatible with platform010 libraries.
|
||||
CFLAGS=" --sysroot=/DOES/NOT/EXIST"
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/trunk"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib -B$LIBGCC_BASE/lib/gcc/x86_64-facebook-linux/trunk/"
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/9.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
GLIBC_LIBS+=" -B$GLIBC_BASE/lib"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
MAYBE_PIC=
|
||||
@@ -29,38 +27,28 @@ else
|
||||
MAYBE_PIC=_pic
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_SNAPPY; then
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
fi
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZLIB; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
fi
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_BZIP; then
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
fi
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_LZ4; then
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZSTD; then
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
fi
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
@@ -118,19 +106,9 @@ if [ -z "$USE_CLANG" ]; then
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
AR="$GCC_BASE/bin/gcc-ar"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS -nostdinc -nostdlib"
|
||||
CFLAGS+=" -I$GCC_BASE/include"
|
||||
CFLAGS+=" -isystem $GCC_BASE/lib/gcc/x86_64-redhat-linux-gnu/11.2.1/include"
|
||||
CFLAGS+=" -isystem $GCC_BASE/lib/gcc/x86_64-redhat-linux-gnu/11.2.1/install-tools/include"
|
||||
CFLAGS+=" -isystem $GCC_BASE/lib/gcc/x86_64-redhat-linux-gnu/11.2.1/include-fixed/"
|
||||
CFLAGS+=" -B$BINUTILS"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -I$GLIBC_INCLUDE"
|
||||
CFLAGS+=" -I$LIBGCC_BASE/include"
|
||||
CFLAGS+=" -I$LIBGCC_BASE/include/c++/11.x/"
|
||||
CFLAGS+=" -I$LIBGCC_BASE/include/c++/11.x/x86_64-facebook-linux/"
|
||||
CFLAGS+=" -I$LIBGCC_BASE/include/c++/11.x/backward"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE -I$GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
@@ -139,28 +117,28 @@ else
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
AR="$CLANG_BIN/llvm-ar"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/trunk "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/trunk/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/9.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/9.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CFLAGS+=" -Wno-expansion-to-defined "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_IOURING_PRESENT"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42 -DROCKSDB_IOURING_PRESENT"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS $LIBURING_LIBS $BENCHMARK_LIBS"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform010/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform009/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform010/lib"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform009/lib"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=$GCC_BASE/lib64"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
@@ -172,4 +150,8 @@ EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GF
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
# lua not supported because it's on track for deprecation, I think
|
||||
LUA_PATH=
|
||||
LUA_LIB=
|
||||
|
||||
export CC CXX AR AS CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
@@ -137,11 +137,11 @@ then
|
||||
# should be relevant for formatting fixes.
|
||||
FORMAT_UPSTREAM_MERGE_BASE="$(git merge-base "$FORMAT_UPSTREAM" HEAD)"
|
||||
# Get the differences
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1)
|
||||
echo "Checking format of changes not yet in $FORMAT_UPSTREAM..."
|
||||
else
|
||||
# Check the format of uncommitted lines,
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1)
|
||||
echo "Checking format of uncommitted changes..."
|
||||
fi
|
||||
|
||||
@@ -149,9 +149,6 @@ if [ -z "$diffs" ]
|
||||
then
|
||||
echo "Nothing needs to be reformatted!"
|
||||
exit 0
|
||||
elif [ $? -ne 1 ]; then
|
||||
# CLANG_FORMAT_DIFF will exit on 1 while there is suggested changes.
|
||||
exit $?
|
||||
elif [ $CHECK_ONLY ]
|
||||
then
|
||||
echo "Your change has unformatted code. Please run make format!"
|
||||
@@ -173,6 +170,11 @@ echo "$diffs" |
|
||||
sed -e "s/\(^-.*$\)/`echo -e \"$COLOR_RED\1$COLOR_END\"`/" |
|
||||
sed -e "s/\(^+.*$\)/`echo -e \"$COLOR_GREEN\1$COLOR_END\"`/"
|
||||
|
||||
if [[ "$OPT" == *"-DTRAVIS"* ]]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "Would you like to fix the format automatically (y/n): \c"
|
||||
|
||||
# Make sure under any mode, we can read user input.
|
||||
|
||||
@@ -1170,7 +1170,7 @@ sub parse_env_var {
|
||||
# Check if any variables contain \n
|
||||
if(my @v = map { s/BASH_FUNC_(.*)\(\)/$1/; $_ } grep { $ENV{$_}=~/\n/ } @vars) {
|
||||
# \n is bad for csh and will cause it to fail.
|
||||
$Global::envwarn = ::shell_quote_scalar(q{echo $SHELL | grep -E "/t?csh" > /dev/null && echo CSH/TCSH DO NOT SUPPORT newlines IN VARIABLES/FUNCTIONS. Unset }."@v".q{ && exec false;}."\n\n") . $Global::envwarn;
|
||||
$Global::envwarn = ::shell_quote_scalar(q{echo $SHELL | egrep "/t?csh" > /dev/null && echo CSH/TCSH DO NOT SUPPORT newlines IN VARIABLES/FUNCTIONS. Unset }."@v".q{ && exec false;}."\n\n") . $Global::envwarn;
|
||||
}
|
||||
|
||||
if(not @qcsh) { push @qcsh, "true"; }
|
||||
@@ -1561,7 +1561,7 @@ sub save_stdin_stdout_stderr {
|
||||
::die_bug("Can't dup STDERR: $!");
|
||||
open $Global::original_stdin, "<&", "STDIN" or
|
||||
::die_bug("Can't dup STDIN: $!");
|
||||
$Global::is_terminal = (-t $Global::original_stderr) && !$ENV{'CIRCLECI'}&& !$ENV{'GITHUB_ACTIONS'} && !$ENV{'TRAVIS'};
|
||||
$Global::is_terminal = (-t $Global::original_stderr) && !$ENV{'CIRCLECI'} && !$ENV{'TRAVIS'};
|
||||
}
|
||||
|
||||
sub enough_file_handles {
|
||||
@@ -1916,8 +1916,7 @@ sub drain_job_queue {
|
||||
} elsif (not $ps_reported and (time() - $last_progress_time) >= 60) {
|
||||
# No progress in at least 60 seconds: run ps
|
||||
print $Global::original_stderr "\n";
|
||||
my $script_dir = ::dirname($0);
|
||||
system("$script_dir/ps_with_stack || ps -wwf");
|
||||
system("ps", "-wf");
|
||||
$ps_reported = 1;
|
||||
}
|
||||
$last_left = $Global::left;
|
||||
|
||||
Executable
+209
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env python2.7
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
import argparse
|
||||
import commands
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
import time
|
||||
|
||||
|
||||
#
|
||||
# Simple logger
|
||||
#
|
||||
|
||||
class Log:
|
||||
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.f = open(self.filename, 'w+', 0)
|
||||
|
||||
def caption(self, str):
|
||||
line = "\n##### %s #####\n" % str
|
||||
if self.f:
|
||||
self.f.write("%s \n" % line)
|
||||
else:
|
||||
print(line)
|
||||
|
||||
def error(self, str):
|
||||
data = "\n\n##### ERROR ##### %s" % str
|
||||
if self.f:
|
||||
self.f.write("%s \n" % data)
|
||||
else:
|
||||
print(data)
|
||||
|
||||
def log(self, str):
|
||||
if self.f:
|
||||
self.f.write("%s \n" % str)
|
||||
else:
|
||||
print(str)
|
||||
|
||||
#
|
||||
# Shell Environment
|
||||
#
|
||||
|
||||
|
||||
class Env(object):
|
||||
|
||||
def __init__(self, logfile, tests):
|
||||
self.tests = tests
|
||||
self.log = Log(logfile)
|
||||
|
||||
def shell(self, cmd, path=os.getcwd()):
|
||||
if path:
|
||||
os.chdir(path)
|
||||
|
||||
self.log.log("==== shell session ===========================")
|
||||
self.log.log("%s> %s" % (path, cmd))
|
||||
status = subprocess.call("cd %s; %s" % (path, cmd), shell=True,
|
||||
stdout=self.log.f, stderr=self.log.f)
|
||||
self.log.log("status = %s" % status)
|
||||
self.log.log("============================================== \n\n")
|
||||
return status
|
||||
|
||||
def GetOutput(self, cmd, path=os.getcwd()):
|
||||
if path:
|
||||
os.chdir(path)
|
||||
|
||||
self.log.log("==== shell session ===========================")
|
||||
self.log.log("%s> %s" % (path, cmd))
|
||||
status, out = commands.getstatusoutput(cmd)
|
||||
self.log.log("status = %s" % status)
|
||||
self.log.log("out = %s" % out)
|
||||
self.log.log("============================================== \n\n")
|
||||
return status, out
|
||||
|
||||
#
|
||||
# Pre-commit checker
|
||||
#
|
||||
|
||||
|
||||
class PreCommitChecker(Env):
|
||||
|
||||
def __init__(self, args):
|
||||
Env.__init__(self, args.logfile, args.tests)
|
||||
self.ignore_failure = args.ignore_failure
|
||||
|
||||
#
|
||||
# Get commands for a given job from the determinator file
|
||||
#
|
||||
def get_commands(self, test):
|
||||
status, out = self.GetOutput(
|
||||
"RATIO=1 build_tools/rocksdb-lego-determinator %s" % test, ".")
|
||||
return status, out
|
||||
|
||||
#
|
||||
# Run a specific CI job
|
||||
#
|
||||
def run_test(self, test):
|
||||
self.log.caption("Running test %s locally" % test)
|
||||
|
||||
# get commands for the CI job determinator
|
||||
status, cmds = self.get_commands(test)
|
||||
if status != 0:
|
||||
self.log.error("Error getting commands for test %s" % test)
|
||||
return False
|
||||
|
||||
# Parse the JSON to extract the commands to run
|
||||
cmds = re.findall("'shell':'([^\']*)'", cmds)
|
||||
|
||||
if len(cmds) == 0:
|
||||
self.log.log("No commands found")
|
||||
return False
|
||||
|
||||
# Run commands
|
||||
for cmd in cmds:
|
||||
# Replace J=<..> with the local environment variable
|
||||
if "J" in os.environ:
|
||||
cmd = cmd.replace("J=1", "J=%s" % os.environ["J"])
|
||||
cmd = cmd.replace("make ", "make -j%s " % os.environ["J"])
|
||||
# Run the command
|
||||
status = self.shell(cmd, ".")
|
||||
if status != 0:
|
||||
self.log.error("Error running command %s for test %s"
|
||||
% (cmd, test))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
#
|
||||
# Run specified CI jobs
|
||||
#
|
||||
def run_tests(self):
|
||||
if not self.tests:
|
||||
self.log.error("Invalid args. Please provide tests")
|
||||
return False
|
||||
|
||||
self.print_separator()
|
||||
self.print_row("TEST", "RESULT")
|
||||
self.print_separator()
|
||||
|
||||
result = True
|
||||
for test in self.tests:
|
||||
start_time = time.time()
|
||||
self.print_test(test)
|
||||
result = self.run_test(test)
|
||||
elapsed_min = (time.time() - start_time) / 60
|
||||
if not result:
|
||||
self.log.error("Error running test %s" % test)
|
||||
self.print_result("FAIL (%dm)" % elapsed_min)
|
||||
if not self.ignore_failure:
|
||||
return False
|
||||
result = False
|
||||
else:
|
||||
self.print_result("PASS (%dm)" % elapsed_min)
|
||||
|
||||
self.print_separator()
|
||||
return result
|
||||
|
||||
#
|
||||
# Print a line
|
||||
#
|
||||
def print_separator(self):
|
||||
print("".ljust(60, "-"))
|
||||
|
||||
#
|
||||
# Print two colums
|
||||
#
|
||||
def print_row(self, c0, c1):
|
||||
print("%s%s" % (c0.ljust(40), c1.ljust(20)))
|
||||
|
||||
def print_test(self, test):
|
||||
print(test.ljust(40), end="")
|
||||
sys.stdout.flush()
|
||||
|
||||
def print_result(self, result):
|
||||
print(result.ljust(20))
|
||||
|
||||
#
|
||||
# Main
|
||||
#
|
||||
parser = argparse.ArgumentParser(description='RocksDB pre-commit checker.')
|
||||
|
||||
# --log <logfile>
|
||||
parser.add_argument('--logfile', default='/tmp/precommit-check.log',
|
||||
help='Log file. Default is /tmp/precommit-check.log')
|
||||
# --ignore_failure
|
||||
parser.add_argument('--ignore_failure', action='store_true', default=False,
|
||||
help='Stop when an error occurs')
|
||||
# <test ....>
|
||||
parser.add_argument('tests', nargs='+',
|
||||
help='CI test(s) to run. e.g: unit punit asan tsan ubsan')
|
||||
|
||||
args = parser.parse_args()
|
||||
checker = PreCommitChecker(args)
|
||||
|
||||
print("Please follow log %s" % checker.log.filename)
|
||||
|
||||
if not checker.run_tests():
|
||||
print("Error running tests. Please check log file %s"
|
||||
% checker.log.filename)
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
|
||||
open(my $ps, "-|", "ps -wwf");
|
||||
my $cols_known = 0;
|
||||
my $cmd_col = 0;
|
||||
my $pid_col = 0;
|
||||
while (<$ps>) {
|
||||
print;
|
||||
my @cols = split(/\s+/);
|
||||
|
||||
if (!$cols_known && /CMD/) {
|
||||
# Parse relevant ps column headers
|
||||
for (my $i = 0; $i <= $#cols; $i++) {
|
||||
if ($cols[$i] eq "CMD") {
|
||||
$cmd_col = $i;
|
||||
}
|
||||
if ($cols[$i] eq "PID") {
|
||||
$pid_col = $i;
|
||||
}
|
||||
}
|
||||
$cols_known = 1;
|
||||
} else {
|
||||
my $pid = $cols[$pid_col];
|
||||
my $cmd = $cols[$cmd_col];
|
||||
# Match numeric PID and relative path command
|
||||
# -> The intention is only to dump stack traces for hangs in code under
|
||||
# test, which means we probably just built it and are executing by
|
||||
# relative path (e.g. ./my_test or foo/bar_test) rather then by absolute
|
||||
# path (e.g. /usr/bin/time) or PATH search (e.g. grep).
|
||||
if ($pid =~ /^[0-9]+$/ && $cmd =~ /^[^\/ ]+[\/]/) {
|
||||
print "Dumping stacks for $pid...\n";
|
||||
system("pstack $pid || gdb -batch -p $pid -ex 'thread apply all bt'");
|
||||
}
|
||||
}
|
||||
}
|
||||
close $ps;
|
||||
@@ -360,7 +360,7 @@ function send_to_ods {
|
||||
echo >&2 "ERROR: Key $key doesn't have a value."
|
||||
return
|
||||
fi
|
||||
curl --silent "https://www.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build&key=$key&value=$value" \
|
||||
curl --silent "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build&key=$key&value=$value" \
|
||||
--connect-timeout 60
|
||||
}
|
||||
|
||||
|
||||
Executable
+1365
File diff suppressed because it is too large
Load Diff
@@ -42,7 +42,7 @@ $RunOnly.Add("c_test") | Out-Null
|
||||
$RunOnly.Add("compact_on_deletion_collector_test") | Out-Null
|
||||
$RunOnly.Add("merge_test") | Out-Null
|
||||
$RunOnly.Add("stringappend_test") | Out-Null # Apparently incorrectly written
|
||||
$RunOnly.Add("backup_engine_test") | Out-Null # Disabled
|
||||
$RunOnly.Add("backupable_db_test") | Out-Null # Disabled
|
||||
$RunOnly.Add("timer_queue_test") | Out-Null # Not a gtest
|
||||
|
||||
if($RunAll -and $SuiteRun -ne "") {
|
||||
@@ -491,3 +491,5 @@ if(!$script:success) {
|
||||
}
|
||||
|
||||
exit 0
|
||||
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
# from official ubuntu 20.04
|
||||
FROM ubuntu:20.04
|
||||
# update system
|
||||
RUN apt-get update && 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
|
||||
# install gcc-7, 8, 10, 11, default is 9
|
||||
RUN apt-get install -y gcc-7 g++-7
|
||||
RUN apt-get install -y gcc-8 g++-8
|
||||
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-11 g++-11
|
||||
# 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-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
|
||||
RUN cd ~/benchmark && mkdir build && cd build && cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DBENCHMARK_ENABLE_GTEST_TESTS=0 && ninja && ninja install
|
||||
|
||||
# clean up
|
||||
RUN rm -rf /var/lib/apt/lists/*
|
||||
RUN rm -rf /root/benchmark
|
||||
@@ -9,7 +9,6 @@ OUTPUT=""
|
||||
function log_header()
|
||||
{
|
||||
echo "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved." >> "$OUTPUT"
|
||||
echo "# The file is generated using update_dependencies.sh." >> "$OUTPUT"
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +18,7 @@ function log_variable()
|
||||
}
|
||||
|
||||
|
||||
TP2_LATEST="/data/users/$USER/fbsource/fbcode/third-party2/"
|
||||
TP2_LATEST="/mnt/vol/engshare/fbcode/third-party2"
|
||||
## $1 => lib name
|
||||
## $2 => lib version (if not provided, will try to pick latest)
|
||||
## $3 => platform (if not provided, will try to pick latest gcc)
|
||||
@@ -51,8 +50,6 @@ function get_lib_base()
|
||||
fi
|
||||
|
||||
result=`ls -1d $result/*/ | head -n1`
|
||||
|
||||
echo Finding link $result
|
||||
|
||||
# lib_name => LIB_NAME_BASE
|
||||
local __res_var=${lib_name^^}"_BASE"
|
||||
@@ -64,10 +61,10 @@ function get_lib_base()
|
||||
}
|
||||
|
||||
###########################################################
|
||||
# platform010 dependencies #
|
||||
# platform007 dependencies #
|
||||
###########################################################
|
||||
|
||||
OUTPUT="$BASEDIR/dependencies_platform010.sh"
|
||||
OUTPUT="$BASEDIR/dependencies_platform007.sh"
|
||||
|
||||
rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
@@ -75,32 +72,111 @@ touch "$OUTPUT"
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos8-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/15/platform010/*/`
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/7.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos7-native/*/`
|
||||
|
||||
log_header
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
# Libraries locations
|
||||
get_lib_base libgcc 11.x platform010
|
||||
get_lib_base glibc 2.34 platform010
|
||||
get_lib_base snappy LATEST platform010
|
||||
get_lib_base zlib 1.2.8 platform010
|
||||
get_lib_base bzip2 LATEST platform010
|
||||
get_lib_base lz4 LATEST platform010
|
||||
get_lib_base zstd LATEST platform010
|
||||
get_lib_base gflags LATEST platform010
|
||||
get_lib_base jemalloc LATEST platform010
|
||||
get_lib_base numa LATEST platform010
|
||||
get_lib_base libunwind LATEST platform010
|
||||
get_lib_base tbb 2018_U5 platform010
|
||||
get_lib_base liburing LATEST platform010
|
||||
get_lib_base benchmark LATEST platform010
|
||||
get_lib_base libgcc 7.x platform007
|
||||
get_lib_base glibc 2.26 platform007
|
||||
get_lib_base snappy LATEST platform007
|
||||
get_lib_base zlib LATEST platform007
|
||||
get_lib_base bzip2 LATEST platform007
|
||||
get_lib_base lz4 LATEST platform007
|
||||
get_lib_base zstd LATEST platform007
|
||||
get_lib_base gflags LATEST platform007
|
||||
get_lib_base jemalloc LATEST platform007
|
||||
get_lib_base numa LATEST platform007
|
||||
get_lib_base libunwind LATEST platform007
|
||||
get_lib_base tbb LATEST platform007
|
||||
get_lib_base liburing LATEST platform007
|
||||
|
||||
get_lib_base kernel-headers fb platform010
|
||||
get_lib_base binutils LATEST centos8-native
|
||||
get_lib_base valgrind LATEST platform010
|
||||
get_lib_base lua 5.3.4 platform010
|
||||
get_lib_base kernel-headers fb platform007
|
||||
get_lib_base binutils LATEST centos7-native
|
||||
get_lib_base valgrind LATEST platform007
|
||||
get_lib_base lua 5.3.4 platform007
|
||||
|
||||
git diff $OUTPUT
|
||||
|
||||
###########################################################
|
||||
# 5.x dependencies #
|
||||
###########################################################
|
||||
|
||||
OUTPUT="$BASEDIR/dependencies.sh"
|
||||
|
||||
rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/5.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos7-native/*/`
|
||||
|
||||
log_header
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
# Libraries locations
|
||||
get_lib_base libgcc 5.x gcc-5-glibc-2.23
|
||||
get_lib_base glibc 2.23 gcc-5-glibc-2.23
|
||||
get_lib_base snappy LATEST gcc-5-glibc-2.23
|
||||
get_lib_base zlib LATEST gcc-5-glibc-2.23
|
||||
get_lib_base bzip2 LATEST gcc-5-glibc-2.23
|
||||
get_lib_base lz4 LATEST gcc-5-glibc-2.23
|
||||
get_lib_base zstd LATEST gcc-5-glibc-2.23
|
||||
get_lib_base gflags LATEST gcc-5-glibc-2.23
|
||||
get_lib_base jemalloc LATEST gcc-5-glibc-2.23
|
||||
get_lib_base numa LATEST gcc-5-glibc-2.23
|
||||
get_lib_base libunwind LATEST gcc-5-glibc-2.23
|
||||
get_lib_base tbb LATEST gcc-5-glibc-2.23
|
||||
|
||||
get_lib_base kernel-headers 4.0.9-36_fbk5_2933_gd092e3f gcc-5-glibc-2.23
|
||||
get_lib_base binutils LATEST centos7-native
|
||||
get_lib_base valgrind LATEST gcc-5-glibc-2.23
|
||||
get_lib_base lua 5.2.3 gcc-5-glibc-2.23
|
||||
|
||||
git diff $OUTPUT
|
||||
|
||||
###########################################################
|
||||
# 4.8.1 dependencies #
|
||||
###########################################################
|
||||
|
||||
OUTPUT="$BASEDIR/dependencies_4.8.1.sh"
|
||||
|
||||
rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
|
||||
echo "Writing 4.8.1 dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/4.8.1/centos6-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos6-native/*/`
|
||||
|
||||
log_header
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
# Libraries locations
|
||||
get_lib_base libgcc 4.8.1 gcc-4.8.1-glibc-2.17
|
||||
get_lib_base glibc 2.17 gcc-4.8.1-glibc-2.17
|
||||
get_lib_base snappy LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base zlib LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base bzip2 LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base lz4 LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base zstd LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base gflags LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base jemalloc LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base numa LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base libunwind LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base tbb 4.0_update2 gcc-4.8.1-glibc-2.17
|
||||
|
||||
get_lib_base kernel-headers LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base binutils LATEST centos6-native
|
||||
get_lib_base valgrind 3.8.1 gcc-4.8.1-glibc-2.17
|
||||
get_lib_base lua 5.2.3 centos6-native
|
||||
|
||||
git diff $OUTPUT
|
||||
|
||||
Vendored
+9
-130
@@ -16,8 +16,7 @@
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
const Cache::CacheItemHelper kNoopCacheItemHelper{};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
lru_cache_options_type_info = {
|
||||
{"capacity",
|
||||
@@ -34,98 +33,14 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
{offsetof(struct LRUCacheOptions, high_pri_pool_ratio),
|
||||
OptionType::kDouble, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
{"low_pri_pool_ratio",
|
||||
{offsetof(struct LRUCacheOptions, low_pri_pool_ratio),
|
||||
OptionType::kDouble, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
comp_sec_cache_options_type_info = {
|
||||
{"capacity",
|
||||
{offsetof(struct CompressedSecondaryCacheOptions, capacity),
|
||||
OptionType::kSizeT, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
{"num_shard_bits",
|
||||
{offsetof(struct CompressedSecondaryCacheOptions, num_shard_bits),
|
||||
OptionType::kInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
{"compression_type",
|
||||
{offsetof(struct CompressedSecondaryCacheOptions, compression_type),
|
||||
OptionType::kCompressionType, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
{"compress_format_version",
|
||||
{offsetof(struct CompressedSecondaryCacheOptions,
|
||||
compress_format_version),
|
||||
OptionType::kUInt32T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
{"enable_custom_split_merge",
|
||||
{offsetof(struct CompressedSecondaryCacheOptions,
|
||||
enable_custom_split_merge),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
};
|
||||
|
||||
namespace {
|
||||
static void NoopDelete(Cache::ObjectPtr /*obj*/,
|
||||
MemoryAllocator* /*allocator*/) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
static size_t SliceSize(Cache::ObjectPtr obj) {
|
||||
return static_cast<Slice*>(obj)->size();
|
||||
}
|
||||
|
||||
static Status SliceSaveTo(Cache::ObjectPtr from_obj, size_t from_offset,
|
||||
size_t length, char* out) {
|
||||
const Slice& slice = *static_cast<Slice*>(from_obj);
|
||||
std::memcpy(out, slice.data() + from_offset, length);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static Status NoopCreate(const Slice& /*data*/, CompressionType /*type*/,
|
||||
CacheTier /*source*/, Cache::CreateContext* /*ctx*/,
|
||||
MemoryAllocator* /*allocator*/,
|
||||
Cache::ObjectPtr* /*out_obj*/,
|
||||
size_t* /*out_charge*/) {
|
||||
assert(false);
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
static Cache::CacheItemHelper kBasicCacheItemHelper(CacheEntryRole::kMisc,
|
||||
&NoopDelete);
|
||||
} // namespace
|
||||
|
||||
const Cache::CacheItemHelper kSliceCacheItemHelper{
|
||||
CacheEntryRole::kMisc, &NoopDelete, &SliceSize,
|
||||
&SliceSaveTo, &NoopCreate, &kBasicCacheItemHelper,
|
||||
};
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status SecondaryCache::CreateFromString(
|
||||
const ConfigOptions& config_options, const std::string& value,
|
||||
std::shared_ptr<SecondaryCache>* result) {
|
||||
if (value.find("compressed_secondary_cache://") == 0) {
|
||||
std::string args = value;
|
||||
args.erase(0, std::strlen("compressed_secondary_cache://"));
|
||||
Status status;
|
||||
std::shared_ptr<SecondaryCache> sec_cache;
|
||||
|
||||
CompressedSecondaryCacheOptions sec_cache_opts;
|
||||
status = OptionTypeInfo::ParseStruct(config_options, "",
|
||||
&comp_sec_cache_options_type_info, "",
|
||||
args, &sec_cache_opts);
|
||||
if (status.ok()) {
|
||||
sec_cache = NewCompressedSecondaryCache(sec_cache_opts);
|
||||
}
|
||||
|
||||
|
||||
if (status.ok()) {
|
||||
result->swap(sec_cache);
|
||||
}
|
||||
return status;
|
||||
} else {
|
||||
return LoadSharedObject<SecondaryCache>(config_options, value, result);
|
||||
}
|
||||
return LoadSharedObject<SecondaryCache>(config_options, value, nullptr,
|
||||
result);
|
||||
}
|
||||
|
||||
Status Cache::CreateFromString(const ConfigOptions& config_options,
|
||||
@@ -136,6 +51,7 @@ Status Cache::CreateFromString(const ConfigOptions& config_options,
|
||||
if (value.find('=') == std::string::npos) {
|
||||
cache = NewLRUCache(ParseSizeT(value));
|
||||
} else {
|
||||
#ifndef ROCKSDB_LITE
|
||||
LRUCacheOptions cache_opts;
|
||||
status = OptionTypeInfo::ParseStruct(config_options, "",
|
||||
&lru_cache_options_type_info, "",
|
||||
@@ -143,51 +59,14 @@ Status Cache::CreateFromString(const ConfigOptions& config_options,
|
||||
if (status.ok()) {
|
||||
cache = NewLRUCache(cache_opts);
|
||||
}
|
||||
#else
|
||||
(void)config_options;
|
||||
status = Status::NotSupported("Cannot load cache in LITE mode ", value);
|
||||
#endif //! ROCKSDB_LITE
|
||||
}
|
||||
if (status.ok()) {
|
||||
result->swap(cache);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
bool Cache::AsyncLookupHandle::IsReady() {
|
||||
return pending_handle == nullptr || pending_handle->IsReady();
|
||||
}
|
||||
|
||||
bool Cache::AsyncLookupHandle::IsPending() { return pending_handle != nullptr; }
|
||||
|
||||
Cache::Handle* Cache::AsyncLookupHandle::Result() {
|
||||
assert(!IsPending());
|
||||
return result_handle;
|
||||
}
|
||||
|
||||
void Cache::StartAsyncLookup(AsyncLookupHandle& async_handle) {
|
||||
async_handle.found_dummy_entry = false; // in case re-used
|
||||
assert(!async_handle.IsPending());
|
||||
async_handle.result_handle =
|
||||
Lookup(async_handle.key, async_handle.helper, async_handle.create_context,
|
||||
async_handle.priority, async_handle.stats);
|
||||
}
|
||||
|
||||
Cache::Handle* Cache::Wait(AsyncLookupHandle& async_handle) {
|
||||
WaitAll(&async_handle, 1);
|
||||
return async_handle.Result();
|
||||
}
|
||||
|
||||
void Cache::WaitAll(AsyncLookupHandle* async_handles, size_t count) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (async_handles[i].IsPending()) {
|
||||
// If a pending handle gets here, it should be marked at "to be handled
|
||||
// by a caller" by that caller erasing the pending_cache on it.
|
||||
assert(async_handles[i].pending_cache == nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Cache::SetEvictionCallback(EvictionCallback&& fn) {
|
||||
// Overwriting non-empty with non-empty could indicate a bug
|
||||
assert(!eviction_callback_ || !fn);
|
||||
eviction_callback_ = std::move(fn);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
Vendored
+170
-423
@@ -12,13 +12,10 @@
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
#include "cache/cache_key.h"
|
||||
#include "cache/sharded_cache.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "monitoring/histogram.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -28,12 +25,10 @@
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
#include "table/block_based/cachable_entry.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/distributed_mutex.h"
|
||||
#include "util/gflags_compat.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/random.h"
|
||||
#include "util/stderr_logger.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
@@ -46,43 +41,21 @@ static constexpr uint64_t GiB = MiB << 10;
|
||||
DEFINE_uint32(threads, 16, "Number of concurrent threads to run.");
|
||||
DEFINE_uint64(cache_size, 1 * GiB,
|
||||
"Number of bytes to use as a cache of uncompressed data.");
|
||||
DEFINE_int32(num_shard_bits, -1,
|
||||
"ShardedCacheOptions::shard_bits. Default = auto");
|
||||
DEFINE_int32(
|
||||
eviction_effort_cap,
|
||||
ROCKSDB_NAMESPACE::HyperClockCacheOptions(1, 1).eviction_effort_cap,
|
||||
"HyperClockCacheOptions::eviction_effort_cap");
|
||||
DEFINE_uint32(num_shard_bits, 6, "shard_bits.");
|
||||
|
||||
DEFINE_double(resident_ratio, 0.25,
|
||||
"Ratio of keys fitting in cache to keyspace.");
|
||||
DEFINE_uint64(ops_per_thread, 2000000U, "Number of operations per thread.");
|
||||
DEFINE_uint32(value_bytes, 8 * KiB, "Size of each value added.");
|
||||
DEFINE_uint32(value_bytes_estimate, 0,
|
||||
"If > 0, overrides estimated_entry_charge or "
|
||||
"min_avg_entry_charge depending on cache_type.");
|
||||
|
||||
DEFINE_int32(
|
||||
degenerate_hash_bits, 0,
|
||||
"With HCC, fix this many hash bits to increase table hash collisions");
|
||||
DEFINE_uint32(skew, 5, "Degree of skew in key selection. 0 = no skew");
|
||||
DEFINE_uint32(skew, 5, "Degree of skew in key selection");
|
||||
DEFINE_bool(populate_cache, true, "Populate cache before operations");
|
||||
|
||||
DEFINE_double(pinned_ratio, 0.25,
|
||||
"Keep roughly this portion of entries pinned in cache.");
|
||||
DEFINE_double(
|
||||
vary_capacity_ratio, 0.0,
|
||||
"If greater than 0.0, will periodically vary the capacity between this "
|
||||
"ratio less than full size and full size. If vary_capacity_ratio + "
|
||||
"pinned_ratio is close to or exceeds 1.0, the cache might thrash.");
|
||||
|
||||
DEFINE_uint32(lookup_insert_percent, 82,
|
||||
DEFINE_uint32(lookup_insert_percent, 87,
|
||||
"Ratio of lookup (+ insert on not found) to total workload "
|
||||
"(expressed as a percentage)");
|
||||
DEFINE_uint32(insert_percent, 2,
|
||||
"Ratio of insert to total workload (expressed as a percentage)");
|
||||
DEFINE_uint32(blind_insert_percent, 5,
|
||||
"Ratio of insert without keeping handle to total workload "
|
||||
"(expressed as a percentage)");
|
||||
DEFINE_uint32(lookup_percent, 10,
|
||||
"Ratio of lookup to total workload (expressed as a percentage)");
|
||||
DEFINE_uint32(erase_percent, 1,
|
||||
@@ -96,39 +69,14 @@ DEFINE_uint32(
|
||||
|
||||
DEFINE_uint32(gather_stats_entries_per_lock, 256,
|
||||
"For Cache::ApplyToAllEntries");
|
||||
|
||||
DEFINE_uint32(usleep, 0, "Sleep up to this many microseconds after each op.");
|
||||
|
||||
DEFINE_bool(lean, false,
|
||||
"If true, no additional computation is performed besides cache "
|
||||
"operations.");
|
||||
|
||||
DEFINE_bool(early_exit, false,
|
||||
"Exit before deallocating most memory. Good for malloc stats, e.g."
|
||||
"MALLOC_CONF=\"stats_print:true\"");
|
||||
|
||||
DEFINE_bool(histograms, true,
|
||||
"Whether to track and print histogram statistics.");
|
||||
|
||||
DEFINE_bool(report_problems, true, "Whether to ReportProblems() at the end.");
|
||||
|
||||
DEFINE_uint32(seed, 0, "Hashing/random seed to use. 0 = choose at random");
|
||||
|
||||
DEFINE_bool(skewed, false, "If true, skew the key access distribution");
|
||||
#ifndef ROCKSDB_LITE
|
||||
DEFINE_string(secondary_cache_uri, "",
|
||||
"Full URI for creating a custom secondary cache object");
|
||||
static class std::shared_ptr<ROCKSDB_NAMESPACE::SecondaryCache> secondary_cache;
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
DEFINE_string(cache_type, "lru_cache", "Type of block cache.");
|
||||
|
||||
DEFINE_bool(use_jemalloc_no_dump_allocator, false,
|
||||
"Whether to use JemallocNoDumpAllocator");
|
||||
|
||||
DEFINE_uint32(jemalloc_no_dump_allocator_num_arenas,
|
||||
ROCKSDB_NAMESPACE::JemallocAllocatorOptions().num_arenas,
|
||||
"JemallocNodumpAllocator::num_arenas");
|
||||
|
||||
DEFINE_bool(jemalloc_no_dump_allocator_limit_tcache_size,
|
||||
ROCKSDB_NAMESPACE::JemallocAllocatorOptions().limit_tcache_size,
|
||||
"JemallocNodumpAllocator::limit_tcache_size");
|
||||
DEFINE_bool(use_clock_cache, false, "");
|
||||
|
||||
// ## BEGIN stress_cache_key sub-tool options ##
|
||||
// See class StressCacheKey below.
|
||||
@@ -157,8 +105,6 @@ DEFINE_uint32(
|
||||
"(-stress_cache_key) Simulated file size in MiB, for accounting purposes");
|
||||
DEFINE_uint32(sck_reopen_nfiles, 100,
|
||||
"(-stress_cache_key) Simulate DB re-open average every n files");
|
||||
DEFINE_uint32(sck_newdb_nreopen, 1000,
|
||||
"(-stress_cache_key) Simulate new DB average every n re-opens");
|
||||
DEFINE_uint32(sck_restarts_per_day, 24,
|
||||
"(-stress_cache_key) Average simulated process restarts per day "
|
||||
"(across DBs)");
|
||||
@@ -192,6 +138,9 @@ class SharedState {
|
||||
public:
|
||||
explicit SharedState(CacheBench* cache_bench)
|
||||
: cv_(&mu_),
|
||||
num_initialized_(0),
|
||||
start_(false),
|
||||
num_done_(0),
|
||||
cache_bench_(cache_bench) {}
|
||||
|
||||
~SharedState() {}
|
||||
@@ -214,31 +163,15 @@ class SharedState {
|
||||
|
||||
bool Started() const { return start_; }
|
||||
|
||||
void AddLookupStats(uint64_t hits, uint64_t misses, size_t pinned_count) {
|
||||
MutexLock l(&mu_);
|
||||
lookup_count_ += hits + misses;
|
||||
lookup_hits_ += hits;
|
||||
pinned_count_ += pinned_count;
|
||||
}
|
||||
|
||||
double GetLookupHitRatio() const {
|
||||
return 1.0 * lookup_hits_ / lookup_count_;
|
||||
}
|
||||
|
||||
size_t GetPinnedCount() const { return pinned_count_; }
|
||||
|
||||
private:
|
||||
port::Mutex mu_;
|
||||
port::CondVar cv_;
|
||||
|
||||
CacheBench* cache_bench_;
|
||||
uint64_t num_initialized_;
|
||||
bool start_;
|
||||
uint64_t num_done_;
|
||||
|
||||
uint64_t num_initialized_ = 0;
|
||||
bool start_ = false;
|
||||
uint64_t num_done_ = 0;
|
||||
uint64_t lookup_count_ = 0;
|
||||
uint64_t lookup_hits_ = 0;
|
||||
size_t pinned_count_ = 0;
|
||||
CacheBench* cache_bench_;
|
||||
};
|
||||
|
||||
// Per-thread state for concurrent executions of the same benchmark.
|
||||
@@ -250,32 +183,26 @@ struct ThreadState {
|
||||
uint64_t duration_us = 0;
|
||||
|
||||
ThreadState(uint32_t index, SharedState* _shared)
|
||||
: tid(index), rnd(FLAGS_seed + 1 + index), shared(_shared) {}
|
||||
: tid(index), rnd(1000 + index), shared(_shared) {}
|
||||
};
|
||||
|
||||
struct KeyGen {
|
||||
char key_data[27];
|
||||
|
||||
Slice GetRand(Random64& rnd, uint64_t max_key, uint32_t skew) {
|
||||
uint64_t raw = rnd.Next();
|
||||
// Skew according to setting
|
||||
for (uint32_t i = 0; i < skew; ++i) {
|
||||
raw = std::min(raw, rnd.Next());
|
||||
}
|
||||
uint64_t key = FastRange64(raw, max_key);
|
||||
if (FLAGS_degenerate_hash_bits) {
|
||||
uint64_t key_hash =
|
||||
Hash64(reinterpret_cast<const char*>(&key), sizeof(key));
|
||||
// HCC uses the high 64 bits and a lower bit mask for starting probe
|
||||
// location, so we fix hash bits starting at the bottom of that word.
|
||||
auto hi_hash = uint64_t{0x9e3779b97f4a7c13U} ^
|
||||
(key_hash << 1 << (FLAGS_degenerate_hash_bits - 1));
|
||||
uint64_t un_hi, un_lo;
|
||||
BijectiveUnhash2x64(hi_hash, key_hash, &un_hi, &un_lo);
|
||||
un_lo ^= BitwiseAnd(FLAGS_seed, INT32_MAX);
|
||||
EncodeFixed64(key_data, un_lo);
|
||||
EncodeFixed64(key_data + 8, un_hi);
|
||||
return Slice(key_data, kCacheKeySize);
|
||||
Slice GetRand(Random64& rnd, uint64_t max_key, int max_log) {
|
||||
uint64_t key = 0;
|
||||
if (!FLAGS_skewed) {
|
||||
uint64_t raw = rnd.Next();
|
||||
// Skew according to setting
|
||||
for (uint32_t i = 0; i < FLAGS_skew; ++i) {
|
||||
raw = std::min(raw, rnd.Next());
|
||||
}
|
||||
key = FastRange64(raw, max_key);
|
||||
} else {
|
||||
key = rnd.Skewed(max_log);
|
||||
if (key > max_key) {
|
||||
key -= max_key;
|
||||
}
|
||||
}
|
||||
// Variable size and alignment
|
||||
size_t off = key % 8;
|
||||
@@ -285,13 +212,12 @@ struct KeyGen {
|
||||
EncodeFixed64(key_data + 10, key);
|
||||
key_data[18] = char{4};
|
||||
EncodeFixed64(key_data + 19, key);
|
||||
assert(27 >= kCacheKeySize);
|
||||
return Slice(&key_data[off], kCacheKeySize);
|
||||
return Slice(&key_data[off], sizeof(key_data) - off);
|
||||
}
|
||||
};
|
||||
|
||||
Cache::ObjectPtr createValue(Random64& rnd, MemoryAllocator* alloc) {
|
||||
char* rv = AllocateBlock(FLAGS_value_bytes, alloc).release();
|
||||
char* createValue(Random64& rnd) {
|
||||
char* rv = new char[FLAGS_value_bytes];
|
||||
// Fill with some filler data, and take some CPU time
|
||||
for (uint32_t i = 0; i < FLAGS_value_bytes; i += 8) {
|
||||
EncodeFixed64(rv + i, rnd.Next());
|
||||
@@ -300,59 +226,28 @@ Cache::ObjectPtr createValue(Random64& rnd, MemoryAllocator* alloc) {
|
||||
}
|
||||
|
||||
// Callbacks for secondary cache
|
||||
size_t SizeFn(Cache::ObjectPtr /*obj*/) { return FLAGS_value_bytes; }
|
||||
size_t SizeFn(void* /*obj*/) { return FLAGS_value_bytes; }
|
||||
|
||||
Status SaveToFn(Cache::ObjectPtr from_obj, size_t /*from_offset*/,
|
||||
size_t length, char* out) {
|
||||
memcpy(out, from_obj, length);
|
||||
Status SaveToFn(void* obj, size_t /*offset*/, size_t size, void* out) {
|
||||
memcpy(out, obj, size);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CreateFn(const Slice& data, CompressionType /*type*/,
|
||||
CacheTier /*source*/, Cache::CreateContext* /*context*/,
|
||||
MemoryAllocator* /*allocator*/, Cache::ObjectPtr* out_obj,
|
||||
size_t* out_charge) {
|
||||
*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) {
|
||||
CustomDeleter{alloc}(static_cast<char*>(value));
|
||||
// Different deleters to simulate using deleter to gather
|
||||
// stats on the code origin and kind of cache entries.
|
||||
void deleter1(const Slice& /*key*/, void* value) {
|
||||
delete[] static_cast<char*>(value);
|
||||
}
|
||||
void deleter2(const Slice& /*key*/, void* value) {
|
||||
delete[] static_cast<char*>(value);
|
||||
}
|
||||
void deleter3(const Slice& /*key*/, void* value) {
|
||||
delete[] static_cast<char*>(value);
|
||||
}
|
||||
|
||||
Cache::CacheItemHelper helper1_wos(CacheEntryRole::kDataBlock, DeleteFn);
|
||||
Cache::CacheItemHelper helper1(CacheEntryRole::kDataBlock, DeleteFn, SizeFn,
|
||||
SaveToFn, CreateFn, &helper1_wos);
|
||||
Cache::CacheItemHelper helper2_wos(CacheEntryRole::kIndexBlock, DeleteFn);
|
||||
Cache::CacheItemHelper helper2(CacheEntryRole::kIndexBlock, DeleteFn, SizeFn,
|
||||
SaveToFn, CreateFn, &helper2_wos);
|
||||
Cache::CacheItemHelper helper3_wos(CacheEntryRole::kFilterBlock, DeleteFn);
|
||||
Cache::CacheItemHelper helper3(CacheEntryRole::kFilterBlock, DeleteFn, SizeFn,
|
||||
SaveToFn, CreateFn, &helper3_wos);
|
||||
|
||||
void ConfigureSecondaryCache(ShardedCacheOptions& opts) {
|
||||
if (!FLAGS_secondary_cache_uri.empty()) {
|
||||
std::shared_ptr<SecondaryCache> secondary_cache;
|
||||
Status s = SecondaryCache::CreateFromString(
|
||||
ConfigOptions(), FLAGS_secondary_cache_uri, &secondary_cache);
|
||||
if (secondary_cache == nullptr) {
|
||||
fprintf(stderr,
|
||||
"No secondary cache registered matching string: %s status=%s\n",
|
||||
FLAGS_secondary_cache_uri.c_str(), s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
opts.secondary_cache = secondary_cache;
|
||||
}
|
||||
}
|
||||
|
||||
ShardedCacheBase* AsShardedCache(Cache* c) {
|
||||
if (!FLAGS_secondary_cache_uri.empty()) {
|
||||
c = static_cast_with_check<CacheWrapper>(c)->GetTarget().get();
|
||||
}
|
||||
return static_cast_with_check<ShardedCacheBase>(c);
|
||||
}
|
||||
Cache::CacheItemHelper helper1(SizeFn, SaveToFn, deleter1);
|
||||
Cache::CacheItemHelper helper2(SizeFn, SaveToFn, deleter2);
|
||||
Cache::CacheItemHelper helper3(SizeFn, SaveToFn, deleter3);
|
||||
} // namespace
|
||||
|
||||
class CacheBench {
|
||||
@@ -367,112 +262,59 @@ class CacheBench {
|
||||
FLAGS_lookup_insert_percent),
|
||||
insert_threshold_(lookup_insert_threshold_ +
|
||||
kHundredthUint64 * FLAGS_insert_percent),
|
||||
blind_insert_threshold_(insert_threshold_ +
|
||||
kHundredthUint64 * FLAGS_blind_insert_percent),
|
||||
lookup_threshold_(blind_insert_threshold_ +
|
||||
lookup_threshold_(insert_threshold_ +
|
||||
kHundredthUint64 * FLAGS_lookup_percent),
|
||||
erase_threshold_(lookup_threshold_ +
|
||||
kHundredthUint64 * FLAGS_erase_percent) {
|
||||
kHundredthUint64 * FLAGS_erase_percent),
|
||||
skewed_(FLAGS_skewed) {
|
||||
if (erase_threshold_ != 100U * kHundredthUint64) {
|
||||
fprintf(stderr, "Percentages must add to 100.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::shared_ptr<MemoryAllocator> allocator;
|
||||
if (FLAGS_use_jemalloc_no_dump_allocator) {
|
||||
JemallocAllocatorOptions opts;
|
||||
opts.num_arenas = FLAGS_jemalloc_no_dump_allocator_num_arenas;
|
||||
opts.limit_tcache_size =
|
||||
FLAGS_jemalloc_no_dump_allocator_limit_tcache_size;
|
||||
Status s = NewJemallocNodumpAllocator(opts, &allocator);
|
||||
assert(s.ok());
|
||||
max_log_ = 0;
|
||||
if (skewed_) {
|
||||
uint64_t max_key = max_key_;
|
||||
while (max_key >>= 1) max_log_++;
|
||||
if (max_key > (static_cast<uint64_t>(1) << max_log_)) max_log_++;
|
||||
}
|
||||
if (FLAGS_cache_type == "clock_cache") {
|
||||
fprintf(stderr, "Old clock cache implementation has been removed.\n");
|
||||
exit(1);
|
||||
} else if (EndsWith(FLAGS_cache_type, "hyper_clock_cache")) {
|
||||
HyperClockCacheOptions opts(
|
||||
FLAGS_cache_size, /*estimated_entry_charge=*/0, FLAGS_num_shard_bits);
|
||||
opts.hash_seed = BitwiseAnd(FLAGS_seed, INT32_MAX);
|
||||
opts.memory_allocator = allocator;
|
||||
opts.eviction_effort_cap = FLAGS_eviction_effort_cap;
|
||||
if (FLAGS_cache_type == "fixed_hyper_clock_cache" ||
|
||||
FLAGS_cache_type == "hyper_clock_cache") {
|
||||
opts.estimated_entry_charge = FLAGS_value_bytes_estimate > 0
|
||||
? FLAGS_value_bytes_estimate
|
||||
: FLAGS_value_bytes;
|
||||
} else if (FLAGS_cache_type == "auto_hyper_clock_cache") {
|
||||
if (FLAGS_value_bytes_estimate > 0) {
|
||||
opts.min_avg_entry_charge = FLAGS_value_bytes_estimate;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Cache type not supported.\n");
|
||||
|
||||
if (FLAGS_use_clock_cache) {
|
||||
cache_ = NewClockCache(FLAGS_cache_size, FLAGS_num_shard_bits);
|
||||
if (!cache_) {
|
||||
fprintf(stderr, "Clock cache not supported.\n");
|
||||
exit(1);
|
||||
}
|
||||
ConfigureSecondaryCache(opts);
|
||||
cache_ = opts.MakeSharedCache();
|
||||
} else if (FLAGS_cache_type == "lru_cache") {
|
||||
LRUCacheOptions opts(FLAGS_cache_size, FLAGS_num_shard_bits,
|
||||
false /* strict_capacity_limit */,
|
||||
0.5 /* high_pri_pool_ratio */);
|
||||
opts.hash_seed = BitwiseAnd(FLAGS_seed, INT32_MAX);
|
||||
opts.memory_allocator = allocator;
|
||||
ConfigureSecondaryCache(opts);
|
||||
cache_ = NewLRUCache(opts);
|
||||
} else {
|
||||
fprintf(stderr, "Cache type not supported.\n");
|
||||
exit(1);
|
||||
LRUCacheOptions opts(FLAGS_cache_size, FLAGS_num_shard_bits, false, 0.5);
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (!FLAGS_secondary_cache_uri.empty()) {
|
||||
Status s = SecondaryCache::CreateFromString(
|
||||
ConfigOptions(), FLAGS_secondary_cache_uri, &secondary_cache);
|
||||
if (secondary_cache == nullptr) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"No secondary cache registered matching string: %s status=%s\n",
|
||||
FLAGS_secondary_cache_uri.c_str(), s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
opts.secondary_cache = secondary_cache;
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
cache_ = NewLRUCache(opts);
|
||||
}
|
||||
}
|
||||
|
||||
~CacheBench() {}
|
||||
|
||||
void PopulateCache() {
|
||||
Random64 rnd(FLAGS_seed);
|
||||
Random64 rnd(1);
|
||||
KeyGen keygen;
|
||||
size_t max_occ = 0;
|
||||
size_t inserts_since_max_occ_increase = 0;
|
||||
size_t keys_since_last_not_found = 0;
|
||||
|
||||
// Avoid redundant insertions by checking Lookup before Insert.
|
||||
// Loop until insertions consistently fail to increase max occupancy or
|
||||
// it becomes difficult to find keys not already inserted.
|
||||
while (inserts_since_max_occ_increase < 100 &&
|
||||
keys_since_last_not_found < 100) {
|
||||
Slice key = keygen.GetRand(rnd, max_key_, FLAGS_skew);
|
||||
|
||||
Cache::Handle* handle = cache_->Lookup(key);
|
||||
if (handle != nullptr) {
|
||||
cache_->Release(handle);
|
||||
++keys_since_last_not_found;
|
||||
continue;
|
||||
}
|
||||
keys_since_last_not_found = 0;
|
||||
|
||||
Status s =
|
||||
cache_->Insert(key, createValue(rnd, cache_->memory_allocator()),
|
||||
&helper1, FLAGS_value_bytes);
|
||||
assert(s.ok());
|
||||
|
||||
handle = cache_->Lookup(key);
|
||||
if (!handle) {
|
||||
fprintf(stderr, "Failed to lookup key just inserted.\n");
|
||||
assert(false);
|
||||
exit(42);
|
||||
} else {
|
||||
cache_->Release(handle);
|
||||
}
|
||||
|
||||
size_t occ = cache_->GetOccupancyCount();
|
||||
if (occ > max_occ) {
|
||||
max_occ = occ;
|
||||
inserts_since_max_occ_increase = 0;
|
||||
} else {
|
||||
++inserts_since_max_occ_increase;
|
||||
}
|
||||
for (uint64_t i = 0; i < 2 * FLAGS_cache_size; i += FLAGS_value_bytes) {
|
||||
cache_->Insert(keygen.GetRand(rnd, max_key_, max_log_), createValue(rnd),
|
||||
&helper1, FLAGS_value_bytes);
|
||||
}
|
||||
printf("Population complete (%zu entries, %g average charge)\n", max_occ,
|
||||
1.0 * FLAGS_cache_size / max_occ);
|
||||
}
|
||||
|
||||
bool Run() {
|
||||
@@ -531,35 +373,19 @@ class CacheBench {
|
||||
FLAGS_ops_per_thread / elapsed_secs);
|
||||
printf("Thread ops/sec = %u\n", ops_per_sec);
|
||||
|
||||
printf("Lookup hit ratio: %g\n", shared.GetLookupHitRatio());
|
||||
printf("\nOperation latency (ns):\n");
|
||||
HistogramImpl combined;
|
||||
for (uint32_t i = 0; i < FLAGS_threads; i++) {
|
||||
combined.Merge(threads[i]->latency_ns_hist);
|
||||
}
|
||||
printf("%s", combined.ToString().c_str());
|
||||
|
||||
size_t occ = cache_->GetOccupancyCount();
|
||||
size_t slot = cache_->GetTableAddressCount();
|
||||
printf("Final load factor: %g (%zu / %zu)\n", 1.0 * occ / slot, occ, slot);
|
||||
|
||||
printf("Final pinned count: %zu\n", shared.GetPinnedCount());
|
||||
|
||||
if (FLAGS_histograms) {
|
||||
printf("\nOperation latency (ns):\n");
|
||||
HistogramImpl combined;
|
||||
for (uint32_t i = 0; i < FLAGS_threads; i++) {
|
||||
combined.Merge(threads[i]->latency_ns_hist);
|
||||
}
|
||||
printf("%s", combined.ToString().c_str());
|
||||
|
||||
if (FLAGS_gather_stats) {
|
||||
printf("\nGather stats latency (us):\n");
|
||||
printf("%s", stats_hist.ToString().c_str());
|
||||
}
|
||||
if (FLAGS_gather_stats) {
|
||||
printf("\nGather stats latency (us):\n");
|
||||
printf("%s", stats_hist.ToString().c_str());
|
||||
}
|
||||
|
||||
if (FLAGS_report_problems) {
|
||||
printf("\n");
|
||||
std::shared_ptr<Logger> logger =
|
||||
std::make_shared<StderrLogger>(InfoLogLevel::DEBUG_LEVEL);
|
||||
cache_->ReportProblems(logger);
|
||||
}
|
||||
printf("%s", stats_report.c_str());
|
||||
printf("\n%s", stats_report.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -570,9 +396,10 @@ class CacheBench {
|
||||
// Cumulative thresholds in the space of a random uint64_t
|
||||
const uint64_t lookup_insert_threshold_;
|
||||
const uint64_t insert_threshold_;
|
||||
const uint64_t blind_insert_threshold_;
|
||||
const uint64_t lookup_threshold_;
|
||||
const uint64_t erase_threshold_;
|
||||
const bool skewed_;
|
||||
int max_log_;
|
||||
|
||||
// A benchmark version of gathering stats on an active block cache by
|
||||
// iterating over it. The primary purpose is to measure the impact of
|
||||
@@ -590,9 +417,7 @@ class CacheBench {
|
||||
uint64_t total_key_size = 0;
|
||||
uint64_t total_charge = 0;
|
||||
uint64_t total_entry_count = 0;
|
||||
uint64_t table_occupancy = 0;
|
||||
uint64_t table_size = 0;
|
||||
std::set<const Cache::CacheItemHelper*> helpers;
|
||||
std::set<Cache::DeleterFn> deleters;
|
||||
StopWatchNano timer(clock);
|
||||
|
||||
for (;;) {
|
||||
@@ -605,11 +430,8 @@ class CacheBench {
|
||||
for (;;) {
|
||||
if (shared->AllDone()) {
|
||||
std::ostringstream ostr;
|
||||
ostr << "\nMost recent cache entry stats:\n"
|
||||
ostr << "Most recent cache entry stats:\n"
|
||||
<< "Number of entries: " << total_entry_count << "\n"
|
||||
<< "Table occupancy: " << table_occupancy << " / "
|
||||
<< table_size << " = "
|
||||
<< (100.0 * table_occupancy / table_size) << "%\n"
|
||||
<< "Total charge: " << BytesToHumanString(total_charge) << "\n"
|
||||
<< "Average key size: "
|
||||
<< (1.0 * total_key_size / total_entry_count) << "\n"
|
||||
@@ -617,7 +439,7 @@ class CacheBench {
|
||||
<< BytesToHumanString(static_cast<uint64_t>(
|
||||
1.0 * total_charge / total_entry_count))
|
||||
<< "\n"
|
||||
<< "Unique helpers: " << helpers.size() << "\n";
|
||||
<< "Unique deleters: " << deleters.size() << "\n";
|
||||
*stats_report = ostr.str();
|
||||
return;
|
||||
}
|
||||
@@ -633,26 +455,20 @@ class CacheBench {
|
||||
total_key_size = 0;
|
||||
total_charge = 0;
|
||||
total_entry_count = 0;
|
||||
helpers.clear();
|
||||
auto fn = [&](const Slice& key, Cache::ObjectPtr /*value*/, size_t charge,
|
||||
const Cache::CacheItemHelper* helper) {
|
||||
deleters.clear();
|
||||
auto fn = [&](const Slice& key, void* /*value*/, size_t charge,
|
||||
Cache::DeleterFn deleter) {
|
||||
total_key_size += key.size();
|
||||
total_charge += charge;
|
||||
++total_entry_count;
|
||||
// Something slightly more expensive as in stats by category
|
||||
helpers.insert(helper);
|
||||
// Something slightly more expensive as in (future) stats by category
|
||||
deleters.insert(deleter);
|
||||
};
|
||||
if (FLAGS_histograms) {
|
||||
timer.Start();
|
||||
}
|
||||
timer.Start();
|
||||
Cache::ApplyToAllEntriesOptions opts;
|
||||
opts.average_entries_per_lock = FLAGS_gather_stats_entries_per_lock;
|
||||
shared->GetCacheBench()->cache_->ApplyToAllEntries(fn, opts);
|
||||
table_occupancy = shared->GetCacheBench()->cache_->GetOccupancyCount();
|
||||
table_size = shared->GetCacheBench()->cache_->GetTableAddressCount();
|
||||
if (FLAGS_histograms) {
|
||||
stats_hist->Add(timer.ElapsedNanos() / 1000);
|
||||
}
|
||||
stats_hist->Add(timer.ElapsedNanos() / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,89 +499,63 @@ class CacheBench {
|
||||
void OperateCache(ThreadState* thread) {
|
||||
// To use looked-up values
|
||||
uint64_t result = 0;
|
||||
uint64_t lookup_misses = 0;
|
||||
uint64_t lookup_hits = 0;
|
||||
// To hold handles for a non-trivial amount of time
|
||||
std::deque<Cache::Handle*> pinned;
|
||||
size_t total_pin_count = static_cast<size_t>(
|
||||
(FLAGS_cache_size * FLAGS_pinned_ratio) / FLAGS_value_bytes + 0.999999);
|
||||
// For this thread. Some round up, some round down, as appropriate
|
||||
size_t pin_count = (total_pin_count + thread->tid) / FLAGS_threads;
|
||||
|
||||
Cache::Handle* handle = nullptr;
|
||||
KeyGen gen;
|
||||
const auto clock = SystemClock::Default().get();
|
||||
uint64_t start_time = clock->NowMicros();
|
||||
StopWatchNano timer(clock);
|
||||
auto system_clock = SystemClock::Default();
|
||||
size_t steps_to_next_capacity_change = 0;
|
||||
|
||||
for (uint64_t i = 0; i < FLAGS_ops_per_thread; i++) {
|
||||
Slice key = gen.GetRand(thread->rnd, max_key_, FLAGS_skew);
|
||||
timer.Start();
|
||||
Slice key = gen.GetRand(thread->rnd, max_key_, max_log_);
|
||||
uint64_t random_op = thread->rnd.Next();
|
||||
|
||||
if (FLAGS_vary_capacity_ratio > 0.0 && thread->tid == 0) {
|
||||
if (steps_to_next_capacity_change == 0) {
|
||||
double cut_ratio = static_cast<double>(thread->rnd.Next()) /
|
||||
static_cast<double>(UINT64_MAX) *
|
||||
FLAGS_vary_capacity_ratio;
|
||||
cache_->SetCapacity(FLAGS_cache_size * (1.0 - cut_ratio));
|
||||
steps_to_next_capacity_change =
|
||||
static_cast<size_t>(FLAGS_ops_per_thread / 100);
|
||||
} else {
|
||||
--steps_to_next_capacity_change;
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAGS_histograms) {
|
||||
timer.Start();
|
||||
}
|
||||
Cache::CreateCallback create_cb = [](const void* buf, size_t size,
|
||||
void** out_obj,
|
||||
size_t* charge) -> Status {
|
||||
*out_obj = reinterpret_cast<void*>(new char[size]);
|
||||
memcpy(*out_obj, buf, size);
|
||||
*charge = size;
|
||||
return Status::OK();
|
||||
};
|
||||
|
||||
if (random_op < lookup_insert_threshold_) {
|
||||
// do lookup
|
||||
auto handle = cache_->Lookup(key, &helper2, /*context*/ nullptr,
|
||||
Cache::Priority::LOW);
|
||||
if (handle) {
|
||||
++lookup_hits;
|
||||
if (!FLAGS_lean) {
|
||||
// do something with the data
|
||||
result += NPHash64(static_cast<char*>(cache_->Value(handle)),
|
||||
FLAGS_value_bytes);
|
||||
}
|
||||
pinned.push_back(handle);
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
// do lookup
|
||||
handle = cache_->Lookup(key, &helper2, create_cb, Cache::Priority::LOW,
|
||||
true);
|
||||
if (handle) {
|
||||
// do something with the data
|
||||
result += NPHash64(static_cast<char*>(cache_->Value(handle)),
|
||||
FLAGS_value_bytes);
|
||||
} else {
|
||||
++lookup_misses;
|
||||
// do insert
|
||||
Status s = cache_->Insert(
|
||||
key, createValue(thread->rnd, cache_->memory_allocator()),
|
||||
&helper2, FLAGS_value_bytes, &pinned.emplace_back());
|
||||
assert(s.ok());
|
||||
cache_->Insert(key, createValue(thread->rnd), &helper2,
|
||||
FLAGS_value_bytes, &handle);
|
||||
}
|
||||
} else if (random_op < insert_threshold_) {
|
||||
// do insert
|
||||
Status s = cache_->Insert(
|
||||
key, createValue(thread->rnd, cache_->memory_allocator()), &helper3,
|
||||
FLAGS_value_bytes, &pinned.emplace_back());
|
||||
assert(s.ok());
|
||||
} else if (random_op < blind_insert_threshold_) {
|
||||
// insert without keeping a handle
|
||||
Status s = cache_->Insert(
|
||||
key, createValue(thread->rnd, cache_->memory_allocator()), &helper3,
|
||||
FLAGS_value_bytes);
|
||||
assert(s.ok());
|
||||
} else if (random_op < lookup_threshold_) {
|
||||
// do lookup
|
||||
auto handle = cache_->Lookup(key, &helper2, /*context*/ nullptr,
|
||||
Cache::Priority::LOW);
|
||||
if (handle) {
|
||||
++lookup_hits;
|
||||
if (!FLAGS_lean) {
|
||||
// do something with the data
|
||||
result += NPHash64(static_cast<char*>(cache_->Value(handle)),
|
||||
FLAGS_value_bytes);
|
||||
}
|
||||
pinned.push_back(handle);
|
||||
} else {
|
||||
++lookup_misses;
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
// do insert
|
||||
cache_->Insert(key, createValue(thread->rnd), &helper3,
|
||||
FLAGS_value_bytes, &handle);
|
||||
} else if (random_op < lookup_threshold_) {
|
||||
if (handle) {
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
// do lookup
|
||||
handle = cache_->Lookup(key, &helper2, create_cb, Cache::Priority::LOW,
|
||||
true);
|
||||
if (handle) {
|
||||
// do something with the data
|
||||
result += NPHash64(static_cast<char*>(cache_->Value(handle)),
|
||||
FLAGS_value_bytes);
|
||||
}
|
||||
} else if (random_op < erase_threshold_) {
|
||||
// do erase
|
||||
@@ -774,27 +564,9 @@ class CacheBench {
|
||||
// Should be extremely unlikely (noop)
|
||||
assert(random_op >= kHundredthUint64 * 100U);
|
||||
}
|
||||
if (FLAGS_histograms) {
|
||||
thread->latency_ns_hist.Add(timer.ElapsedNanos());
|
||||
}
|
||||
if (FLAGS_usleep > 0) {
|
||||
unsigned us =
|
||||
static_cast<unsigned>(thread->rnd.Uniform(FLAGS_usleep + 1));
|
||||
if (us > 0) {
|
||||
system_clock->SleepForMicroseconds(us);
|
||||
}
|
||||
}
|
||||
while (pinned.size() > pin_count) {
|
||||
cache_->Release(pinned.front());
|
||||
pinned.pop_front();
|
||||
}
|
||||
thread->latency_ns_hist.Add(timer.ElapsedNanos());
|
||||
}
|
||||
if (FLAGS_early_exit) {
|
||||
MutexLock l(thread->shared->GetMutex());
|
||||
exit(0);
|
||||
}
|
||||
thread->shared->AddLookupStats(lookup_hits, lookup_misses, pinned.size());
|
||||
for (auto handle : pinned) {
|
||||
if (handle) {
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
@@ -807,23 +579,12 @@ class CacheBench {
|
||||
}
|
||||
|
||||
void PrintEnv() const {
|
||||
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
|
||||
printf(
|
||||
"WARNING: Optimization is disabled: benchmarks unnecessarily slow\n");
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
printf("WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
|
||||
#endif
|
||||
printf("----------------------------\n");
|
||||
printf("RocksDB version : %d.%d\n", kMajorVersion, kMinorVersion);
|
||||
printf("Cache impl name : %s\n", cache_->Name());
|
||||
printf("DMutex impl name : %s\n", DMutex::kName());
|
||||
printf("Number of threads : %u\n", FLAGS_threads);
|
||||
printf("Ops per thread : %" PRIu64 "\n", FLAGS_ops_per_thread);
|
||||
printf("Cache size : %s\n",
|
||||
BytesToHumanString(FLAGS_cache_size).c_str());
|
||||
printf("Num shard bits : %d\n",
|
||||
AsShardedCache(cache_.get())->GetNumShardBits());
|
||||
printf("Num shard bits : %u\n", FLAGS_num_shard_bits);
|
||||
printf("Max key : %" PRIu64 "\n", max_key_);
|
||||
printf("Resident ratio : %g\n", FLAGS_resident_ratio);
|
||||
printf("Skew degree : %u\n", FLAGS_skew);
|
||||
@@ -984,7 +745,7 @@ class StressCacheKey {
|
||||
|
||||
void RunOnce() {
|
||||
// Re-initialized simulated state
|
||||
const size_t db_count = std::max(size_t{FLAGS_sck_db_count}, size_t{1});
|
||||
const size_t db_count = FLAGS_sck_db_count;
|
||||
dbs_.reset(new TableProperties[db_count]{});
|
||||
const size_t table_mask = (size_t{1} << FLAGS_sck_table_bits) - 1;
|
||||
table_.reset(new uint64_t[table_mask + 1]{});
|
||||
@@ -1001,13 +762,13 @@ class StressCacheKey {
|
||||
|
||||
process_count_ = 0;
|
||||
session_count_ = 0;
|
||||
newdb_count_ = 0;
|
||||
ResetProcess(/*newdbs*/ true);
|
||||
ResetProcess();
|
||||
|
||||
Random64 r{std::random_device{}()};
|
||||
|
||||
uint64_t max_file_count =
|
||||
uint64_t{FLAGS_sck_files_per_day} * FLAGS_sck_days_per_run;
|
||||
uint64_t file_size = FLAGS_sck_file_size_mb * uint64_t{1024} * 1024U;
|
||||
uint32_t report_count = 0;
|
||||
uint32_t collisions_this_run = 0;
|
||||
size_t db_i = 0;
|
||||
@@ -1020,9 +781,9 @@ class StressCacheKey {
|
||||
}
|
||||
// Any other periodic actions before simulating next file
|
||||
if (!FLAGS_sck_footer_unique_id && r.OneIn(FLAGS_sck_reopen_nfiles)) {
|
||||
ResetSession(db_i, /*newdb*/ r.OneIn(FLAGS_sck_newdb_nreopen));
|
||||
ResetSession(db_i);
|
||||
} else if (r.OneIn(restart_nfiles_)) {
|
||||
ResetProcess(/*newdbs*/ false);
|
||||
ResetProcess();
|
||||
}
|
||||
// Simulate next file
|
||||
OffsetableCacheKey ock;
|
||||
@@ -1035,7 +796,8 @@ class StressCacheKey {
|
||||
}
|
||||
bool is_stable;
|
||||
BlockBasedTable::SetupBaseCacheKey(&dbs_[db_i], /* ignored */ "",
|
||||
/* ignored */ 42, &ock, &is_stable);
|
||||
/* ignored */ 42, file_size, &ock,
|
||||
&is_stable);
|
||||
assert(is_stable);
|
||||
// Get a representative cache key, which later we analytically generalize
|
||||
// to a range.
|
||||
@@ -1045,11 +807,13 @@ class StressCacheKey {
|
||||
reduced_key = GetSliceHash64(ck.AsSlice()) >> shift_away;
|
||||
} else if (FLAGS_sck_footer_unique_id) {
|
||||
// Special case: keep only file number, not session counter
|
||||
reduced_key = DecodeFixed64(ck.AsSlice().data()) >> shift_away;
|
||||
uint32_t a = DecodeFixed32(ck.AsSlice().data() + 4) >> shift_away_a;
|
||||
uint32_t b = DecodeFixed32(ck.AsSlice().data() + 12) >> shift_away_b;
|
||||
reduced_key = (uint64_t{a} << 32) + b;
|
||||
} else {
|
||||
// Try to keep file number and session counter (shift away other bits)
|
||||
uint32_t a = DecodeFixed32(ck.AsSlice().data()) << shift_away_a;
|
||||
uint32_t b = DecodeFixed32(ck.AsSlice().data() + 4) >> shift_away_b;
|
||||
uint32_t b = DecodeFixed32(ck.AsSlice().data() + 12) >> shift_away_b;
|
||||
reduced_key = (uint64_t{a} << 32) + b;
|
||||
}
|
||||
if (reduced_key == 0) {
|
||||
@@ -1071,7 +835,7 @@ class StressCacheKey {
|
||||
// Our goal is to predict probability of no collisions, not expected
|
||||
// number of collisions. To make the distinction, we have to get rid
|
||||
// of observing correlated collisions, which this takes care of:
|
||||
ResetProcess(/*newdbs*/ false);
|
||||
ResetProcess();
|
||||
} else {
|
||||
// Replace (end of lifetime for file that was in this slot)
|
||||
table_[pos] = reduced_key;
|
||||
@@ -1089,11 +853,10 @@ class StressCacheKey {
|
||||
}
|
||||
// Report
|
||||
printf(
|
||||
"%" PRIu64 " days, %" PRIu64 " proc, %" PRIu64 " sess, %" PRIu64
|
||||
" newdb, %u coll, occ %g%%, ejected %g%% \r",
|
||||
"%" PRIu64 " days, %" PRIu64 " proc, %" PRIu64
|
||||
" sess, %u coll, occ %g%%, ejected %g%% \r",
|
||||
file_count / FLAGS_sck_files_per_day, process_count_,
|
||||
session_count_, newdb_count_ - FLAGS_sck_db_count,
|
||||
collisions_this_run, 100.0 * sampled_count / 1000.0,
|
||||
session_count_, collisions_this_run, 100.0 * sampled_count / 1000.0,
|
||||
100.0 * (1.0 - sampled_count / 1000.0 * table_mask / file_count));
|
||||
fflush(stdout);
|
||||
}
|
||||
@@ -1101,27 +864,16 @@ class StressCacheKey {
|
||||
collisions_ += collisions_this_run;
|
||||
}
|
||||
|
||||
void ResetSession(size_t i, bool newdb) {
|
||||
void ResetSession(size_t i) {
|
||||
dbs_[i].db_session_id = DBImpl::GenerateDbSessionId(nullptr);
|
||||
if (newdb) {
|
||||
++newdb_count_;
|
||||
if (FLAGS_sck_footer_unique_id) {
|
||||
// Simulate how footer id would behave
|
||||
dbs_[i].db_id = "none";
|
||||
} else {
|
||||
// db_id might be ignored, depending on the implementation details
|
||||
dbs_[i].db_id = std::to_string(newdb_count_);
|
||||
dbs_[i].orig_file_number = 0;
|
||||
}
|
||||
}
|
||||
session_count_++;
|
||||
}
|
||||
|
||||
void ResetProcess(bool newdbs) {
|
||||
void ResetProcess() {
|
||||
process_count_++;
|
||||
DBImpl::TEST_ResetDbSessionIdGen();
|
||||
for (size_t i = 0; i < FLAGS_sck_db_count; ++i) {
|
||||
ResetSession(i, newdbs);
|
||||
ResetSession(i);
|
||||
}
|
||||
if (FLAGS_sck_footer_unique_id) {
|
||||
// For footer unique ID, this tracks process-wide generated SST file
|
||||
@@ -1136,14 +888,12 @@ class StressCacheKey {
|
||||
std::unique_ptr<uint64_t[]> table_;
|
||||
uint64_t process_count_ = 0;
|
||||
uint64_t session_count_ = 0;
|
||||
uint64_t newdb_count_ = 0;
|
||||
uint64_t collisions_ = 0;
|
||||
uint32_t restart_nfiles_ = 0;
|
||||
double multiplier_ = 0.0;
|
||||
};
|
||||
|
||||
int cache_bench_tool(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
if (FLAGS_stress_cache_key) {
|
||||
@@ -1157,14 +907,11 @@ int cache_bench_tool(int argc, char** argv) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (FLAGS_seed == 0) {
|
||||
FLAGS_seed = static_cast<uint32_t>(port::GetProcessID());
|
||||
printf("Using seed = %" PRIu32 "\n", FLAGS_seed);
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::CacheBench bench;
|
||||
if (FLAGS_populate_cache) {
|
||||
bench.PopulateCache();
|
||||
printf("Population complete\n");
|
||||
printf("----------------------------\n");
|
||||
}
|
||||
if (bench.Run()) {
|
||||
return 0;
|
||||
|
||||
Vendored
+22
-56
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
std::array<std::string, kNumCacheEntryRoles> kCacheEntryRoleToCamelString{{
|
||||
std::array<const char*, kNumCacheEntryRoles> kCacheEntryRoleToCamelString{{
|
||||
"DataBlock",
|
||||
"FilterBlock",
|
||||
"FilterMetaBlock",
|
||||
@@ -21,14 +21,10 @@ std::array<std::string, kNumCacheEntryRoles> kCacheEntryRoleToCamelString{{
|
||||
"WriteBuffer",
|
||||
"CompressionDictionaryBuildingBuffer",
|
||||
"FilterConstruction",
|
||||
"BlockBasedTableReader",
|
||||
"FileMetadata",
|
||||
"BlobValue",
|
||||
"BlobCache",
|
||||
"Misc",
|
||||
}};
|
||||
|
||||
std::array<std::string, kNumCacheEntryRoles> kCacheEntryRoleToHyphenString{{
|
||||
std::array<const char*, kNumCacheEntryRoles> kCacheEntryRoleToHyphenString{{
|
||||
"data-block",
|
||||
"filter-block",
|
||||
"filter-meta-block",
|
||||
@@ -38,67 +34,37 @@ std::array<std::string, kNumCacheEntryRoles> kCacheEntryRoleToHyphenString{{
|
||||
"write-buffer",
|
||||
"compression-dictionary-building-buffer",
|
||||
"filter-construction",
|
||||
"block-based-table-reader",
|
||||
"file-metadata",
|
||||
"blob-value",
|
||||
"blob-cache",
|
||||
"misc",
|
||||
}};
|
||||
|
||||
const std::string& GetCacheEntryRoleName(CacheEntryRole role) {
|
||||
return kCacheEntryRoleToHyphenString[static_cast<size_t>(role)];
|
||||
}
|
||||
|
||||
const std::string& BlockCacheEntryStatsMapKeys::CacheId() {
|
||||
static const std::string kCacheId = "id";
|
||||
return kCacheId;
|
||||
}
|
||||
|
||||
const std::string& BlockCacheEntryStatsMapKeys::CacheCapacityBytes() {
|
||||
static const std::string kCacheCapacityBytes = "capacity";
|
||||
return kCacheCapacityBytes;
|
||||
}
|
||||
|
||||
const std::string&
|
||||
BlockCacheEntryStatsMapKeys::LastCollectionDurationSeconds() {
|
||||
static const std::string kLastCollectionDurationSeconds =
|
||||
"secs_for_last_collection";
|
||||
return kLastCollectionDurationSeconds;
|
||||
}
|
||||
|
||||
const std::string& BlockCacheEntryStatsMapKeys::LastCollectionAgeSeconds() {
|
||||
static const std::string kLastCollectionAgeSeconds =
|
||||
"secs_since_last_collection";
|
||||
return kLastCollectionAgeSeconds;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::string GetPrefixedCacheEntryRoleName(const std::string& prefix,
|
||||
CacheEntryRole role) {
|
||||
const std::string& role_name = GetCacheEntryRoleName(role);
|
||||
std::string prefixed_role_name;
|
||||
prefixed_role_name.reserve(prefix.size() + role_name.size());
|
||||
prefixed_role_name.append(prefix);
|
||||
prefixed_role_name.append(role_name);
|
||||
return prefixed_role_name;
|
||||
struct Registry {
|
||||
std::mutex mutex;
|
||||
std::unordered_map<Cache::DeleterFn, CacheEntryRole> role_map;
|
||||
void Register(Cache::DeleterFn fn, CacheEntryRole role) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
role_map[fn] = role;
|
||||
}
|
||||
std::unordered_map<Cache::DeleterFn, CacheEntryRole> Copy() {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
return role_map;
|
||||
}
|
||||
};
|
||||
|
||||
Registry& GetRegistry() {
|
||||
STATIC_AVOID_DESTRUCTION(Registry, registry);
|
||||
return registry;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string BlockCacheEntryStatsMapKeys::EntryCount(CacheEntryRole role) {
|
||||
const static std::string kPrefix = "count.";
|
||||
return GetPrefixedCacheEntryRoleName(kPrefix, role);
|
||||
void RegisterCacheDeleterRole(Cache::DeleterFn fn, CacheEntryRole role) {
|
||||
GetRegistry().Register(fn, role);
|
||||
}
|
||||
|
||||
std::string BlockCacheEntryStatsMapKeys::UsedBytes(CacheEntryRole role) {
|
||||
const static std::string kPrefix = "bytes.";
|
||||
return GetPrefixedCacheEntryRoleName(kPrefix, role);
|
||||
}
|
||||
|
||||
std::string BlockCacheEntryStatsMapKeys::UsedPercent(CacheEntryRole role) {
|
||||
const static std::string kPrefix = "percent.";
|
||||
return GetPrefixedCacheEntryRoleName(kPrefix, role);
|
||||
std::unordered_map<Cache::DeleterFn, CacheEntryRole> CopyCacheDeleterRoleMap() {
|
||||
return GetRegistry().Copy();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
Vendored
+116
-2
@@ -7,14 +7,128 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
extern std::array<std::string, kNumCacheEntryRoles>
|
||||
// Classifications of block cache entries, for reporting statistics
|
||||
// Adding new enum to this class requires corresponding updates to
|
||||
// kCacheEntryRoleToCamelString and kCacheEntryRoleToHyphenString
|
||||
enum class CacheEntryRole {
|
||||
// Block-based table data block
|
||||
kDataBlock,
|
||||
// Block-based table filter block (full or partitioned)
|
||||
kFilterBlock,
|
||||
// Block-based table metadata block for partitioned filter
|
||||
kFilterMetaBlock,
|
||||
// Block-based table deprecated filter block (old "block-based" filter)
|
||||
kDeprecatedFilterBlock,
|
||||
// Block-based table index block
|
||||
kIndexBlock,
|
||||
// Other kinds of block-based table block
|
||||
kOtherBlock,
|
||||
// WriteBufferManager reservations to account for memtable usage
|
||||
kWriteBuffer,
|
||||
// BlockBasedTableBuilder reservations to account for
|
||||
// compression dictionary building buffer's memory usage
|
||||
kCompressionDictionaryBuildingBuffer,
|
||||
// Filter reservations to account for
|
||||
// (new) bloom and ribbon filter construction's memory usage
|
||||
kFilterConstruction,
|
||||
// Default bucket, for miscellaneous cache entries. Do not use for
|
||||
// entries that could potentially add up to large usage.
|
||||
kMisc,
|
||||
};
|
||||
constexpr uint32_t kNumCacheEntryRoles =
|
||||
static_cast<uint32_t>(CacheEntryRole::kMisc) + 1;
|
||||
|
||||
extern std::array<const char*, kNumCacheEntryRoles>
|
||||
kCacheEntryRoleToCamelString;
|
||||
extern std::array<std::string, kNumCacheEntryRoles>
|
||||
extern std::array<const char*, kNumCacheEntryRoles>
|
||||
kCacheEntryRoleToHyphenString;
|
||||
|
||||
// To associate cache entries with their role, we use a hack on the
|
||||
// existing Cache interface. Because the deleter of an entry can authenticate
|
||||
// the code origin of an entry, we can elaborate the choice of deleter to
|
||||
// also encode role information, without inferring false role information
|
||||
// from entries not choosing to encode a role.
|
||||
//
|
||||
// The rest of this file is for handling mappings between deleters and
|
||||
// roles.
|
||||
|
||||
// To infer a role from a deleter, the deleter must be registered. This
|
||||
// can be done "manually" with this function. This function is thread-safe,
|
||||
// and the registration mappings go into private but static storage. (Note
|
||||
// that DeleterFn is a function pointer, not std::function. Registrations
|
||||
// should not be too many.)
|
||||
void RegisterCacheDeleterRole(Cache::DeleterFn fn, CacheEntryRole role);
|
||||
|
||||
// Gets a copy of the registered deleter -> role mappings. This is the only
|
||||
// function for reading the mappings made with RegisterCacheDeleterRole.
|
||||
// Why only this interface for reading?
|
||||
// * This function has to be thread safe, which could incur substantial
|
||||
// overhead. We should not pay this overhead for every deleter look-up.
|
||||
// * This is suitable for preparing for batch operations, like with
|
||||
// CacheEntryStatsCollector.
|
||||
// * The number of mappings should be sufficiently small (dozens).
|
||||
std::unordered_map<Cache::DeleterFn, CacheEntryRole> CopyCacheDeleterRoleMap();
|
||||
|
||||
// ************************************************************** //
|
||||
// An automatic registration infrastructure. This enables code
|
||||
// to simply ask for a deleter associated with a particular type
|
||||
// and role, and registration is automatic. In a sense, this is
|
||||
// a small dependency injection infrastructure, because linking
|
||||
// in new deleter instantiations is essentially sufficient for
|
||||
// making stats collection (using CopyCacheDeleterRoleMap) aware
|
||||
// of them.
|
||||
|
||||
namespace cache_entry_roles_detail {
|
||||
|
||||
template <typename T, CacheEntryRole R>
|
||||
struct RegisteredDeleter {
|
||||
RegisteredDeleter() { RegisterCacheDeleterRole(Delete, R); }
|
||||
|
||||
// These have global linkage to help ensure compiler optimizations do not
|
||||
// break uniqueness for each <T,R>
|
||||
static void Delete(const Slice& /* key */, void* value) {
|
||||
// Supports T == Something[], unlike delete operator
|
||||
std::default_delete<T>()(
|
||||
static_cast<typename std::remove_extent<T>::type*>(value));
|
||||
}
|
||||
};
|
||||
|
||||
template <CacheEntryRole R>
|
||||
struct RegisteredNoopDeleter {
|
||||
RegisteredNoopDeleter() { RegisterCacheDeleterRole(Delete, R); }
|
||||
|
||||
static void Delete(const Slice& /* key */, void* /* value */) {
|
||||
// Here was `assert(value == nullptr);` but we can also put pointers
|
||||
// to static data in Cache, for testing at least.
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cache_entry_roles_detail
|
||||
|
||||
// Get an automatically registered deleter for value type T and role R.
|
||||
// Based on C++ semantics, registration is invoked exactly once in a
|
||||
// thread-safe way on first call to this function, for each <T, R>.
|
||||
template <typename T, CacheEntryRole R>
|
||||
Cache::DeleterFn GetCacheEntryDeleterForRole() {
|
||||
static cache_entry_roles_detail::RegisteredDeleter<T, R> reg;
|
||||
return reg.Delete;
|
||||
}
|
||||
|
||||
// Get an automatically registered no-op deleter (value should be nullptr)
|
||||
// and associated with role R. This is used for Cache "reservation" entries
|
||||
// such as for WriteBufferManager.
|
||||
template <CacheEntryRole R>
|
||||
Cache::DeleterFn GetNoopDeleterForRole() {
|
||||
static cache_entry_roles_detail::RegisteredNoopDeleter<R> reg;
|
||||
return reg.Delete;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
Vendored
+14
-13
@@ -10,8 +10,8 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "cache/cache_helpers.h"
|
||||
#include "cache/cache_key.h"
|
||||
#include "cache/typed_cache.h"
|
||||
#include "port/lang.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/status.h"
|
||||
@@ -111,14 +111,11 @@ class CacheEntryStatsCollector {
|
||||
// Gets or creates a shared instance of CacheEntryStatsCollector in the
|
||||
// cache itself, and saves into `ptr`. This shared_ptr will hold the
|
||||
// entry in cache until all refs are destroyed.
|
||||
static Status GetShared(Cache *raw_cache, SystemClock *clock,
|
||||
static Status GetShared(Cache *cache, SystemClock *clock,
|
||||
std::shared_ptr<CacheEntryStatsCollector> *ptr) {
|
||||
assert(raw_cache);
|
||||
BasicTypedCacheInterface<CacheEntryStatsCollector, CacheEntryRole::kMisc>
|
||||
cache{raw_cache};
|
||||
|
||||
const Slice &cache_key = GetCacheKey();
|
||||
auto h = cache.Lookup(cache_key);
|
||||
|
||||
Cache::Handle *h = cache->Lookup(cache_key);
|
||||
if (h == nullptr) {
|
||||
// Not yet in cache, but Cache doesn't provide a built-in way to
|
||||
// avoid racing insert. So we double-check under a shared mutex,
|
||||
@@ -126,15 +123,15 @@ class CacheEntryStatsCollector {
|
||||
STATIC_AVOID_DESTRUCTION(std::mutex, static_mutex);
|
||||
std::lock_guard<std::mutex> lock(static_mutex);
|
||||
|
||||
h = cache.Lookup(cache_key);
|
||||
h = cache->Lookup(cache_key);
|
||||
if (h == nullptr) {
|
||||
auto new_ptr = new CacheEntryStatsCollector(cache.get(), clock);
|
||||
auto new_ptr = new CacheEntryStatsCollector(cache, clock);
|
||||
// TODO: non-zero charge causes some tests that count block cache
|
||||
// usage to go flaky. Fix the problem somehow so we can use an
|
||||
// accurate charge.
|
||||
size_t charge = 0;
|
||||
Status s =
|
||||
cache.Insert(cache_key, new_ptr, charge, &h, Cache::Priority::HIGH);
|
||||
Status s = cache->Insert(cache_key, new_ptr, charge, Deleter, &h,
|
||||
Cache::Priority::HIGH);
|
||||
if (!s.ok()) {
|
||||
assert(h == nullptr);
|
||||
delete new_ptr;
|
||||
@@ -143,11 +140,11 @@ class CacheEntryStatsCollector {
|
||||
}
|
||||
}
|
||||
// If we reach here, shared entry is in cache with handle `h`.
|
||||
assert(cache.get()->GetCacheItemHelper(h) == cache.GetBasicHelper());
|
||||
assert(cache->GetDeleter(h) == Deleter);
|
||||
|
||||
// Build an aliasing shared_ptr that keeps `ptr` in cache while there
|
||||
// are references.
|
||||
*ptr = cache.SharedGuard(h);
|
||||
*ptr = MakeSharedCacheHandleGuard<CacheEntryStatsCollector>(cache, h);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -160,6 +157,10 @@ class CacheEntryStatsCollector {
|
||||
cache_(cache),
|
||||
clock_(clock) {}
|
||||
|
||||
static void Deleter(const Slice &, void *value) {
|
||||
delete static_cast<CacheEntryStatsCollector *>(value);
|
||||
}
|
||||
|
||||
static const Slice &GetCacheKey() {
|
||||
// For each template instantiation
|
||||
static CacheKey ckey = CacheKey::CreateUniqueForProcessLifetime();
|
||||
|
||||
Vendored
-41
@@ -1,41 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "cache/cache_helpers.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
void ReleaseCacheHandleCleanup(void* arg1, void* arg2) {
|
||||
Cache* const cache = static_cast<Cache*>(arg1);
|
||||
assert(cache);
|
||||
|
||||
Cache::Handle* const cache_handle = static_cast<Cache::Handle*>(arg2);
|
||||
assert(cache_handle);
|
||||
|
||||
cache->Release(cache_handle);
|
||||
}
|
||||
|
||||
Status WarmInCache(Cache* cache, const Slice& key, const Slice& saved,
|
||||
Cache::CreateContext* create_context,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
Cache::Priority priority, size_t* out_charge) {
|
||||
assert(helper);
|
||||
assert(helper->create_cb);
|
||||
Cache::ObjectPtr value;
|
||||
size_t charge;
|
||||
Status st = helper->create_cb(saved, CompressionType::kNoCompression,
|
||||
CacheTier::kVolatileTier, create_context,
|
||||
cache->memory_allocator(), &value, &charge);
|
||||
if (st.ok()) {
|
||||
st =
|
||||
cache->Insert(key, value, helper, charge, /*handle*/ nullptr, priority);
|
||||
if (out_charge) {
|
||||
*out_charge = charge;
|
||||
}
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+10
-24
@@ -7,7 +7,7 @@
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -17,17 +17,22 @@ template <typename T>
|
||||
T* GetFromCacheHandle(Cache* cache, Cache::Handle* handle) {
|
||||
assert(cache);
|
||||
assert(handle);
|
||||
|
||||
return static_cast<T*>(cache->Value(handle));
|
||||
}
|
||||
|
||||
// Simple generic deleter for Cache (to be used with Cache::Insert).
|
||||
template <typename T>
|
||||
void DeleteCacheEntry(const Slice& /* key */, void* value) {
|
||||
delete static_cast<T*>(value);
|
||||
}
|
||||
|
||||
// Turns a T* into a Slice so it can be used as a key with Cache.
|
||||
template <typename T>
|
||||
Slice GetSliceForKey(const T* t) {
|
||||
Slice GetSlice(const T* t) {
|
||||
return Slice(reinterpret_cast<const char*>(t), sizeof(T));
|
||||
}
|
||||
|
||||
void ReleaseCacheHandleCleanup(void* arg1, void* arg2);
|
||||
|
||||
// Generic resource management object for cache handles that releases the handle
|
||||
// when destroyed. Has unique ownership of the handle, so copying it is not
|
||||
// allowed, while moving it transfers ownership.
|
||||
@@ -79,16 +84,6 @@ class CacheHandleGuard {
|
||||
Cache::Handle* GetCacheHandle() const { return handle_; }
|
||||
T* GetValue() const { return value_; }
|
||||
|
||||
void TransferTo(Cleanable* cleanable) {
|
||||
if (cleanable) {
|
||||
if (handle_ != nullptr) {
|
||||
assert(cache_);
|
||||
cleanable->RegisterCleanup(&ReleaseCacheHandleCleanup, cache_, handle_);
|
||||
}
|
||||
}
|
||||
ResetFields();
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
ReleaseHandle();
|
||||
ResetFields();
|
||||
@@ -124,16 +119,7 @@ template <typename T>
|
||||
std::shared_ptr<T> MakeSharedCacheHandleGuard(Cache* cache,
|
||||
Cache::Handle* handle) {
|
||||
auto wrapper = std::make_shared<CacheHandleGuard<T>>(cache, handle);
|
||||
return std::shared_ptr<T>(wrapper, GetFromCacheHandle<T>(cache, handle));
|
||||
return std::shared_ptr<T>(wrapper, static_cast<T*>(cache->Value(handle)));
|
||||
}
|
||||
|
||||
// Given the persistable data (saved) for a block cache entry, parse that
|
||||
// into a cache entry object and insert it into the given cache. The charge
|
||||
// of the new entry can be returned to the caller through `out_charge`.
|
||||
Status WarmInCache(Cache* cache, const Slice& key, const Slice& saved,
|
||||
Cache::CreateContext* create_context,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
Cache::Priority priority = Cache::Priority::LOW,
|
||||
size_t* out_charge = nullptr);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
Vendored
+162
-182
@@ -8,7 +8,7 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/math.h"
|
||||
@@ -17,7 +17,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Value space plan for CacheKey:
|
||||
//
|
||||
// file_num_etc64_ | offset_etc64_ | Only generated by
|
||||
// session_etc64_ | offset_etc64_ | Only generated by
|
||||
// ---------------+---------------+------------------------------------------
|
||||
// 0 | 0 | Reserved for "empty" CacheKey()
|
||||
// 0 | > 0, < 1<<63 | CreateUniqueForCacheLifetime
|
||||
@@ -44,7 +44,7 @@ CacheKey CacheKey::CreateUniqueForProcessLifetime() {
|
||||
return CacheKey(0, id);
|
||||
}
|
||||
|
||||
// How we generate CacheKeys and base OffsetableCacheKey, assuming that
|
||||
// Value plan for CacheKeys from OffsetableCacheKey, assuming that
|
||||
// db_session_ids are generated from a base_session_id and
|
||||
// session_id_counter (by SemiStructuredUniqueIdGen+EncodeSessionId
|
||||
// in DBImpl::GenerateDbSessionId):
|
||||
@@ -56,108 +56,63 @@ CacheKey CacheKey::CreateUniqueForProcessLifetime() {
|
||||
// base_session_id (unstructured, from GenerateRawUniqueId)
|
||||
// session_id_counter (structured)
|
||||
// * usually much smaller than 2**24
|
||||
// orig_file_number (structured)
|
||||
// file_number (structured)
|
||||
// * usually smaller than 2**24
|
||||
// offset_in_file (structured, might skip lots of values)
|
||||
// * usually smaller than 2**32
|
||||
// max_offset determines placement of file_number to prevent
|
||||
// overlapping with offset
|
||||
//
|
||||
// Overall approach (see https://github.com/pdillinger/unique_id for
|
||||
// background):
|
||||
// Outputs come from bitwise-xor of the constituent pieces, low bits on left:
|
||||
//
|
||||
// First, we have three "structured" values, up to 64 bits each, that we
|
||||
// need to fit, without losses, into 128 bits. In practice, the values will
|
||||
// be small enough that they should fit. For example, applications generating
|
||||
// large SST files (large offsets) will naturally produce fewer files (small
|
||||
// file numbers). But we don't know ahead of time what bounds the values will
|
||||
// have.
|
||||
//
|
||||
// Second, we have unstructured inputs that enable distinct RocksDB processes
|
||||
// to pick a random point in space, likely very different from others. Xoring
|
||||
// the structured with the unstructured give us a cache key that is
|
||||
// structurally distinct between related keys (e.g. same file or same RocksDB
|
||||
// process) and distinct with high probability between unrelated keys.
|
||||
//
|
||||
// The problem of packing three structured values into the space for two is
|
||||
// complicated by the fact that we want to derive cache keys from SST unique
|
||||
// IDs, which have already combined structured and unstructured inputs in a
|
||||
// practically inseparable way. And we want a base cache key that works
|
||||
// with an offset of any size. So basically, we need to encode these three
|
||||
// structured values, each up to 64 bits, into 128 bits without knowing any
|
||||
// of their sizes. The DownwardInvolution() function gives us a mechanism to
|
||||
// accomplish this. (See its properties in math.h.) Specifically, for inputs
|
||||
// a, b, and c:
|
||||
// lower64 = DownwardInvolution(a) ^ ReverseBits(b);
|
||||
// upper64 = c ^ ReverseBits(a);
|
||||
// The 128-bit output is unique assuming there exist some i, j, and k
|
||||
// where a < 2**i, b < 2**j, c < 2**k, i <= 64, j <= 64, k <= 64, and
|
||||
// i + j + k <= 128. In other words, as long as there exist some bounds
|
||||
// that would allow us to pack the bits of a, b, and c into the output
|
||||
// if we know the bound, we can generate unique outputs without knowing
|
||||
// those bounds. To validate this claim, the inversion function (given
|
||||
// the bounds) has been implemented in CacheKeyDecoder in
|
||||
// db_block_cache_test.cc.
|
||||
//
|
||||
// With that in mind, the outputs in terms of the conceptual inputs look
|
||||
// like this, using bitwise-xor of the constituent pieces, low bits on left:
|
||||
//
|
||||
// |------------------------- file_num_etc64 -------------------------|
|
||||
// | +++++++++ base_session_id (lower 64 bits, involution) +++++++++ |
|
||||
// |------------------------- session_etc64 -------------------------|
|
||||
// | +++++++++++++++ base_session_id (lower 64 bits) +++++++++++++++ |
|
||||
// |-----------------------------------------------------------------|
|
||||
// | session_id_counter (involution) ..... | |
|
||||
// | session_id_counter ...| |
|
||||
// |-----------------------------------------------------------------|
|
||||
// | hash of: ++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
|
||||
// | * base_session_id (upper ~39 bits) |
|
||||
// | * db_id (~122 bits entropy) |
|
||||
// |-----------------------------------------------------------------|
|
||||
// | | ..... orig_file_number (reversed) |
|
||||
// | | ... file_number |
|
||||
// | | overflow & meta |
|
||||
// |-----------------------------------------------------------------|
|
||||
//
|
||||
//
|
||||
// |------------------------- offset_etc64 --------------------------|
|
||||
// | ++++++++++ base_session_id (lower 64 bits, reversed) ++++++++++ |
|
||||
// |-----------------------------------------------------------------|
|
||||
// | | ..... session_id_counter (reversed) |
|
||||
// | hash of: ++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
|
||||
// | * base_session_id (upper ~39 bits) |
|
||||
// | * db_id (~122 bits entropy) |
|
||||
// |-----------------------------------------------------------------|
|
||||
// | offset_in_file ............... | |
|
||||
// |-----------------------------------------------------------------|
|
||||
// | | file_number, 0-3 |
|
||||
// | | lower bytes |
|
||||
// |-----------------------------------------------------------------|
|
||||
//
|
||||
// Some oddities or inconveniences of this layout are due to deriving
|
||||
// the "base" cache key (without offset) from the SST unique ID (see
|
||||
// GetSstInternalUniqueId). Specifically,
|
||||
// * Lower 64 of base_session_id occurs in both output words (ok but
|
||||
// weird)
|
||||
// * The inclusion of db_id is bad for the conditions under which we
|
||||
// can guarantee uniqueness, but could be useful in some cases with
|
||||
// few small files per process, to make up for db session id only having
|
||||
// ~103 bits of entropy.
|
||||
// Based on max_offset, a maximal number of bytes 0..3 is chosen for
|
||||
// including from lower bits of file_number in offset_etc64. The choice
|
||||
// is encoded in two bits of metadata going into session_etc64, though
|
||||
// the common case of 3 bytes is encoded as 0 so that session_etc64
|
||||
// is unmodified by file_number concerns in the common case.
|
||||
//
|
||||
// In fact, if DB ids were not involved, we would be guaranteed unique
|
||||
// cache keys for files generated in a single process until total bits for
|
||||
// biggest session_id_counter, orig_file_number, and offset_in_file
|
||||
// reach 128 bits.
|
||||
// There is nothing preventing "file number overflow & meta" from meeting
|
||||
// and overlapping with session_id_counter, but reaching such a case requires
|
||||
// an intractable combination of large file offsets (thus at least some large
|
||||
// files), large file numbers (thus large number of files generated), and
|
||||
// large number of session IDs generated in a single process. A trillion each
|
||||
// (2**40) of session ids, offsets, and file numbers comes to 120 bits.
|
||||
// With two bits of metadata and byte granularity, this is on the verge of
|
||||
// overlap, but even in the overlap case, it doesn't seem likely that
|
||||
// a file from billions of files or session ids ago will still be live
|
||||
// or cached.
|
||||
//
|
||||
// With the DB id limitation, we only have nice guaranteed unique cache
|
||||
// keys for files generated in a single process until biggest
|
||||
// session_id_counter and offset_in_file reach combined 64 bits. This
|
||||
// is quite good in practice because we can have millions of DB Opens
|
||||
// with terabyte size SST files, or billions of DB Opens with gigabyte
|
||||
// size SST files.
|
||||
// In fact, if our SST files are all < 4TB (see
|
||||
// BlockBasedTable::kMaxFileSizeStandardEncoding), then SST files generated
|
||||
// in a single process are guaranteed to have unique cache keys, unless/until
|
||||
// number session ids * max file number = 2**86, e.g. 1 trillion DB::Open in
|
||||
// a single process and 64 trillion files generated. Even at that point, to
|
||||
// see a collision we would need a miraculous re-synchronization of session
|
||||
// id and file number, along with a live file or stale cache entry from
|
||||
// trillions of files ago.
|
||||
//
|
||||
// One of the considerations in the translation between existing SST unique
|
||||
// IDs and base cache keys is supporting better SST unique IDs in a future
|
||||
// format_version. If we use a process-wide file counter instead of
|
||||
// session counter and file numbers, we only need to combine two 64-bit values
|
||||
// instead of three. But we don't want to track unique ID versions in the
|
||||
// manifest, so we want to keep the same translation layer between SST unique
|
||||
// IDs and base cache keys, even with updated SST unique IDs. If the new
|
||||
// unique IDs put the file counter where the orig_file_number was, and
|
||||
// use no structured field where session_id_counter was, then our translation
|
||||
// layer works fine for two structured fields as well as three (for
|
||||
// compatibility). The small computation for the translation (one
|
||||
// DownwardInvolution(), two ReverseBits(), both ~log(64) instructions deep)
|
||||
// is negligible for computing as part of SST file reader open.
|
||||
//
|
||||
// More on how https://github.com/pdillinger/unique_id applies here:
|
||||
// How https://github.com/pdillinger/unique_id applies here:
|
||||
// Every bit of output always includes "unstructured" uniqueness bits and
|
||||
// often combines with "structured" uniqueness bits. The "unstructured" bits
|
||||
// change infrequently: only when we cannot guarantee our state tracking for
|
||||
@@ -186,11 +141,12 @@ CacheKey CacheKey::CreateUniqueForProcessLifetime() {
|
||||
// 128 bits cache key size
|
||||
// - 55 <- ideal size for byte offsets + file numbers
|
||||
// - 2 <- bits for offsets and file numbers not exactly powers of two
|
||||
// - 2 <- bits for file number encoding metadata
|
||||
// + 2 <- bits saved not using byte offsets in BlockBasedTable::GetCacheKey
|
||||
// ----
|
||||
// 73 <- bits remaining for distinguishing session IDs
|
||||
// The probability of a collision in 73 bits of session ID data is less than
|
||||
// 1 in 2**(73 - (2 * 16)), or roughly 1 in a trillion. And this assumes all
|
||||
// 71 <- bits remaining for distinguishing session IDs
|
||||
// The probability of a collision in 71 bits of session ID data is less than
|
||||
// 1 in 2**(71 - (2 * 16)), or roughly 1 in a trillion. And this assumes all
|
||||
// data from the last 180 days is in cache for potential collision, and that
|
||||
// cache keys under each session id exhaustively cover the remaining 57 bits
|
||||
// while in reality they'll only cover a small fraction of it.
|
||||
@@ -204,7 +160,7 @@ CacheKey CacheKey::CreateUniqueForProcessLifetime() {
|
||||
// Now suppose we have many DBs per host, say 2**10, with same host-wide write
|
||||
// rate and process/session lifetime. File numbers will be ~10 bits smaller
|
||||
// and we will have 2**10 times as many session IDs because of simultaneous
|
||||
// lifetimes. So now collision chance is less than 1 in 2**(83 - (2 * 26)),
|
||||
// lifetimes. So now collision chance is less than 1 in 2**(81 - (2 * 26)),
|
||||
// or roughly 1 in a billion.
|
||||
//
|
||||
// Suppose instead we generated random or hashed cache keys for each
|
||||
@@ -220,17 +176,17 @@ CacheKey CacheKey::CreateUniqueForProcessLifetime() {
|
||||
// activity over many months, by making some pessimistic simplifying
|
||||
// assumptions. See class StressCacheKey in cache_bench_tool.cc for details.
|
||||
// Here is some sample output with
|
||||
// `./cache_bench -stress_cache_key -sck_keep_bits=43`:
|
||||
// `./cache_bench -stress_cache_key -sck_keep_bits=40`:
|
||||
//
|
||||
// Total cache or DBs size: 32TiB Writing 925.926 MiB/s or 76.2939TiB/day
|
||||
// Multiply by 1.15292e+18 to correct for simulation losses (but still
|
||||
// Multiply by 9.22337e+18 to correct for simulation losses (but still
|
||||
// assume whole file cached)
|
||||
//
|
||||
// These come from default settings of 2.5M files per day of 32 MB each, and
|
||||
// `-sck_keep_bits=43` means that to represent a single file, we are only
|
||||
// keeping 43 bits of the 128-bit (base) cache key. With file size of 2**25
|
||||
// contiguous keys (pessimistic), our simulation is about 2\*\*(128-43-25) or
|
||||
// about 1 billion billion times more prone to collision than reality.
|
||||
// `-sck_keep_bits=40` means that to represent a single file, we are only
|
||||
// keeping 40 bits of the 128-bit (base) cache key. With file size of 2**25
|
||||
// contiguous keys (pessimistic), our simulation is about 2\*\*(128-40-25) or
|
||||
// about 9 billion billion times more prone to collision than reality.
|
||||
//
|
||||
// More default assumptions, relatively pessimistic:
|
||||
// * 100 DBs in same process (doesn't matter much)
|
||||
@@ -238,55 +194,49 @@ CacheKey CacheKey::CreateUniqueForProcessLifetime() {
|
||||
// average every 100 files generated
|
||||
// * Restart process (all new session IDs unrelated to old) 24 times per day
|
||||
//
|
||||
// After enough data, we get a result at the end (-sck_keep_bits=43):
|
||||
// After enough data, we get a result at the end (-sck_keep_bits=40):
|
||||
//
|
||||
// (keep 43 bits) 18 collisions after 2 x 90 days, est 10 days between
|
||||
// (1.15292e+19 corrected)
|
||||
// (keep 40 bits) 17 collisions after 2 x 90 days, est 10.5882 days between
|
||||
// (9.76592e+19 corrected)
|
||||
//
|
||||
// If we believe the (pessimistic) simulation and the mathematical
|
||||
// extrapolation, we would need to run a billion machines all for 11 billion
|
||||
// extrapolation, we would need to run a billion machines all for 97 billion
|
||||
// days to expect a cache key collision. To help verify that our extrapolation
|
||||
// ("corrected") is robust, we can make our simulation more precise by
|
||||
// increasing the "keep" bits, which takes more running time to get enough
|
||||
// ("corrected") is robust, we can make our simulation more precise with
|
||||
// `-sck_keep_bits=41` and `42`, which takes more running time to get enough
|
||||
// collision data:
|
||||
//
|
||||
// (keep 44 bits) 16 collisions after 5 x 90 days, est 28.125 days between
|
||||
// (1.6213e+19 corrected)
|
||||
// (keep 45 bits) 15 collisions after 7 x 90 days, est 42 days between
|
||||
// (1.21057e+19 corrected)
|
||||
// (keep 46 bits) 15 collisions after 17 x 90 days, est 102 days between
|
||||
// (1.46997e+19 corrected)
|
||||
// (keep 47 bits) 15 collisions after 49 x 90 days, est 294 days between
|
||||
// (2.11849e+19 corrected)
|
||||
// (keep 41 bits) 16 collisions after 4 x 90 days, est 22.5 days between
|
||||
// (1.03763e+20 corrected)
|
||||
// (keep 42 bits) 19 collisions after 10 x 90 days, est 47.3684 days between
|
||||
// (1.09224e+20 corrected)
|
||||
//
|
||||
// The extrapolated prediction seems to be within noise (sampling error).
|
||||
// The extrapolated prediction is very close. If anything, we might have some
|
||||
// very small losses of structured data (see class StressCacheKey in
|
||||
// cache_bench_tool.cc) leading to more accurate & more attractive prediction
|
||||
// with more bits kept.
|
||||
//
|
||||
// With the `-sck_randomize` option, we can see that typical workloads like
|
||||
// above have lower collision probability than "random" cache keys (note:
|
||||
// offsets still non-randomized) by a modest amount (roughly 2-3x less
|
||||
// collision prone than random), which should make us reasonably comfortable
|
||||
// even in "degenerate" cases (e.g. repeatedly launch a process to generate
|
||||
// one file with SstFileWriter):
|
||||
// offsets still non-randomized) by a modest amount (roughly 20x less collision
|
||||
// prone than random), which should make us reasonably comfortable even in
|
||||
// "degenerate" cases (e.g. repeatedly launch a process to generate 1 file
|
||||
// with SstFileWriter):
|
||||
//
|
||||
// (rand 43 bits) 22 collisions after 1 x 90 days, est 4.09091 days between
|
||||
// (4.7165e+18 corrected)
|
||||
// (rand 40 bits) 197 collisions after 1 x 90 days, est 0.456853 days between
|
||||
// (4.21372e+18 corrected)
|
||||
//
|
||||
// We can see that with more frequent process restarts,
|
||||
// -sck_restarts_per_day=5000, which means more all-new session IDs, we get
|
||||
// closer to the "random" cache key performance:
|
||||
// We can see that with more frequent process restarts (all new session IDs),
|
||||
// we get closer to the "random" cache key performance:
|
||||
//
|
||||
// 15 collisions after 1 x 90 days, est 6 days between (6.91753e+18 corrected)
|
||||
//
|
||||
// And with less frequent process restarts and re-opens,
|
||||
// -sck_restarts_per_day=1 -sck_reopen_nfiles=1000, we get lower collision
|
||||
// probability:
|
||||
//
|
||||
// 18 collisions after 8 x 90 days, est 40 days between (4.61169e+19 corrected)
|
||||
// (-sck_restarts_per_day=5000): 140 collisions after 1 x 90 days, ...
|
||||
// (5.92931e+18 corrected)
|
||||
//
|
||||
// Other tests have been run to validate other conditions behave as expected,
|
||||
// never behaving "worse than random" unless we start chopping off structured
|
||||
// data.
|
||||
//
|
||||
//
|
||||
// Conclusion: Even in extreme cases, rapidly burning through "all new" IDs
|
||||
// that only arise when a new process is started, the chance of any cache key
|
||||
// collisions in a giant fleet of machines is negligible. Especially when
|
||||
@@ -299,66 +249,96 @@ CacheKey CacheKey::CreateUniqueForProcessLifetime() {
|
||||
// quantify) block cache corruptions, including collisions, should be added.
|
||||
OffsetableCacheKey::OffsetableCacheKey(const std::string &db_id,
|
||||
const std::string &db_session_id,
|
||||
uint64_t file_number) {
|
||||
UniqueId64x2 internal_id;
|
||||
Status s = GetSstInternalUniqueId(db_id, db_session_id, file_number,
|
||||
&internal_id, /*force=*/true);
|
||||
assert(s.ok());
|
||||
*this = FromInternalUniqueId(&internal_id);
|
||||
}
|
||||
|
||||
OffsetableCacheKey OffsetableCacheKey::FromInternalUniqueId(UniqueIdPtr id) {
|
||||
uint64_t session_lower = id.ptr[0];
|
||||
uint64_t file_num_etc = id.ptr[1];
|
||||
|
||||
uint64_t file_number,
|
||||
uint64_t max_offset) {
|
||||
#ifndef NDEBUG
|
||||
bool is_empty = session_lower == 0 && file_num_etc == 0;
|
||||
max_offset_ = max_offset;
|
||||
#endif
|
||||
// Closely related to GetSstInternalUniqueId, but only need 128 bits and
|
||||
// need to include an offset within the file.
|
||||
// See also https://github.com/pdillinger/unique_id for background.
|
||||
uint64_t session_upper = 0; // Assignment to appease clang-analyze
|
||||
uint64_t session_lower = 0; // Assignment to appease clang-analyze
|
||||
{
|
||||
Status s = DecodeSessionId(db_session_id, &session_upper, &session_lower);
|
||||
if (!s.ok()) {
|
||||
// A reasonable fallback in case malformed
|
||||
Hash2x64(db_session_id.data(), db_session_id.size(), &session_upper,
|
||||
&session_lower);
|
||||
}
|
||||
}
|
||||
|
||||
// Hash the session upper (~39 bits entropy) and DB id (120+ bits entropy)
|
||||
// for more global uniqueness entropy.
|
||||
// (It is possible that many DBs descended from one common DB id are copied
|
||||
// around and proliferate, in which case session id is critical, but it is
|
||||
// more common for different DBs to have different DB ids.)
|
||||
uint64_t db_hash = Hash64(db_id.data(), db_id.size(), session_upper);
|
||||
|
||||
// This establishes the db+session id part of the cache key.
|
||||
//
|
||||
// Exactly preserve (in common cases; see modifiers below) session lower to
|
||||
// ensure that session ids generated during the same process lifetime are
|
||||
// guaranteed unique.
|
||||
//
|
||||
// We put this first for CommonPrefixSlice(), so that a small-ish set of
|
||||
// cache key prefixes to cover entries relevant to any DB.
|
||||
session_etc64_ = session_lower;
|
||||
// This provides extra entopy in case of different DB id or process
|
||||
// generating a session id, but is also partly/variably obscured by
|
||||
// file_number and offset (see below).
|
||||
offset_etc64_ = db_hash;
|
||||
|
||||
// Into offset_etc64_ we are (eventually) going to pack & xor in an offset and
|
||||
// a file_number, but we might need the file_number to overflow into
|
||||
// session_etc64_. (There must only be one session_etc64_ value per
|
||||
// file, and preferably shared among many files.)
|
||||
//
|
||||
// Figure out how many bytes of file_number we are going to be able to
|
||||
// pack in with max_offset, though our encoding will only support packing
|
||||
// in up to 3 bytes of file_number. (16M file numbers is enough for a new
|
||||
// file number every second for half a year.)
|
||||
int file_number_bytes_in_offset_etc =
|
||||
(63 - FloorLog2(max_offset | 0x100000000U)) / 8;
|
||||
int file_number_bits_in_offset_etc = file_number_bytes_in_offset_etc * 8;
|
||||
|
||||
// Assert two bits of metadata
|
||||
assert(file_number_bytes_in_offset_etc >= 0 &&
|
||||
file_number_bytes_in_offset_etc <= 3);
|
||||
// Assert we couldn't have used a larger allowed number of bytes (shift
|
||||
// would chop off bytes).
|
||||
assert(file_number_bytes_in_offset_etc == 3 ||
|
||||
(max_offset << (file_number_bits_in_offset_etc + 8) >>
|
||||
(file_number_bits_in_offset_etc + 8)) != max_offset);
|
||||
|
||||
uint64_t mask = (uint64_t{1} << (file_number_bits_in_offset_etc)) - 1;
|
||||
// Pack into high bits of etc so that offset can go in low bits of etc
|
||||
// TODO: could be EndianSwapValue?
|
||||
uint64_t offset_etc_modifier = ReverseBits(file_number & mask);
|
||||
assert(offset_etc_modifier << file_number_bits_in_offset_etc == 0U);
|
||||
|
||||
// Overflow and 3 - byte count (likely both zero) go into session_id part
|
||||
uint64_t session_etc_modifier =
|
||||
(file_number >> file_number_bits_in_offset_etc << 2) |
|
||||
static_cast<uint64_t>(3 - file_number_bytes_in_offset_etc);
|
||||
// Packed into high bits to minimize interference with session id counter.
|
||||
session_etc_modifier = ReverseBits(session_etc_modifier);
|
||||
|
||||
// Assert session_id part is only modified in extreme cases
|
||||
assert(session_etc_modifier == 0 || file_number > /*3 bytes*/ 0xffffffU ||
|
||||
max_offset > /*5 bytes*/ 0xffffffffffU);
|
||||
|
||||
// Xor in the modifiers
|
||||
session_etc64_ ^= session_etc_modifier;
|
||||
offset_etc64_ ^= offset_etc_modifier;
|
||||
|
||||
// Although DBImpl guarantees (in recent versions) that session_lower is not
|
||||
// zero, that's not entirely sufficient to guarantee that file_num_etc64_ is
|
||||
// zero, that's not entirely sufficient to guarantee that session_etc64_ is
|
||||
// not zero (so that the 0 case can be used by CacheKey::CreateUnique*)
|
||||
// However, if we are given an "empty" id as input, then we should produce
|
||||
// "empty" as output.
|
||||
// As a consequence, this function is only bijective assuming
|
||||
// id[0] == 0 only if id[1] == 0.
|
||||
if (session_lower == 0U) {
|
||||
session_lower = file_num_etc;
|
||||
if (session_etc64_ == 0U) {
|
||||
session_etc64_ = session_upper | 1U;
|
||||
}
|
||||
|
||||
// See comments above for how DownwardInvolution and ReverseBits
|
||||
// make this function invertible under various assumptions.
|
||||
OffsetableCacheKey rv;
|
||||
rv.file_num_etc64_ =
|
||||
DownwardInvolution(session_lower) ^ ReverseBits(file_num_etc);
|
||||
rv.offset_etc64_ = ReverseBits(session_lower);
|
||||
|
||||
// Because of these transformations and needing to allow arbitrary
|
||||
// offset (thus, second 64 bits of cache key might be 0), we need to
|
||||
// make some correction to ensure the first 64 bits is not 0.
|
||||
// Fortunately, the transformation ensures the second 64 bits is not 0
|
||||
// for non-empty base key, so we can swap in the case one is 0 without
|
||||
// breaking bijectivity (assuming condition above).
|
||||
assert(is_empty || rv.offset_etc64_ > 0);
|
||||
if (rv.file_num_etc64_ == 0) {
|
||||
std::swap(rv.file_num_etc64_, rv.offset_etc64_);
|
||||
}
|
||||
assert(is_empty || rv.file_num_etc64_ > 0);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Inverse of FromInternalUniqueId (assuming file_num_etc64 == 0 only if
|
||||
// offset_etc64 == 0)
|
||||
UniqueId64x2 OffsetableCacheKey::ToInternalUniqueId() {
|
||||
uint64_t a = file_num_etc64_;
|
||||
uint64_t b = offset_etc64_;
|
||||
if (b == 0) {
|
||||
std::swap(a, b);
|
||||
}
|
||||
UniqueId64x2 rv;
|
||||
rv[0] = ReverseBits(b);
|
||||
rv[1] = ReverseBits(a ^ DownwardInvolution(rv[0]));
|
||||
return rv;
|
||||
assert(session_etc64_ != 0);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
Vendored
+29
-40
@@ -9,7 +9,6 @@
|
||||
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -34,10 +33,10 @@ class CacheKey {
|
||||
public:
|
||||
// For convenience, constructs an "empty" cache key that is never returned
|
||||
// by other means.
|
||||
inline CacheKey() : file_num_etc64_(), offset_etc64_() {}
|
||||
inline CacheKey() : session_etc64_(), offset_etc64_() {}
|
||||
|
||||
inline bool IsEmpty() const {
|
||||
return (file_num_etc64_ == 0) & (offset_etc64_ == 0);
|
||||
return (session_etc64_ == 0) & (offset_etc64_ == 0);
|
||||
}
|
||||
|
||||
// Use this cache key as a Slice (byte order is endianness-dependent)
|
||||
@@ -60,14 +59,12 @@ class CacheKey {
|
||||
|
||||
protected:
|
||||
friend class OffsetableCacheKey;
|
||||
CacheKey(uint64_t file_num_etc64, uint64_t offset_etc64)
|
||||
: file_num_etc64_(file_num_etc64), offset_etc64_(offset_etc64) {}
|
||||
uint64_t file_num_etc64_;
|
||||
CacheKey(uint64_t session_etc64, uint64_t offset_etc64)
|
||||
: session_etc64_(session_etc64), offset_etc64_(offset_etc64) {}
|
||||
uint64_t session_etc64_;
|
||||
uint64_t offset_etc64_;
|
||||
};
|
||||
|
||||
constexpr uint8_t kCacheKeySize = static_cast<uint8_t>(sizeof(CacheKey));
|
||||
|
||||
// A file-specific generator of cache keys, sometimes referred to as the
|
||||
// "base" cache key for a file because all the cache keys for various offsets
|
||||
// within the file are computed using simple arithmetic. The basis for the
|
||||
@@ -86,58 +83,50 @@ class OffsetableCacheKey : private CacheKey {
|
||||
inline OffsetableCacheKey() : CacheKey() {}
|
||||
|
||||
// Constructs an OffsetableCacheKey with the given information about a file.
|
||||
// This constructor never generates an "empty" base key.
|
||||
// max_offset is based on file size (see WithOffset) and is required here to
|
||||
// choose an appropriate (sub-)encoding. This constructor never generates an
|
||||
// "empty" base key.
|
||||
OffsetableCacheKey(const std::string &db_id, const std::string &db_session_id,
|
||||
uint64_t file_number);
|
||||
|
||||
// Creates an OffsetableCacheKey from an SST unique ID, so that cache keys
|
||||
// can be derived from DB manifest data before reading the file from
|
||||
// storage--so that every part of the file can potentially go in a persistent
|
||||
// cache.
|
||||
//
|
||||
// Calling GetSstInternalUniqueId() on a db_id, db_session_id, and
|
||||
// file_number and passing the result to this function produces the same
|
||||
// base cache key as feeding those inputs directly to the constructor.
|
||||
//
|
||||
// This is a bijective transformation assuming either id is empty or
|
||||
// lower 64 bits is non-zero:
|
||||
// * Empty (all zeros) input -> empty (all zeros) output
|
||||
// * Lower 64 input is non-zero -> lower 64 output (file_num_etc64_) is
|
||||
// non-zero
|
||||
static OffsetableCacheKey FromInternalUniqueId(UniqueIdPtr id);
|
||||
|
||||
// This is the inverse transformation to the above, assuming either empty
|
||||
// or lower 64 bits (file_num_etc64_) is non-zero. Perhaps only useful for
|
||||
// testing.
|
||||
UniqueId64x2 ToInternalUniqueId();
|
||||
uint64_t file_number, uint64_t max_offset);
|
||||
|
||||
inline bool IsEmpty() const {
|
||||
bool result = file_num_etc64_ == 0;
|
||||
bool result = session_etc64_ == 0;
|
||||
assert(!(offset_etc64_ > 0 && result));
|
||||
return result;
|
||||
}
|
||||
|
||||
// Construct a CacheKey for an offset within a file. An offset is not
|
||||
// necessarily a byte offset if a smaller unique identifier of keyable
|
||||
// offsets is used.
|
||||
// Construct a CacheKey for an offset within a file, which must be
|
||||
// <= max_offset provided in constructor. An offset is not necessarily a
|
||||
// byte offset if a smaller unique identifier of keyable offsets is used.
|
||||
//
|
||||
// This class was designed to make this hot code extremely fast.
|
||||
inline CacheKey WithOffset(uint64_t offset) const {
|
||||
assert(!IsEmpty());
|
||||
return CacheKey(file_num_etc64_, offset_etc64_ ^ offset);
|
||||
assert(offset <= max_offset_);
|
||||
return CacheKey(session_etc64_, offset_etc64_ ^ offset);
|
||||
}
|
||||
|
||||
// The "common prefix" is a shared prefix for all the returned CacheKeys.
|
||||
// It is specific to the file but the same for all offsets within the file.
|
||||
// The "common prefix" is a shared prefix for all the returned CacheKeys,
|
||||
// that also happens to usually be the same among many files in the same DB,
|
||||
// so is efficient and highly accurate (not perfectly) for DB-specific cache
|
||||
// dump selection (but not file-specific).
|
||||
static constexpr size_t kCommonPrefixSize = 8;
|
||||
inline Slice CommonPrefixSlice() const {
|
||||
static_assert(sizeof(file_num_etc64_) == kCommonPrefixSize,
|
||||
static_assert(sizeof(session_etc64_) == kCommonPrefixSize,
|
||||
"8 byte common prefix expected");
|
||||
assert(!IsEmpty());
|
||||
assert(&this->file_num_etc64_ == static_cast<const void *>(this));
|
||||
assert(&this->session_etc64_ == static_cast<const void *>(this));
|
||||
|
||||
return Slice(reinterpret_cast<const char *>(this), kCommonPrefixSize);
|
||||
}
|
||||
|
||||
// For any max_offset <= this value, the same encoding scheme is guaranteed.
|
||||
static constexpr uint64_t kMaxOffsetStandardEncoding = 0xffffffffffU;
|
||||
|
||||
private:
|
||||
#ifndef NDEBUG
|
||||
uint64_t max_offset_ = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
Vendored
+72
-68
@@ -13,49 +13,31 @@
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "cache/cache_entry_roles.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "table/block_based/reader_common.h"
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationManagerImpl<R>::CacheReservationHandle::CacheReservationHandle(
|
||||
std::size_t incremental_memory_used,
|
||||
std::shared_ptr<CacheReservationManagerImpl> cache_res_mgr)
|
||||
: incremental_memory_used_(incremental_memory_used) {
|
||||
assert(cache_res_mgr);
|
||||
cache_res_mgr_ = cache_res_mgr;
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationManagerImpl<
|
||||
R>::CacheReservationHandle::~CacheReservationHandle() {
|
||||
Status s = cache_res_mgr_->ReleaseCacheReservation(incremental_memory_used_);
|
||||
s.PermitUncheckedError();
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationManagerImpl<R>::CacheReservationManagerImpl(
|
||||
std::shared_ptr<Cache> cache, bool delayed_decrease)
|
||||
: cache_(cache),
|
||||
delayed_decrease_(delayed_decrease),
|
||||
CacheReservationManager::CacheReservationManager(std::shared_ptr<Cache> cache,
|
||||
bool delayed_decrease)
|
||||
: delayed_decrease_(delayed_decrease),
|
||||
cache_allocated_size_(0),
|
||||
memory_used_(0) {
|
||||
assert(cache != nullptr);
|
||||
cache_ = cache;
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationManagerImpl<R>::~CacheReservationManagerImpl() {
|
||||
CacheReservationManager::~CacheReservationManager() {
|
||||
for (auto* handle : dummy_handles_) {
|
||||
cache_.ReleaseAndEraseIfLastRef(handle);
|
||||
cache_->Release(handle, true);
|
||||
}
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
Status CacheReservationManagerImpl<R>::UpdateCacheReservation(
|
||||
Status CacheReservationManager::UpdateCacheReservation(
|
||||
std::size_t new_mem_used) {
|
||||
memory_used_ = new_mem_used;
|
||||
std::size_t cur_cache_allocated_size =
|
||||
@@ -63,7 +45,7 @@ Status CacheReservationManagerImpl<R>::UpdateCacheReservation(
|
||||
if (new_mem_used == cur_cache_allocated_size) {
|
||||
return Status::OK();
|
||||
} else if (new_mem_used > cur_cache_allocated_size) {
|
||||
Status s = IncreaseCacheReservation(new_mem_used);
|
||||
Status s = IncreaseCacheReservation<R>(new_mem_used);
|
||||
return s;
|
||||
} else {
|
||||
// In delayed decrease mode, we don't decrease cache reservation
|
||||
@@ -84,37 +66,47 @@ Status CacheReservationManagerImpl<R>::UpdateCacheReservation(
|
||||
}
|
||||
}
|
||||
|
||||
// Explicitly instantiate templates for "CacheEntryRole" values we use.
|
||||
// This makes it possible to keep the template definitions in the .cc file.
|
||||
template Status CacheReservationManager::UpdateCacheReservation<
|
||||
CacheEntryRole::kWriteBuffer>(std::size_t new_mem_used);
|
||||
template Status CacheReservationManager::UpdateCacheReservation<
|
||||
CacheEntryRole::kCompressionDictionaryBuildingBuffer>(
|
||||
std::size_t new_mem_used);
|
||||
// For cache reservation manager unit tests
|
||||
template Status CacheReservationManager::UpdateCacheReservation<
|
||||
CacheEntryRole::kMisc>(std::size_t new_mem_used);
|
||||
|
||||
template <CacheEntryRole R>
|
||||
Status CacheReservationManagerImpl<R>::MakeCacheReservation(
|
||||
Status CacheReservationManager::MakeCacheReservation(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle>* handle) {
|
||||
assert(handle);
|
||||
std::unique_ptr<CacheReservationHandle<R>>* handle) {
|
||||
assert(handle != nullptr);
|
||||
Status s =
|
||||
UpdateCacheReservation(GetTotalMemoryUsed() + incremental_memory_used);
|
||||
(*handle).reset(new CacheReservationManagerImpl::CacheReservationHandle(
|
||||
incremental_memory_used,
|
||||
std::enable_shared_from_this<
|
||||
CacheReservationManagerImpl<R>>::shared_from_this()));
|
||||
UpdateCacheReservation<R>(GetTotalMemoryUsed() + incremental_memory_used);
|
||||
(*handle).reset(new CacheReservationHandle<R>(incremental_memory_used,
|
||||
shared_from_this()));
|
||||
return s;
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
Status CacheReservationManagerImpl<R>::ReleaseCacheReservation(
|
||||
std::size_t incremental_memory_used) {
|
||||
assert(GetTotalMemoryUsed() >= incremental_memory_used);
|
||||
std::size_t updated_total_mem_used =
|
||||
GetTotalMemoryUsed() - incremental_memory_used;
|
||||
Status s = UpdateCacheReservation(updated_total_mem_used);
|
||||
return s;
|
||||
}
|
||||
template Status
|
||||
CacheReservationManager::MakeCacheReservation<CacheEntryRole::kMisc>(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationHandle<CacheEntryRole::kMisc>>* handle);
|
||||
template Status CacheReservationManager::MakeCacheReservation<
|
||||
CacheEntryRole::kFilterConstruction>(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<
|
||||
CacheReservationHandle<CacheEntryRole::kFilterConstruction>>* handle);
|
||||
|
||||
template <CacheEntryRole R>
|
||||
Status CacheReservationManagerImpl<R>::IncreaseCacheReservation(
|
||||
Status CacheReservationManager::IncreaseCacheReservation(
|
||||
std::size_t new_mem_used) {
|
||||
Status return_status = Status::OK();
|
||||
while (new_mem_used > cache_allocated_size_.load(std::memory_order_relaxed)) {
|
||||
Cache::Handle* handle = nullptr;
|
||||
return_status = cache_.Insert(GetNextCacheKey(), kSizeDummyEntry, &handle);
|
||||
return_status = cache_->Insert(GetNextCacheKey(), nullptr, kSizeDummyEntry,
|
||||
GetNoopDeleterForRole<R>(), &handle);
|
||||
|
||||
if (return_status != Status::OK()) {
|
||||
return return_status;
|
||||
@@ -126,8 +118,7 @@ Status CacheReservationManagerImpl<R>::IncreaseCacheReservation(
|
||||
return return_status;
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
Status CacheReservationManagerImpl<R>::DecreaseCacheReservation(
|
||||
Status CacheReservationManager::DecreaseCacheReservation(
|
||||
std::size_t new_mem_used) {
|
||||
Status return_status = Status::OK();
|
||||
|
||||
@@ -139,25 +130,22 @@ Status CacheReservationManagerImpl<R>::DecreaseCacheReservation(
|
||||
cache_allocated_size_.load(std::memory_order_relaxed)) {
|
||||
assert(!dummy_handles_.empty());
|
||||
auto* handle = dummy_handles_.back();
|
||||
cache_.ReleaseAndEraseIfLastRef(handle);
|
||||
cache_->Release(handle, true);
|
||||
dummy_handles_.pop_back();
|
||||
cache_allocated_size_ -= kSizeDummyEntry;
|
||||
}
|
||||
return return_status;
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
std::size_t CacheReservationManagerImpl<R>::GetTotalReservedCacheSize() {
|
||||
std::size_t CacheReservationManager::GetTotalReservedCacheSize() {
|
||||
return cache_allocated_size_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
std::size_t CacheReservationManagerImpl<R>::GetTotalMemoryUsed() {
|
||||
std::size_t CacheReservationManager::GetTotalMemoryUsed() {
|
||||
return memory_used_;
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
Slice CacheReservationManagerImpl<R>::GetNextCacheKey() {
|
||||
Slice CacheReservationManager::GetNextCacheKey() {
|
||||
// Calling this function will have the side-effect of changing the
|
||||
// underlying cache_key_ that is shared among other keys generated from this
|
||||
// fucntion. Therefore please make sure the previous keys are saved/copied
|
||||
@@ -167,18 +155,34 @@ Slice CacheReservationManagerImpl<R>::GetNextCacheKey() {
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
const Cache::CacheItemHelper*
|
||||
CacheReservationManagerImpl<R>::TEST_GetCacheItemHelperForRole() {
|
||||
return CacheInterface::GetHelper();
|
||||
Cache::DeleterFn CacheReservationManager::TEST_GetNoopDeleterForRole() {
|
||||
return GetNoopDeleterForRole<R>();
|
||||
}
|
||||
|
||||
template class CacheReservationManagerImpl<
|
||||
CacheEntryRole::kBlockBasedTableReader>;
|
||||
template class CacheReservationManagerImpl<
|
||||
CacheEntryRole::kCompressionDictionaryBuildingBuffer>;
|
||||
template class CacheReservationManagerImpl<CacheEntryRole::kFilterConstruction>;
|
||||
template class CacheReservationManagerImpl<CacheEntryRole::kMisc>;
|
||||
template class CacheReservationManagerImpl<CacheEntryRole::kWriteBuffer>;
|
||||
template class CacheReservationManagerImpl<CacheEntryRole::kFileMetadata>;
|
||||
template class CacheReservationManagerImpl<CacheEntryRole::kBlobCache>;
|
||||
template Cache::DeleterFn CacheReservationManager::TEST_GetNoopDeleterForRole<
|
||||
CacheEntryRole::kFilterConstruction>();
|
||||
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationHandle<R>::CacheReservationHandle(
|
||||
std::size_t incremental_memory_used,
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr)
|
||||
: incremental_memory_used_(incremental_memory_used) {
|
||||
assert(cache_res_mgr != nullptr);
|
||||
cache_res_mgr_ = cache_res_mgr;
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationHandle<R>::~CacheReservationHandle() {
|
||||
assert(cache_res_mgr_ != nullptr);
|
||||
assert(cache_res_mgr_->GetTotalMemoryUsed() >= incremental_memory_used_);
|
||||
|
||||
Status s = cache_res_mgr_->UpdateCacheReservation<R>(
|
||||
cache_res_mgr_->GetTotalMemoryUsed() - incremental_memory_used_);
|
||||
s.PermitUncheckedError();
|
||||
}
|
||||
|
||||
// Explicitly instantiate templates for "CacheEntryRole" values we use.
|
||||
// This makes it possible to keep the template definitions in the .cc file.
|
||||
template class CacheReservationHandle<CacheEntryRole::kMisc>;
|
||||
template class CacheReservationHandle<CacheEntryRole::kFilterConstruction>;
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
Vendored
+53
-180
@@ -13,96 +13,54 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "cache/cache_entry_roles.h"
|
||||
#include "cache/cache_key.h"
|
||||
#include "cache/typed_cache.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
// CacheReservationManager is an interface for reserving cache space for the
|
||||
// memory used
|
||||
class CacheReservationManager {
|
||||
public:
|
||||
// CacheReservationHandle is for managing the lifetime of a cache reservation
|
||||
// for an incremental amount of memory used (i.e, incremental_memory_used)
|
||||
class CacheReservationHandle {
|
||||
public:
|
||||
virtual ~CacheReservationHandle() {}
|
||||
};
|
||||
virtual ~CacheReservationManager() {}
|
||||
virtual Status UpdateCacheReservation(std::size_t new_memory_used) = 0;
|
||||
// TODO(hx235): replace the usage of
|
||||
// `UpdateCacheReservation(memory_used_delta, increase)` with
|
||||
// `UpdateCacheReservation(new_memory_used)` so that we only have one
|
||||
// `UpdateCacheReservation` function
|
||||
virtual Status UpdateCacheReservation(std::size_t memory_used_delta,
|
||||
bool increase) = 0;
|
||||
virtual Status MakeCacheReservation(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle>
|
||||
*handle) = 0;
|
||||
virtual std::size_t GetTotalReservedCacheSize() = 0;
|
||||
virtual std::size_t GetTotalMemoryUsed() = 0;
|
||||
};
|
||||
|
||||
// CacheReservationManagerImpl implements interface CacheReservationManager
|
||||
// for reserving cache space for the memory used by inserting/releasing dummy
|
||||
// entries in the cache.
|
||||
template <CacheEntryRole R>
|
||||
class CacheReservationHandle;
|
||||
|
||||
// CacheReservationManager is for reserving cache space for the memory used
|
||||
// through inserting/releasing dummy entries in the cache.
|
||||
//
|
||||
// This class is NOT thread-safe, except that GetTotalReservedCacheSize()
|
||||
// can be called without external synchronization.
|
||||
template <CacheEntryRole R>
|
||||
class CacheReservationManagerImpl
|
||||
: public CacheReservationManager,
|
||||
public std::enable_shared_from_this<CacheReservationManagerImpl<R>> {
|
||||
class CacheReservationManager
|
||||
: public std::enable_shared_from_this<CacheReservationManager> {
|
||||
public:
|
||||
class CacheReservationHandle
|
||||
: public CacheReservationManager::CacheReservationHandle {
|
||||
public:
|
||||
CacheReservationHandle(
|
||||
std::size_t incremental_memory_used,
|
||||
std::shared_ptr<CacheReservationManagerImpl> cache_res_mgr);
|
||||
~CacheReservationHandle() override;
|
||||
|
||||
private:
|
||||
std::size_t incremental_memory_used_;
|
||||
std::shared_ptr<CacheReservationManagerImpl> cache_res_mgr_;
|
||||
};
|
||||
|
||||
// Construct a CacheReservationManagerImpl
|
||||
// Construct a CacheReservationManager
|
||||
// @param cache The cache where dummy entries are inserted and released for
|
||||
// reserving cache space
|
||||
// @param delayed_decrease If set true, then dummy entries won't be released
|
||||
// immediately when memory usage decreases.
|
||||
// immediately when memory usage decreases.
|
||||
// Instead, it will be released when the memory usage
|
||||
// decreases to 3/4 of what we have reserved so far.
|
||||
// This is for saving some future dummy entry
|
||||
// insertion when memory usage increases are likely to
|
||||
// happen in the near future.
|
||||
//
|
||||
// REQUIRED: cache is not nullptr
|
||||
explicit CacheReservationManagerImpl(std::shared_ptr<Cache> cache,
|
||||
bool delayed_decrease = false);
|
||||
explicit CacheReservationManager(std::shared_ptr<Cache> cache,
|
||||
bool delayed_decrease = false);
|
||||
|
||||
// no copy constructor, copy assignment, move constructor, move assignment
|
||||
CacheReservationManagerImpl(const CacheReservationManagerImpl &) = delete;
|
||||
CacheReservationManagerImpl &operator=(const CacheReservationManagerImpl &) =
|
||||
delete;
|
||||
CacheReservationManagerImpl(CacheReservationManagerImpl &&) = delete;
|
||||
CacheReservationManagerImpl &operator=(CacheReservationManagerImpl &&) =
|
||||
delete;
|
||||
CacheReservationManager(const CacheReservationManager &) = delete;
|
||||
CacheReservationManager &operator=(const CacheReservationManager &) = delete;
|
||||
CacheReservationManager(CacheReservationManager &&) = delete;
|
||||
CacheReservationManager &operator=(CacheReservationManager &&) = delete;
|
||||
|
||||
~CacheReservationManagerImpl() override;
|
||||
~CacheReservationManager();
|
||||
|
||||
// One of the two ways of reserving/releasing cache space,
|
||||
// see MakeCacheReservation() for the other.
|
||||
//
|
||||
// Use ONLY one of these two ways to prevent unexpected behavior.
|
||||
template <CacheEntryRole R>
|
||||
|
||||
// One of the two ways of reserving/releasing cache,
|
||||
// see CacheReservationManager::MakeCacheReservation() for the other.
|
||||
// Use ONLY one of them to prevent unexpected behavior.
|
||||
//
|
||||
// Insert and release dummy entries in the cache to
|
||||
// match the size of total dummy entries with the least multiple of
|
||||
@@ -132,18 +90,11 @@ class CacheReservationManagerImpl
|
||||
// Otherwise, it returns the first non-ok status;
|
||||
// On releasing dummy entries, it always returns Status::OK().
|
||||
// On keeping dummy entries the same, it always returns Status::OK().
|
||||
Status UpdateCacheReservation(std::size_t new_memory_used) override;
|
||||
Status UpdateCacheReservation(std::size_t new_memory_used);
|
||||
|
||||
Status UpdateCacheReservation(std::size_t /* memory_used_delta */,
|
||||
bool /* increase */) override {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
// One of the two ways of reserving cache space and releasing is done through
|
||||
// destruction of CacheReservationHandle.
|
||||
// See UpdateCacheReservation() for the other way.
|
||||
//
|
||||
// Use ONLY one of these two ways to prevent unexpected behavior.
|
||||
// One of the two ways of reserving/releasing cache,
|
||||
// see CacheReservationManager::UpdateCacheReservation() for the other.
|
||||
// Use ONLY one of them to prevent unexpected behavior.
|
||||
//
|
||||
// Insert dummy entries in the cache for the incremental memory usage
|
||||
// to match the size of total dummy entries with the least multiple of
|
||||
@@ -167,19 +118,21 @@ class CacheReservationManagerImpl
|
||||
// calling MakeCacheReservation() is needed if you want
|
||||
// GetTotalMemoryUsed() indeed returns the latest memory used.
|
||||
//
|
||||
// @param handle An pointer to std::unique_ptr<CacheReservationHandle> that
|
||||
// manages the lifetime of the cache reservation represented by the
|
||||
// handle.
|
||||
// @param handle An pointer to std::unique_ptr<CacheReservationHandle<R>> that
|
||||
// manages the lifetime of the handle and its cache reservation.
|
||||
//
|
||||
// @return It returns Status::OK() if all dummy
|
||||
// entry insertions succeed.
|
||||
// Otherwise, it returns the first non-ok status;
|
||||
//
|
||||
// REQUIRES: handle != nullptr
|
||||
// REQUIRES: The CacheReservationManager object is NOT managed by
|
||||
// std::unique_ptr as CacheReservationHandle needs to
|
||||
// shares ownership to the CacheReservationManager object.
|
||||
template <CacheEntryRole R>
|
||||
Status MakeCacheReservation(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle> *handle)
|
||||
override;
|
||||
std::unique_ptr<CacheReservationHandle<R>> *handle);
|
||||
|
||||
// Return the size of the cache (which is a multiple of kSizeDummyEntry)
|
||||
// successfully reserved by calling UpdateCacheReservation().
|
||||
@@ -189,30 +142,29 @@ class CacheReservationManagerImpl
|
||||
// smaller number than the actual reserved cache size due to
|
||||
// the returned number will always be a multiple of kSizeDummyEntry
|
||||
// and cache full might happen in the middle of inserting a dummy entry.
|
||||
std::size_t GetTotalReservedCacheSize() override;
|
||||
std::size_t GetTotalReservedCacheSize();
|
||||
|
||||
// Return the latest total memory used indicated by the most recent call of
|
||||
// UpdateCacheReservation(std::size_t new_memory_used);
|
||||
std::size_t GetTotalMemoryUsed() override;
|
||||
std::size_t GetTotalMemoryUsed();
|
||||
|
||||
static constexpr std::size_t GetDummyEntrySize() { return kSizeDummyEntry; }
|
||||
|
||||
// For testing only - it is to help ensure the CacheItemHelperForRole<R>
|
||||
// accessed from CacheReservationManagerImpl and the one accessed from the
|
||||
// test are from the same translation units
|
||||
static const Cache::CacheItemHelper *TEST_GetCacheItemHelperForRole();
|
||||
// For testing only - it is to help ensure the NoopDeleterForRole<R>
|
||||
// accessed from CacheReservationManager and the one accessed from the test
|
||||
// are from the same translation units
|
||||
template <CacheEntryRole R>
|
||||
static Cache::DeleterFn TEST_GetNoopDeleterForRole();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t kSizeDummyEntry = 256 * 1024;
|
||||
|
||||
Slice GetNextCacheKey();
|
||||
|
||||
Status ReleaseCacheReservation(std::size_t incremental_memory_used);
|
||||
template <CacheEntryRole R>
|
||||
Status IncreaseCacheReservation(std::size_t new_mem_used);
|
||||
Status DecreaseCacheReservation(std::size_t new_mem_used);
|
||||
|
||||
using CacheInterface = PlaceholderSharedCacheInterface<R>;
|
||||
CacheInterface cache_;
|
||||
std::shared_ptr<Cache> cache_;
|
||||
bool delayed_decrease_;
|
||||
std::atomic<std::size_t> cache_allocated_size_;
|
||||
std::size_t memory_used_;
|
||||
@@ -220,99 +172,20 @@ class CacheReservationManagerImpl
|
||||
CacheKey cache_key_;
|
||||
};
|
||||
|
||||
class ConcurrentCacheReservationManager
|
||||
: public CacheReservationManager,
|
||||
public std::enable_shared_from_this<ConcurrentCacheReservationManager> {
|
||||
// CacheReservationHandle is for managing the lifetime of a cache reservation
|
||||
// This class is NOT thread-safe
|
||||
template <CacheEntryRole R>
|
||||
class CacheReservationHandle {
|
||||
public:
|
||||
class CacheReservationHandle
|
||||
: public CacheReservationManager::CacheReservationHandle {
|
||||
public:
|
||||
CacheReservationHandle(
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr,
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle>
|
||||
cache_res_handle) {
|
||||
assert(cache_res_mgr && cache_res_handle);
|
||||
cache_res_mgr_ = cache_res_mgr;
|
||||
cache_res_handle_ = std::move(cache_res_handle);
|
||||
}
|
||||
|
||||
~CacheReservationHandle() override {
|
||||
std::lock_guard<std::mutex> lock(cache_res_mgr_->cache_res_mgr_mu_);
|
||||
cache_res_handle_.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr_;
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle>
|
||||
cache_res_handle_;
|
||||
};
|
||||
|
||||
explicit ConcurrentCacheReservationManager(
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr) {
|
||||
cache_res_mgr_ = std::move(cache_res_mgr);
|
||||
}
|
||||
ConcurrentCacheReservationManager(const ConcurrentCacheReservationManager &) =
|
||||
delete;
|
||||
ConcurrentCacheReservationManager &operator=(
|
||||
const ConcurrentCacheReservationManager &) = delete;
|
||||
ConcurrentCacheReservationManager(ConcurrentCacheReservationManager &&) =
|
||||
delete;
|
||||
ConcurrentCacheReservationManager &operator=(
|
||||
ConcurrentCacheReservationManager &&) = delete;
|
||||
|
||||
~ConcurrentCacheReservationManager() override {}
|
||||
|
||||
inline Status UpdateCacheReservation(std::size_t new_memory_used) override {
|
||||
std::lock_guard<std::mutex> lock(cache_res_mgr_mu_);
|
||||
return cache_res_mgr_->UpdateCacheReservation(new_memory_used);
|
||||
}
|
||||
|
||||
inline Status UpdateCacheReservation(std::size_t memory_used_delta,
|
||||
bool increase) override {
|
||||
std::lock_guard<std::mutex> lock(cache_res_mgr_mu_);
|
||||
std::size_t total_mem_used = cache_res_mgr_->GetTotalMemoryUsed();
|
||||
Status s;
|
||||
if (!increase) {
|
||||
s = cache_res_mgr_->UpdateCacheReservation(
|
||||
(total_mem_used > memory_used_delta)
|
||||
? (total_mem_used - memory_used_delta)
|
||||
: 0);
|
||||
} else {
|
||||
s = cache_res_mgr_->UpdateCacheReservation(total_mem_used +
|
||||
memory_used_delta);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
inline Status MakeCacheReservation(
|
||||
// REQUIRES: cache_res_mgr != nullptr
|
||||
explicit CacheReservationHandle(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle> *handle)
|
||||
override {
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle>
|
||||
wrapped_handle;
|
||||
Status s;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cache_res_mgr_mu_);
|
||||
s = cache_res_mgr_->MakeCacheReservation(incremental_memory_used,
|
||||
&wrapped_handle);
|
||||
}
|
||||
(*handle).reset(
|
||||
new ConcurrentCacheReservationManager::CacheReservationHandle(
|
||||
std::enable_shared_from_this<
|
||||
ConcurrentCacheReservationManager>::shared_from_this(),
|
||||
std::move(wrapped_handle)));
|
||||
return s;
|
||||
}
|
||||
inline std::size_t GetTotalReservedCacheSize() override {
|
||||
return cache_res_mgr_->GetTotalReservedCacheSize();
|
||||
}
|
||||
inline std::size_t GetTotalMemoryUsed() override {
|
||||
std::lock_guard<std::mutex> lock(cache_res_mgr_mu_);
|
||||
return cache_res_mgr_->GetTotalMemoryUsed();
|
||||
}
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr);
|
||||
|
||||
~CacheReservationHandle();
|
||||
|
||||
private:
|
||||
std::mutex cache_res_mgr_mu_;
|
||||
std::size_t incremental_memory_used_;
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr_;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+89
-52
@@ -15,6 +15,7 @@
|
||||
#include "cache/cache_entry_roles.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
@@ -22,24 +23,25 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class CacheReservationManagerTest : public ::testing::Test {
|
||||
protected:
|
||||
static constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManagerImpl<CacheEntryRole::kMisc>::GetDummyEntrySize();
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
static constexpr std::size_t kCacheCapacity = 4096 * kSizeDummyEntry;
|
||||
static constexpr int kNumShardBits = 0; // 2^0 shard
|
||||
static constexpr std::size_t kMetaDataChargeOverhead = 10000;
|
||||
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(kCacheCapacity, kNumShardBits);
|
||||
std::shared_ptr<CacheReservationManager> test_cache_rev_mng;
|
||||
std::unique_ptr<CacheReservationManager> test_cache_rev_mng;
|
||||
|
||||
CacheReservationManagerTest() {
|
||||
test_cache_rev_mng =
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache);
|
||||
test_cache_rev_mng.reset(new CacheReservationManager(cache));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(CacheReservationManagerTest, GenerateCacheKey) {
|
||||
std::size_t new_mem_used = 1 * kSizeDummyEntry;
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_GE(cache->GetPinnedUsage(), 1 * kSizeDummyEntry);
|
||||
ASSERT_LT(cache->GetPinnedUsage(),
|
||||
@@ -47,14 +49,13 @@ TEST_F(CacheReservationManagerTest, GenerateCacheKey) {
|
||||
|
||||
// Next unique Cache key
|
||||
CacheKey ckey = CacheKey::CreateUniqueForCacheLifetime(cache.get());
|
||||
// Get to the underlying values
|
||||
uint64_t* ckey_data = reinterpret_cast<uint64_t*>(&ckey);
|
||||
// Back it up to the one used by CRM (using CacheKey implementation details)
|
||||
ckey_data[1]--;
|
||||
using PairU64 = std::pair<uint64_t, uint64_t>;
|
||||
auto& ckey_pair = *reinterpret_cast<PairU64*>(&ckey);
|
||||
ckey_pair.second--;
|
||||
|
||||
// Specific key (subject to implementation details)
|
||||
EXPECT_EQ(ckey_data[0], 0);
|
||||
EXPECT_EQ(ckey_data[1], 2);
|
||||
EXPECT_EQ(ckey_pair, PairU64(0, 2));
|
||||
|
||||
Cache::Handle* handle = cache->Lookup(ckey.AsSlice());
|
||||
EXPECT_NE(handle, nullptr)
|
||||
@@ -65,7 +66,10 @@ TEST_F(CacheReservationManagerTest, GenerateCacheKey) {
|
||||
|
||||
TEST_F(CacheReservationManagerTest, KeepCacheReservationTheSame) {
|
||||
std::size_t new_mem_used = 1 * kSizeDummyEntry;
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
1 * kSizeDummyEntry);
|
||||
@@ -75,7 +79,9 @@ TEST_F(CacheReservationManagerTest, KeepCacheReservationTheSame) {
|
||||
ASSERT_LT(initial_pinned_usage,
|
||||
1 * kSizeDummyEntry + kMetaDataChargeOverhead);
|
||||
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to keep cache reservation the same when new_mem_used equals "
|
||||
"to current cache reservation";
|
||||
@@ -94,7 +100,10 @@ TEST_F(CacheReservationManagerTest, KeepCacheReservationTheSame) {
|
||||
TEST_F(CacheReservationManagerTest,
|
||||
IncreaseCacheReservationByMultiplesOfDummyEntrySize) {
|
||||
std::size_t new_mem_used = 2 * kSizeDummyEntry;
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to increase cache reservation correctly";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
@@ -112,7 +121,10 @@ TEST_F(CacheReservationManagerTest,
|
||||
TEST_F(CacheReservationManagerTest,
|
||||
IncreaseCacheReservationNotByMultiplesOfDummyEntrySize) {
|
||||
std::size_t new_mem_used = 2 * kSizeDummyEntry + kSizeDummyEntry / 2;
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to increase cache reservation correctly";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
@@ -131,7 +143,7 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
IncreaseCacheReservationOnFullCache) {
|
||||
;
|
||||
constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManagerImpl<CacheEntryRole::kMisc>::GetDummyEntrySize();
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
constexpr std::size_t kSmallCacheCapacity = 4 * kSizeDummyEntry;
|
||||
constexpr std::size_t kBigCacheCapacity = 4096 * kSizeDummyEntry;
|
||||
constexpr std::size_t kMetaDataChargeOverhead = 10000;
|
||||
@@ -141,13 +153,15 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
lo.num_shard_bits = 0; // 2^0 shard
|
||||
lo.strict_capacity_limit = true;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lo);
|
||||
std::shared_ptr<CacheReservationManager> test_cache_rev_mng =
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache);
|
||||
std::unique_ptr<CacheReservationManager> test_cache_rev_mng(
|
||||
new CacheReservationManager(cache));
|
||||
|
||||
std::size_t new_mem_used = kSmallCacheCapacity + 1;
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::MemoryLimit())
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
EXPECT_EQ(s, Status::Incomplete())
|
||||
<< "Failed to return status to indicate failure of dummy entry insertion "
|
||||
"during cache reservation on full cache";
|
||||
EXPECT_GE(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
@@ -169,7 +183,9 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
"encountering cache resevation failure due to full cache";
|
||||
|
||||
new_mem_used = kSmallCacheCapacity / 2; // 2 dummy entries
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to decrease cache reservation after encountering cache "
|
||||
"reservation failure due to full cache";
|
||||
@@ -191,8 +207,10 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
|
||||
// Create cache full again for subsequent tests
|
||||
new_mem_used = kSmallCacheCapacity + 1;
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
EXPECT_EQ(s, Status::MemoryLimit())
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
EXPECT_EQ(s, Status::Incomplete())
|
||||
<< "Failed to return status to indicate failure of dummy entry insertion "
|
||||
"during cache reservation on full cache";
|
||||
EXPECT_GE(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
@@ -217,7 +235,9 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
// succeed
|
||||
cache->SetCapacity(kBigCacheCapacity);
|
||||
new_mem_used = kSmallCacheCapacity + 1;
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to increase cache reservation after increasing cache capacity "
|
||||
"and mitigating cache full error";
|
||||
@@ -239,7 +259,10 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
TEST_F(CacheReservationManagerTest,
|
||||
DecreaseCacheReservationByMultiplesOfDummyEntrySize) {
|
||||
std::size_t new_mem_used = 2 * kSizeDummyEntry;
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
2 * kSizeDummyEntry);
|
||||
@@ -249,7 +272,9 @@ TEST_F(CacheReservationManagerTest,
|
||||
2 * kSizeDummyEntry + kMetaDataChargeOverhead);
|
||||
|
||||
new_mem_used = 1 * kSizeDummyEntry;
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to decrease cache reservation correctly";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
@@ -267,7 +292,10 @@ TEST_F(CacheReservationManagerTest,
|
||||
TEST_F(CacheReservationManagerTest,
|
||||
DecreaseCacheReservationNotByMultiplesOfDummyEntrySize) {
|
||||
std::size_t new_mem_used = 2 * kSizeDummyEntry;
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
2 * kSizeDummyEntry);
|
||||
@@ -277,7 +305,9 @@ TEST_F(CacheReservationManagerTest,
|
||||
2 * kSizeDummyEntry + kMetaDataChargeOverhead);
|
||||
|
||||
new_mem_used = kSizeDummyEntry / 2;
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to decrease cache reservation correctly";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
@@ -295,7 +325,7 @@ TEST_F(CacheReservationManagerTest,
|
||||
TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
DecreaseCacheReservationWithDelayedDecrease) {
|
||||
constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManagerImpl<CacheEntryRole::kMisc>::GetDummyEntrySize();
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
constexpr std::size_t kCacheCapacity = 4096 * kSizeDummyEntry;
|
||||
constexpr std::size_t kMetaDataChargeOverhead = 10000;
|
||||
|
||||
@@ -303,12 +333,14 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
lo.capacity = kCacheCapacity;
|
||||
lo.num_shard_bits = 0;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lo);
|
||||
std::shared_ptr<CacheReservationManager> test_cache_rev_mng =
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache, true /* delayed_decrease */);
|
||||
std::unique_ptr<CacheReservationManager> test_cache_rev_mng(
|
||||
new CacheReservationManager(cache, true /* delayed_decrease */));
|
||||
|
||||
std::size_t new_mem_used = 8 * kSizeDummyEntry;
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
8 * kSizeDummyEntry);
|
||||
@@ -319,7 +351,9 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
8 * kSizeDummyEntry + kMetaDataChargeOverhead);
|
||||
|
||||
new_mem_used = 6 * kSizeDummyEntry;
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK()) << "Failed to delay decreasing cache reservation";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
8 * kSizeDummyEntry)
|
||||
@@ -331,7 +365,9 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
<< "Failed to delay decreasing underlying dummy entries in cache";
|
||||
|
||||
new_mem_used = 7 * kSizeDummyEntry;
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK()) << "Failed to delay decreasing cache reservation";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
8 * kSizeDummyEntry)
|
||||
@@ -343,7 +379,9 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
<< "Failed to delay decreasing underlying dummy entries in cache";
|
||||
|
||||
new_mem_used = 6 * kSizeDummyEntry - 1;
|
||||
s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
EXPECT_EQ(s, Status::OK())
|
||||
<< "Failed to decrease cache reservation correctly when new_mem_used < "
|
||||
"GetTotalReservedCacheSize() * 3 / 4 on delayed decrease mode";
|
||||
@@ -367,7 +405,7 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
TEST(CacheReservationManagerDestructorTest,
|
||||
ReleaseRemainingDummyEntriesOnDestruction) {
|
||||
constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManagerImpl<CacheEntryRole::kMisc>::GetDummyEntrySize();
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
constexpr std::size_t kCacheCapacity = 4096 * kSizeDummyEntry;
|
||||
constexpr std::size_t kMetaDataChargeOverhead = 10000;
|
||||
|
||||
@@ -376,11 +414,13 @@ TEST(CacheReservationManagerDestructorTest,
|
||||
lo.num_shard_bits = 0;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lo);
|
||||
{
|
||||
std::shared_ptr<CacheReservationManager> test_cache_rev_mng =
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache);
|
||||
std::unique_ptr<CacheReservationManager> test_cache_rev_mng(
|
||||
new CacheReservationManager(cache));
|
||||
std::size_t new_mem_used = 1 * kSizeDummyEntry;
|
||||
Status s = test_cache_rev_mng->UpdateCacheReservation(new_mem_used);
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_GE(cache->GetPinnedUsage(), 1 * kSizeDummyEntry);
|
||||
ASSERT_LT(cache->GetPinnedUsage(),
|
||||
@@ -402,19 +442,18 @@ TEST(CacheReservationHandleTest, HandleTest) {
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lo);
|
||||
|
||||
std::shared_ptr<CacheReservationManager> test_cache_rev_mng(
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache));
|
||||
std::make_shared<CacheReservationManager>(cache));
|
||||
|
||||
std::size_t mem_used = 0;
|
||||
const std::size_t incremental_mem_used_handle_1 = 1 * kSizeDummyEntry;
|
||||
const std::size_t incremental_mem_used_handle_2 = 2 * kSizeDummyEntry;
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle> handle_1,
|
||||
std::unique_ptr<CacheReservationHandle<CacheEntryRole::kMisc>> handle_1,
|
||||
handle_2;
|
||||
|
||||
// To test consecutive CacheReservationManager::MakeCacheReservation works
|
||||
// correctly in terms of returning the handle as well as updating cache
|
||||
// reservation and the latest total memory used
|
||||
Status s = test_cache_rev_mng->MakeCacheReservation(
|
||||
Status s = test_cache_rev_mng->MakeCacheReservation<CacheEntryRole::kMisc>(
|
||||
incremental_mem_used_handle_1, &handle_1);
|
||||
mem_used = mem_used + incremental_mem_used_handle_1;
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
@@ -424,8 +463,8 @@ TEST(CacheReservationHandleTest, HandleTest) {
|
||||
EXPECT_GE(cache->GetPinnedUsage(), mem_used);
|
||||
EXPECT_LT(cache->GetPinnedUsage(), mem_used + kMetaDataChargeOverhead);
|
||||
|
||||
s = test_cache_rev_mng->MakeCacheReservation(incremental_mem_used_handle_2,
|
||||
&handle_2);
|
||||
s = test_cache_rev_mng->MakeCacheReservation<CacheEntryRole::kMisc>(
|
||||
incremental_mem_used_handle_2, &handle_2);
|
||||
mem_used = mem_used + incremental_mem_used_handle_2;
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
EXPECT_TRUE(handle_2 != nullptr);
|
||||
@@ -434,9 +473,8 @@ TEST(CacheReservationHandleTest, HandleTest) {
|
||||
EXPECT_GE(cache->GetPinnedUsage(), mem_used);
|
||||
EXPECT_LT(cache->GetPinnedUsage(), mem_used + kMetaDataChargeOverhead);
|
||||
|
||||
// To test
|
||||
// CacheReservationManager::CacheReservationHandle::~CacheReservationHandle()
|
||||
// works correctly in releasing the cache reserved for the handle
|
||||
// To test CacheReservationHandle::~CacheReservationHandle() works correctly
|
||||
// in releasing the cache reserved for the handle
|
||||
handle_1.reset();
|
||||
EXPECT_TRUE(handle_1 == nullptr);
|
||||
mem_used = mem_used - incremental_mem_used_handle_1;
|
||||
@@ -463,7 +501,6 @@ TEST(CacheReservationHandleTest, HandleTest) {
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
Vendored
+289
-461
File diff suppressed because it is too large
Load Diff
Vendored
-111
@@ -1,111 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "cache/charged_cache.h"
|
||||
|
||||
#include "cache/cache_reservation_manager.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
ChargedCache::ChargedCache(std::shared_ptr<Cache> cache,
|
||||
std::shared_ptr<Cache> block_cache)
|
||||
: CacheWrapper(cache),
|
||||
cache_res_mgr_(std::make_shared<ConcurrentCacheReservationManager>(
|
||||
std::make_shared<
|
||||
CacheReservationManagerImpl<CacheEntryRole::kBlobCache>>(
|
||||
block_cache))) {}
|
||||
|
||||
Status ChargedCache::Insert(const Slice& key, ObjectPtr obj,
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
Handle** handle, Priority priority,
|
||||
const Slice& compressed_val, CompressionType type) {
|
||||
Status s = target_->Insert(key, obj, helper, charge, handle, priority,
|
||||
compressed_val, type);
|
||||
if (s.ok()) {
|
||||
// Insert may cause the cache entry eviction if the cache is full. So we
|
||||
// directly call the reservation manager to update the total memory used
|
||||
// in the cache.
|
||||
assert(cache_res_mgr_);
|
||||
cache_res_mgr_->UpdateCacheReservation(target_->GetUsage())
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Cache::Handle* ChargedCache::Lookup(const Slice& key,
|
||||
const CacheItemHelper* helper,
|
||||
CreateContext* create_context,
|
||||
Priority priority, Statistics* stats) {
|
||||
auto handle = target_->Lookup(key, helper, create_context, priority, stats);
|
||||
// Lookup may promote the KV pair from the secondary cache to the primary
|
||||
// cache. So we directly call the reservation manager to update the total
|
||||
// memory used in the cache.
|
||||
if (helper && helper->create_cb) {
|
||||
assert(cache_res_mgr_);
|
||||
cache_res_mgr_->UpdateCacheReservation(target_->GetUsage())
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
void ChargedCache::WaitAll(AsyncLookupHandle* async_handles, size_t count) {
|
||||
target_->WaitAll(async_handles, count);
|
||||
// In case of any promotions. Although some could finish by return of
|
||||
// StartAsyncLookup, Wait/WaitAll will generally be used, so simpler to
|
||||
// update here.
|
||||
assert(cache_res_mgr_);
|
||||
cache_res_mgr_->UpdateCacheReservation(target_->GetUsage())
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
|
||||
bool ChargedCache::Release(Cache::Handle* handle, bool useful,
|
||||
bool erase_if_last_ref) {
|
||||
size_t memory_used_delta = target_->GetUsage(handle);
|
||||
bool erased = target_->Release(handle, useful, erase_if_last_ref);
|
||||
if (erased) {
|
||||
assert(cache_res_mgr_);
|
||||
cache_res_mgr_
|
||||
->UpdateCacheReservation(memory_used_delta, /* increase */ false)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
return erased;
|
||||
}
|
||||
|
||||
bool ChargedCache::Release(Cache::Handle* handle, bool erase_if_last_ref) {
|
||||
size_t memory_used_delta = target_->GetUsage(handle);
|
||||
bool erased = target_->Release(handle, erase_if_last_ref);
|
||||
if (erased) {
|
||||
assert(cache_res_mgr_);
|
||||
cache_res_mgr_
|
||||
->UpdateCacheReservation(memory_used_delta, /* increase */ false)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
return erased;
|
||||
}
|
||||
|
||||
void ChargedCache::Erase(const Slice& key) {
|
||||
target_->Erase(key);
|
||||
assert(cache_res_mgr_);
|
||||
cache_res_mgr_->UpdateCacheReservation(target_->GetUsage())
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
|
||||
void ChargedCache::EraseUnRefEntries() {
|
||||
target_->EraseUnRefEntries();
|
||||
assert(cache_res_mgr_);
|
||||
cache_res_mgr_->UpdateCacheReservation(target_->GetUsage())
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
|
||||
void ChargedCache::SetCapacity(size_t capacity) {
|
||||
target_->SetCapacity(capacity);
|
||||
// SetCapacity can result in evictions when the cache capacity is decreased,
|
||||
// so we would want to update the cache reservation here as well.
|
||||
assert(cache_res_mgr_);
|
||||
cache_res_mgr_->UpdateCacheReservation(target_->GetUsage())
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
-61
@@ -1,61 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class ConcurrentCacheReservationManager;
|
||||
|
||||
// A cache interface which wraps around another cache and takes care of
|
||||
// reserving space in block cache towards a single global memory limit, and
|
||||
// forwards all the calls to the underlying cache.
|
||||
class ChargedCache : public CacheWrapper {
|
||||
public:
|
||||
ChargedCache(std::shared_ptr<Cache> cache,
|
||||
std::shared_ptr<Cache> block_cache);
|
||||
|
||||
Status Insert(
|
||||
const Slice& key, ObjectPtr obj, const CacheItemHelper* helper,
|
||||
size_t charge, Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW, const Slice& compressed_val = Slice(),
|
||||
CompressionType type = CompressionType::kNoCompression) override;
|
||||
|
||||
Cache::Handle* Lookup(const Slice& key, const CacheItemHelper* helper,
|
||||
CreateContext* create_context,
|
||||
Priority priority = Priority::LOW,
|
||||
Statistics* stats = nullptr) override;
|
||||
|
||||
void WaitAll(AsyncLookupHandle* async_handles, size_t count) override;
|
||||
|
||||
bool Release(Cache::Handle* handle, bool useful,
|
||||
bool erase_if_last_ref = false) override;
|
||||
bool Release(Cache::Handle* handle, bool erase_if_last_ref = false) override;
|
||||
|
||||
void Erase(const Slice& key) override;
|
||||
void EraseUnRefEntries() override;
|
||||
|
||||
static const char* kClassName() { return "ChargedCache"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
void SetCapacity(size_t capacity) override;
|
||||
|
||||
inline Cache* GetCache() const { return target_.get(); }
|
||||
|
||||
inline ConcurrentCacheReservationManager* TEST_GetCacheReservationManager()
|
||||
const {
|
||||
return cache_res_mgr_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+738
-3556
File diff suppressed because it is too large
Load Diff
Vendored
+2
-1151
File diff suppressed because it is too large
Load Diff
Vendored
-414
@@ -1,414 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "cache/compressed_secondary_cache.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "memory/memory_allocator_impl.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
CompressedSecondaryCache::CompressedSecondaryCache(
|
||||
const CompressedSecondaryCacheOptions& opts)
|
||||
: cache_(opts.LRUCacheOptions::MakeSharedCache()),
|
||||
cache_options_(opts),
|
||||
cache_res_mgr_(std::make_shared<ConcurrentCacheReservationManager>(
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache_))),
|
||||
disable_cache_(opts.capacity == 0) {}
|
||||
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() {}
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool /*wait*/, bool advise_erase,
|
||||
Statistics* stats, bool& kept_in_sec_cache) {
|
||||
assert(helper);
|
||||
// This is a minor optimization. Its ok to skip it in TSAN in order to
|
||||
// avoid a false positive.
|
||||
#ifndef __SANITIZE_THREAD__
|
||||
if (disable_cache_) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle;
|
||||
kept_in_sec_cache = false;
|
||||
Cache::Handle* lru_handle = cache_->Lookup(key);
|
||||
if (lru_handle == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* handle_value = cache_->Value(lru_handle);
|
||||
if (handle_value == nullptr) {
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
|
||||
RecordTick(stats, COMPRESSED_SECONDARY_CACHE_DUMMY_HITS);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CacheAllocationPtr* ptr{nullptr};
|
||||
CacheAllocationPtr merged_value;
|
||||
size_t handle_value_charge{0};
|
||||
const char* data_ptr = nullptr;
|
||||
CacheTier source = CacheTier::kVolatileCompressedTier;
|
||||
CompressionType type = cache_options_.compression_type;
|
||||
if (cache_options_.enable_custom_split_merge) {
|
||||
CacheValueChunk* value_chunk_ptr =
|
||||
reinterpret_cast<CacheValueChunk*>(handle_value);
|
||||
merged_value = MergeChunksIntoValue(value_chunk_ptr, handle_value_charge);
|
||||
ptr = &merged_value;
|
||||
data_ptr = ptr->get();
|
||||
} else {
|
||||
uint32_t type_32 = static_cast<uint32_t>(type);
|
||||
uint32_t source_32 = static_cast<uint32_t>(source);
|
||||
ptr = reinterpret_cast<CacheAllocationPtr*>(handle_value);
|
||||
handle_value_charge = cache_->GetCharge(lru_handle);
|
||||
data_ptr = ptr->get();
|
||||
data_ptr = GetVarint32Ptr(data_ptr, data_ptr + 1,
|
||||
static_cast<uint32_t*>(&type_32));
|
||||
type = static_cast<CompressionType>(type_32);
|
||||
data_ptr = GetVarint32Ptr(data_ptr, data_ptr + 1,
|
||||
static_cast<uint32_t*>(&source_32));
|
||||
source = static_cast<CacheTier>(source_32);
|
||||
handle_value_charge -= (data_ptr - ptr->get());
|
||||
}
|
||||
MemoryAllocator* allocator = cache_options_.memory_allocator.get();
|
||||
|
||||
Status s;
|
||||
Cache::ObjectPtr value{nullptr};
|
||||
size_t charge{0};
|
||||
if (source == CacheTier::kVolatileCompressedTier) {
|
||||
if (cache_options_.compression_type == kNoCompression ||
|
||||
cache_options_.do_not_compress_roles.Contains(helper->role)) {
|
||||
s = helper->create_cb(Slice(data_ptr, handle_value_charge),
|
||||
kNoCompression, CacheTier::kVolatileTier,
|
||||
create_context, allocator, &value, &charge);
|
||||
} else {
|
||||
UncompressionContext uncompression_context(
|
||||
cache_options_.compression_type);
|
||||
UncompressionInfo uncompression_info(uncompression_context,
|
||||
UncompressionDict::GetEmptyDict(),
|
||||
cache_options_.compression_type);
|
||||
|
||||
size_t uncompressed_size{0};
|
||||
CacheAllocationPtr uncompressed =
|
||||
UncompressData(uncompression_info, (char*)data_ptr,
|
||||
handle_value_charge, &uncompressed_size,
|
||||
cache_options_.compress_format_version, allocator);
|
||||
|
||||
if (!uncompressed) {
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/true);
|
||||
return nullptr;
|
||||
}
|
||||
s = helper->create_cb(Slice(uncompressed.get(), uncompressed_size),
|
||||
kNoCompression, CacheTier::kVolatileTier,
|
||||
create_context, allocator, &value, &charge);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/true);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (advise_erase) {
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/true);
|
||||
// Insert a dummy handle.
|
||||
cache_
|
||||
->Insert(key, /*obj=*/nullptr,
|
||||
GetHelper(cache_options_.enable_custom_split_merge),
|
||||
/*charge=*/0)
|
||||
.PermitUncheckedError();
|
||||
} else {
|
||||
kept_in_sec_cache = true;
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
|
||||
}
|
||||
handle.reset(new CompressedSecondaryCacheResultHandle(value, charge));
|
||||
RecordTick(stats, COMPRESSED_SECONDARY_CACHE_HITS);
|
||||
return handle;
|
||||
}
|
||||
|
||||
bool CompressedSecondaryCache::MaybeInsertDummy(const Slice& key) {
|
||||
auto internal_helper = GetHelper(cache_options_.enable_custom_split_merge);
|
||||
Cache::Handle* lru_handle = cache_->Lookup(key);
|
||||
if (lru_handle == nullptr) {
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_insert_dummy_count, 1);
|
||||
// Insert a dummy handle if the handle is evicted for the first time.
|
||||
cache_->Insert(key, /*obj=*/nullptr, internal_helper, /*charge=*/0)
|
||||
.PermitUncheckedError();
|
||||
return true;
|
||||
} else {
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Status CompressedSecondaryCache::InsertInternal(
|
||||
const Slice& key, Cache::ObjectPtr value,
|
||||
const Cache::CacheItemHelper* helper, CompressionType type,
|
||||
CacheTier source) {
|
||||
if (source != CacheTier::kVolatileCompressedTier &&
|
||||
cache_options_.enable_custom_split_merge) {
|
||||
// We don't support custom split/merge for the tiered case
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
auto internal_helper = GetHelper(cache_options_.enable_custom_split_merge);
|
||||
char header[10];
|
||||
char* payload = header;
|
||||
payload = EncodeVarint32(payload, static_cast<uint32_t>(type));
|
||||
payload = EncodeVarint32(payload, static_cast<uint32_t>(source));
|
||||
|
||||
size_t header_size = payload - header;
|
||||
size_t data_size = (*helper->size_cb)(value);
|
||||
size_t total_size = data_size + header_size;
|
||||
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, data_ptr);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
Slice val(data_ptr, data_size);
|
||||
|
||||
std::string compressed_val;
|
||||
if (cache_options_.compression_type != kNoCompression &&
|
||||
type == kNoCompression &&
|
||||
!cache_options_.do_not_compress_roles.Contains(helper->role)) {
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_uncompressed_bytes, data_size);
|
||||
CompressionContext compression_context(cache_options_.compression_type,
|
||||
cache_options_.compression_opts);
|
||||
uint64_t sample_for_compression{0};
|
||||
CompressionInfo compression_info(
|
||||
cache_options_.compression_opts, compression_context,
|
||||
CompressionDict::GetEmptyDict(), cache_options_.compression_type,
|
||||
sample_for_compression);
|
||||
|
||||
bool success =
|
||||
CompressData(val, compression_info,
|
||||
cache_options_.compress_format_version, &compressed_val);
|
||||
|
||||
if (!success) {
|
||||
return Status::Corruption("Error compressing value.");
|
||||
}
|
||||
|
||||
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);
|
||||
if (cache_options_.enable_custom_split_merge) {
|
||||
size_t charge{0};
|
||||
CacheValueChunk* value_chunks_head =
|
||||
SplitValueIntoChunks(val, cache_options_.compression_type, charge);
|
||||
return cache_->Insert(key, value_chunks_head, internal_helper, charge);
|
||||
} else {
|
||||
std::memcpy(ptr.get(), header, header_size);
|
||||
CacheAllocationPtr* buf = new CacheAllocationPtr(std::move(ptr));
|
||||
return cache_->Insert(key, buf, internal_helper, total_size);
|
||||
}
|
||||
}
|
||||
|
||||
Status CompressedSecondaryCache::Insert(const Slice& key,
|
||||
Cache::ObjectPtr value,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
bool force_insert) {
|
||||
if (value == nullptr) {
|
||||
return Status::InvalidArgument();
|
||||
}
|
||||
|
||||
if (!force_insert && MaybeInsertDummy(key)) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
return InsertInternal(key, value, helper, kNoCompression,
|
||||
CacheTier::kVolatileCompressedTier);
|
||||
}
|
||||
|
||||
Status CompressedSecondaryCache::InsertSaved(
|
||||
const Slice& key, const Slice& saved, CompressionType type = kNoCompression,
|
||||
CacheTier source = CacheTier::kVolatileTier) {
|
||||
if (type == kNoCompression) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
auto slice_helper = &kSliceCacheItemHelper;
|
||||
if (MaybeInsertDummy(key)) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
return InsertInternal(
|
||||
key, static_cast<Cache::ObjectPtr>(const_cast<Slice*>(&saved)),
|
||||
slice_helper, type, source);
|
||||
}
|
||||
|
||||
void CompressedSecondaryCache::Erase(const Slice& key) { cache_->Erase(key); }
|
||||
|
||||
Status CompressedSecondaryCache::SetCapacity(size_t capacity) {
|
||||
MutexLock l(&capacity_mutex_);
|
||||
cache_options_.capacity = capacity;
|
||||
cache_->SetCapacity(capacity);
|
||||
disable_cache_ = capacity == 0;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CompressedSecondaryCache::GetCapacity(size_t& capacity) {
|
||||
MutexLock l(&capacity_mutex_);
|
||||
capacity = cache_options_.capacity;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::string CompressedSecondaryCache::GetPrintableOptions() const {
|
||||
std::string ret;
|
||||
ret.reserve(20000);
|
||||
const int kBufferSize{200};
|
||||
char buffer[kBufferSize];
|
||||
ret.append(cache_->GetPrintableOptions());
|
||||
snprintf(buffer, kBufferSize, " compression_type : %s\n",
|
||||
CompressionTypeToString(cache_options_.compression_type).c_str());
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " compression_opts : %s\n",
|
||||
CompressionOptionsToString(
|
||||
const_cast<CompressionOptions&>(cache_options_.compression_opts))
|
||||
.c_str());
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " compress_format_version : %d\n",
|
||||
cache_options_.compress_format_version);
|
||||
ret.append(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CompressedSecondaryCache::CacheValueChunk*
|
||||
CompressedSecondaryCache::SplitValueIntoChunks(const Slice& value,
|
||||
CompressionType compression_type,
|
||||
size_t& charge) {
|
||||
assert(!value.empty());
|
||||
const char* src_ptr = value.data();
|
||||
size_t src_size{value.size()};
|
||||
|
||||
CacheValueChunk dummy_head = CacheValueChunk();
|
||||
CacheValueChunk* current_chunk = &dummy_head;
|
||||
// Do not split when value size is large or there is no compression.
|
||||
size_t predicted_chunk_size{0};
|
||||
size_t actual_chunk_size{0};
|
||||
size_t tmp_size{0};
|
||||
while (src_size > 0) {
|
||||
predicted_chunk_size = sizeof(CacheValueChunk) - 1 + src_size;
|
||||
auto upper =
|
||||
std::upper_bound(malloc_bin_sizes_.begin(), malloc_bin_sizes_.end(),
|
||||
predicted_chunk_size);
|
||||
// Do not split when value size is too small, too large, close to a bin
|
||||
// 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() ||
|
||||
compression_type == kNoCompression) {
|
||||
tmp_size = predicted_chunk_size;
|
||||
} else {
|
||||
tmp_size = *(--upper);
|
||||
}
|
||||
|
||||
CacheValueChunk* new_chunk =
|
||||
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;
|
||||
memcpy(current_chunk->data, src_ptr, actual_chunk_size);
|
||||
current_chunk->size = actual_chunk_size;
|
||||
src_ptr += actual_chunk_size;
|
||||
src_size -= actual_chunk_size;
|
||||
charge += tmp_size;
|
||||
}
|
||||
current_chunk->next = nullptr;
|
||||
|
||||
return dummy_head.next;
|
||||
}
|
||||
|
||||
CacheAllocationPtr CompressedSecondaryCache::MergeChunksIntoValue(
|
||||
const void* chunks_head, size_t& charge) {
|
||||
const CacheValueChunk* head =
|
||||
reinterpret_cast<const CacheValueChunk*>(chunks_head);
|
||||
const CacheValueChunk* current_chunk = head;
|
||||
charge = 0;
|
||||
while (current_chunk != nullptr) {
|
||||
charge += current_chunk->size;
|
||||
current_chunk = current_chunk->next;
|
||||
}
|
||||
|
||||
CacheAllocationPtr ptr =
|
||||
AllocateBlock(charge, cache_options_.memory_allocator.get());
|
||||
current_chunk = head;
|
||||
size_t pos{0};
|
||||
while (current_chunk != nullptr) {
|
||||
memcpy(ptr.get() + pos, current_chunk->data, current_chunk->size);
|
||||
pos += current_chunk->size;
|
||||
current_chunk = current_chunk->next;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
const Cache::CacheItemHelper* CompressedSecondaryCache::GetHelper(
|
||||
bool enable_custom_split_merge) const {
|
||||
if (enable_custom_split_merge) {
|
||||
static const Cache::CacheItemHelper kHelper{
|
||||
CacheEntryRole::kMisc,
|
||||
[](Cache::ObjectPtr obj, MemoryAllocator* /*alloc*/) {
|
||||
CacheValueChunk* chunks_head = static_cast<CacheValueChunk*>(obj);
|
||||
while (chunks_head != nullptr) {
|
||||
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*/) {
|
||||
delete static_cast<CacheAllocationPtr*>(obj);
|
||||
obj = nullptr;
|
||||
}};
|
||||
return &kHelper;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache>
|
||||
CompressedSecondaryCacheOptions::MakeSharedSecondaryCache() const {
|
||||
return std::make_shared<CompressedSecondaryCache>(*this);
|
||||
}
|
||||
|
||||
Status CompressedSecondaryCache::Deflate(size_t decrease) {
|
||||
return cache_res_mgr_->UpdateCacheReservation(decrease, /*increase=*/true);
|
||||
}
|
||||
|
||||
Status CompressedSecondaryCache::Inflate(size_t increase) {
|
||||
return cache_res_mgr_->UpdateCacheReservation(increase, /*increase=*/false);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
-151
@@ -1,151 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include "cache/cache_reservation_manager.h"
|
||||
#include "cache/lru_cache.h"
|
||||
#include "memory/memory_allocator_impl.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class CompressedSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
|
||||
public:
|
||||
CompressedSecondaryCacheResultHandle(Cache::ObjectPtr value, size_t size)
|
||||
: value_(value), size_(size) {}
|
||||
~CompressedSecondaryCacheResultHandle() override = default;
|
||||
|
||||
CompressedSecondaryCacheResultHandle(
|
||||
const CompressedSecondaryCacheResultHandle&) = delete;
|
||||
CompressedSecondaryCacheResultHandle& operator=(
|
||||
const CompressedSecondaryCacheResultHandle&) = delete;
|
||||
|
||||
bool IsReady() override { return true; }
|
||||
|
||||
void Wait() override {}
|
||||
|
||||
Cache::ObjectPtr Value() override { return value_; }
|
||||
|
||||
size_t Size() override { return size_; }
|
||||
|
||||
private:
|
||||
Cache::ObjectPtr value_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
// The CompressedSecondaryCache is a concrete implementation of
|
||||
// rocksdb::SecondaryCache.
|
||||
//
|
||||
// When a block is found from CompressedSecondaryCache::Lookup, we check whether
|
||||
// there is a dummy block with the same key in the primary cache.
|
||||
// 1. If the dummy block exits, we erase the block from
|
||||
// CompressedSecondaryCache and insert it into the primary cache.
|
||||
// 2. If not, we just insert a dummy block into the primary cache
|
||||
// (charging the actual size of the block) and don not erase the block from
|
||||
// CompressedSecondaryCache. A standalone handle is returned to the caller.
|
||||
//
|
||||
// When a block is evicted from the primary cache, we check whether
|
||||
// there is a dummy block with the same key in CompressedSecondaryCache.
|
||||
// 1. If the dummy block exits, the block is inserted into
|
||||
// CompressedSecondaryCache.
|
||||
// 2. If not, we just insert a dummy block (size 0) in CompressedSecondaryCache.
|
||||
//
|
||||
// Users can also cast a pointer to CompressedSecondaryCache and call methods on
|
||||
// it directly, especially custom methods that may be added
|
||||
// in the future. For example -
|
||||
// std::unique_ptr<rocksdb::SecondaryCache> cache =
|
||||
// NewCompressedSecondaryCache(opts);
|
||||
// static_cast<CompressedSecondaryCache*>(cache.get())->Erase(key);
|
||||
|
||||
class CompressedSecondaryCache : public SecondaryCache {
|
||||
public:
|
||||
explicit CompressedSecondaryCache(
|
||||
const CompressedSecondaryCacheOptions& opts);
|
||||
~CompressedSecondaryCache() override;
|
||||
|
||||
const char* Name() const override { return "CompressedSecondaryCache"; }
|
||||
|
||||
Status Insert(const Slice& key, Cache::ObjectPtr value,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
bool force_insert) override;
|
||||
|
||||
Status InsertSaved(const Slice& key, const Slice& saved, CompressionType type,
|
||||
CacheTier source) override;
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool /*wait*/, bool advise_erase,
|
||||
Statistics* stats, bool& kept_in_sec_cache) override;
|
||||
|
||||
bool SupportForceErase() const override { return true; }
|
||||
|
||||
void Erase(const Slice& key) override;
|
||||
|
||||
void WaitAll(std::vector<SecondaryCacheResultHandle*> /*handles*/) override {}
|
||||
|
||||
Status SetCapacity(size_t capacity) override;
|
||||
|
||||
Status GetCapacity(size_t& capacity) override;
|
||||
|
||||
Status Deflate(size_t decrease) override;
|
||||
|
||||
Status Inflate(size_t increase) override;
|
||||
|
||||
std::string GetPrintableOptions() const override;
|
||||
|
||||
size_t TEST_GetUsage() { return cache_->GetUsage(); }
|
||||
|
||||
private:
|
||||
friend class CompressedSecondaryCacheTestBase;
|
||||
static constexpr std::array<uint16_t, 8> malloc_bin_sizes_{
|
||||
128, 256, 512, 1024, 2048, 4096, 8192, 16384};
|
||||
|
||||
struct CacheValueChunk {
|
||||
// TODO try "CacheAllocationPtr next;".
|
||||
CacheValueChunk* next;
|
||||
size_t size;
|
||||
// Beginning of the chunk data (MUST BE THE LAST FIELD IN THIS STRUCT!)
|
||||
char data[1];
|
||||
|
||||
void Free() { delete[] reinterpret_cast<char*>(this); }
|
||||
};
|
||||
|
||||
// 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,
|
||||
CompressionType compression_type,
|
||||
size_t& charge);
|
||||
|
||||
// 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);
|
||||
|
||||
Status InsertInternal(const Slice& key, Cache::ObjectPtr value,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
CompressionType type, CacheTier source);
|
||||
|
||||
// TODO: clean up to use cleaner interfaces in typed_cache.h
|
||||
const Cache::CacheItemHelper* GetHelper(bool enable_custom_split_merge) const;
|
||||
std::shared_ptr<Cache> cache_;
|
||||
CompressedSecondaryCacheOptions cache_options_;
|
||||
mutable port::Mutex capacity_mutex_;
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr_;
|
||||
bool disable_cache_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
-1379
File diff suppressed because it is too large
Load Diff
Vendored
+419
-343
File diff suppressed because it is too large
Load Diff
Vendored
+226
-212
@@ -13,14 +13,12 @@
|
||||
|
||||
#include "cache/sharded_cache.h"
|
||||
#include "port/lang.h"
|
||||
#include "port/likely.h"
|
||||
#include "port/malloc.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/distributed_mutex.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace lru_cache {
|
||||
|
||||
// LRU cache implementation. This class is not thread-safe.
|
||||
|
||||
@@ -38,63 +36,74 @@ namespace lru_cache {
|
||||
// (refs == 0 && in_cache == true)
|
||||
// 3. Referenced externally AND not in hash table.
|
||||
// In that case the entry is not in the LRU list and not in hash table.
|
||||
// The entry must be freed if refs becomes 0 in this state.
|
||||
// The entry can be freed when refs becomes 0.
|
||||
// (refs >= 1 && in_cache == false)
|
||||
// If you call LRUCacheShard::Release enough times on an entry in state 1, it
|
||||
// will go into state 2. To move from state 1 to state 3, either call
|
||||
// LRUCacheShard::Erase or LRUCacheShard::Insert with the same key (but
|
||||
// possibly different value). To move from state 2 to state 1, use
|
||||
// LRUCacheShard::Lookup.
|
||||
// While refs > 0, public properties like value and deleter must not change.
|
||||
//
|
||||
// All newly created LRUHandles are in state 1. If you call
|
||||
// LRUCacheShard::Release on entry in state 1, it will go into state 2.
|
||||
// To move from state 1 to state 3, either call LRUCacheShard::Erase or
|
||||
// LRUCacheShard::Insert with the same key (but possibly different value).
|
||||
// To move from state 2 to state 1, use LRUCacheShard::Lookup.
|
||||
// Before destruction, make sure that no handles are in state 1. This means
|
||||
// that any successful LRUCacheShard::Lookup/LRUCacheShard::Insert have a
|
||||
// matching LRUCache::Release (to move into state 2) or LRUCacheShard::Erase
|
||||
// (to move into state 3).
|
||||
|
||||
struct LRUHandle : public Cache::Handle {
|
||||
Cache::ObjectPtr value;
|
||||
const Cache::CacheItemHelper* helper;
|
||||
LRUHandle* next_hash;
|
||||
struct LRUHandle {
|
||||
void* value;
|
||||
union Info {
|
||||
Info() {}
|
||||
~Info() {}
|
||||
Cache::DeleterFn deleter;
|
||||
const ShardedCache::CacheItemHelper* helper;
|
||||
} info_;
|
||||
// An entry is not added to the LRUHandleTable until the secondary cache
|
||||
// lookup is complete, so its safe to have this union.
|
||||
union {
|
||||
LRUHandle* next_hash;
|
||||
SecondaryCacheResultHandle* sec_handle;
|
||||
};
|
||||
LRUHandle* next;
|
||||
LRUHandle* prev;
|
||||
size_t total_charge; // TODO(opt): Only allow uint32_t?
|
||||
size_t charge; // TODO(opt): Only allow uint32_t?
|
||||
size_t key_length;
|
||||
// The hash of key(). Used for fast sharding and comparisons.
|
||||
uint32_t hash;
|
||||
// The number of external refs to this entry. The cache itself is not counted.
|
||||
uint32_t refs;
|
||||
|
||||
// Mutable flags - access controlled by mutex
|
||||
// The m_ and M_ prefixes (and im_ and IM_ later) are to hopefully avoid
|
||||
// checking an M_ flag on im_flags or an IM_ flag on m_flags.
|
||||
uint8_t m_flags;
|
||||
enum MFlags : uint8_t {
|
||||
enum Flags : uint8_t {
|
||||
// Whether this entry is referenced by the hash table.
|
||||
M_IN_CACHE = (1 << 0),
|
||||
// Whether this entry has had any lookups (hits).
|
||||
M_HAS_HIT = (1 << 1),
|
||||
IN_CACHE = (1 << 0),
|
||||
// Whether this entry is high priority entry.
|
||||
IS_HIGH_PRI = (1 << 1),
|
||||
// Whether this entry is in high-pri pool.
|
||||
M_IN_HIGH_PRI_POOL = (1 << 2),
|
||||
// Whether this entry is in low-pri pool.
|
||||
M_IN_LOW_PRI_POOL = (1 << 3),
|
||||
IN_HIGH_PRI_POOL = (1 << 2),
|
||||
// Whether this entry has had any lookups (hits).
|
||||
HAS_HIT = (1 << 3),
|
||||
// Can this be inserted into the secondary cache
|
||||
IS_SECONDARY_CACHE_COMPATIBLE = (1 << 4),
|
||||
// Is the handle still being read from a lower tier
|
||||
IS_PENDING = (1 << 5),
|
||||
// Has the item been promoted from a lower tier
|
||||
IS_PROMOTED = (1 << 6),
|
||||
};
|
||||
|
||||
// "Immutable" flags - only set in single-threaded context and then
|
||||
// can be accessed without mutex
|
||||
uint8_t im_flags;
|
||||
enum ImFlags : uint8_t {
|
||||
// Whether this entry is high priority entry.
|
||||
IM_IS_HIGH_PRI = (1 << 0),
|
||||
// Whether this entry is low priority entry.
|
||||
IM_IS_LOW_PRI = (1 << 1),
|
||||
// Marks result handles that should not be inserted into cache
|
||||
IM_IS_STANDALONE = (1 << 2),
|
||||
};
|
||||
uint8_t flags;
|
||||
|
||||
#ifdef __SANITIZE_THREAD__
|
||||
// TSAN can report a false data race on flags, where one thread is writing
|
||||
// to one of the mutable bits and another thread is reading this immutable
|
||||
// bit. So precisely suppress that TSAN warning, we separate out this bit
|
||||
// during TSAN runs.
|
||||
bool is_secondary_cache_compatible_for_tsan;
|
||||
#endif // __SANITIZE_THREAD__
|
||||
|
||||
// Beginning of the key (MUST BE THE LAST FIELD IN THIS STRUCT!)
|
||||
char key_data[1];
|
||||
|
||||
Slice key() const { return Slice(key_data, key_length); }
|
||||
|
||||
// For HandleImpl concept
|
||||
uint32_t GetHash() const { return hash; }
|
||||
|
||||
// Increase the reference count by 1.
|
||||
void Ref() { refs++; }
|
||||
|
||||
@@ -108,97 +117,110 @@ struct LRUHandle : public Cache::Handle {
|
||||
// Return true if there are external refs, false otherwise.
|
||||
bool HasRefs() const { return refs > 0; }
|
||||
|
||||
bool InCache() const { return m_flags & M_IN_CACHE; }
|
||||
bool IsHighPri() const { return im_flags & IM_IS_HIGH_PRI; }
|
||||
bool InHighPriPool() const { return m_flags & M_IN_HIGH_PRI_POOL; }
|
||||
bool IsLowPri() const { return im_flags & IM_IS_LOW_PRI; }
|
||||
bool InLowPriPool() const { return m_flags & M_IN_LOW_PRI_POOL; }
|
||||
bool HasHit() const { return m_flags & M_HAS_HIT; }
|
||||
bool IsStandalone() const { return im_flags & IM_IS_STANDALONE; }
|
||||
bool InCache() const { return flags & IN_CACHE; }
|
||||
bool IsHighPri() const { return flags & IS_HIGH_PRI; }
|
||||
bool InHighPriPool() const { return flags & IN_HIGH_PRI_POOL; }
|
||||
bool HasHit() const { return flags & HAS_HIT; }
|
||||
bool IsSecondaryCacheCompatible() const {
|
||||
#ifdef __SANITIZE_THREAD__
|
||||
return is_secondary_cache_compatible_for_tsan;
|
||||
#else
|
||||
return flags & IS_SECONDARY_CACHE_COMPATIBLE;
|
||||
#endif // __SANITIZE_THREAD__
|
||||
}
|
||||
bool IsPending() const { return flags & IS_PENDING; }
|
||||
bool IsPromoted() const { return flags & IS_PROMOTED; }
|
||||
|
||||
void SetInCache(bool in_cache) {
|
||||
if (in_cache) {
|
||||
m_flags |= M_IN_CACHE;
|
||||
flags |= IN_CACHE;
|
||||
} else {
|
||||
m_flags &= ~M_IN_CACHE;
|
||||
flags &= ~IN_CACHE;
|
||||
}
|
||||
}
|
||||
|
||||
void SetPriority(Cache::Priority priority) {
|
||||
if (priority == Cache::Priority::HIGH) {
|
||||
im_flags |= IM_IS_HIGH_PRI;
|
||||
im_flags &= ~IM_IS_LOW_PRI;
|
||||
} else if (priority == Cache::Priority::LOW) {
|
||||
im_flags &= ~IM_IS_HIGH_PRI;
|
||||
im_flags |= IM_IS_LOW_PRI;
|
||||
flags |= IS_HIGH_PRI;
|
||||
} else {
|
||||
im_flags &= ~IM_IS_HIGH_PRI;
|
||||
im_flags &= ~IM_IS_LOW_PRI;
|
||||
flags &= ~IS_HIGH_PRI;
|
||||
}
|
||||
}
|
||||
|
||||
void SetInHighPriPool(bool in_high_pri_pool) {
|
||||
if (in_high_pri_pool) {
|
||||
m_flags |= M_IN_HIGH_PRI_POOL;
|
||||
flags |= IN_HIGH_PRI_POOL;
|
||||
} else {
|
||||
m_flags &= ~M_IN_HIGH_PRI_POOL;
|
||||
flags &= ~IN_HIGH_PRI_POOL;
|
||||
}
|
||||
}
|
||||
|
||||
void SetInLowPriPool(bool in_low_pri_pool) {
|
||||
if (in_low_pri_pool) {
|
||||
m_flags |= M_IN_LOW_PRI_POOL;
|
||||
void SetHit() { flags |= HAS_HIT; }
|
||||
|
||||
void SetSecondaryCacheCompatible(bool compat) {
|
||||
if (compat) {
|
||||
flags |= IS_SECONDARY_CACHE_COMPATIBLE;
|
||||
} else {
|
||||
m_flags &= ~M_IN_LOW_PRI_POOL;
|
||||
flags &= ~IS_SECONDARY_CACHE_COMPATIBLE;
|
||||
}
|
||||
#ifdef __SANITIZE_THREAD__
|
||||
is_secondary_cache_compatible_for_tsan = compat;
|
||||
#endif // __SANITIZE_THREAD__
|
||||
}
|
||||
|
||||
void SetIncomplete(bool incomp) {
|
||||
if (incomp) {
|
||||
flags |= IS_PENDING;
|
||||
} else {
|
||||
flags &= ~IS_PENDING;
|
||||
}
|
||||
}
|
||||
|
||||
void SetHit() { m_flags |= M_HAS_HIT; }
|
||||
|
||||
void SetIsStandalone(bool is_standalone) {
|
||||
if (is_standalone) {
|
||||
im_flags |= IM_IS_STANDALONE;
|
||||
void SetPromoted(bool promoted) {
|
||||
if (promoted) {
|
||||
flags |= IS_PROMOTED;
|
||||
} else {
|
||||
im_flags &= ~IM_IS_STANDALONE;
|
||||
flags &= ~IS_PROMOTED;
|
||||
}
|
||||
}
|
||||
|
||||
void Free(MemoryAllocator* allocator) {
|
||||
void Free() {
|
||||
assert(refs == 0);
|
||||
assert(helper);
|
||||
if (helper->del_cb) {
|
||||
helper->del_cb(value, allocator);
|
||||
#ifdef __SANITIZE_THREAD__
|
||||
// Here we can safely assert they are the same without a data race reported
|
||||
assert(((flags & IS_SECONDARY_CACHE_COMPATIBLE) != 0) ==
|
||||
is_secondary_cache_compatible_for_tsan);
|
||||
#endif // __SANITIZE_THREAD__
|
||||
if (!IsSecondaryCacheCompatible() && info_.deleter) {
|
||||
(*info_.deleter)(key(), value);
|
||||
} else if (IsSecondaryCacheCompatible()) {
|
||||
if (IsPending()) {
|
||||
assert(sec_handle != nullptr);
|
||||
SecondaryCacheResultHandle* tmp_sec_handle = sec_handle;
|
||||
tmp_sec_handle->Wait();
|
||||
value = tmp_sec_handle->Value();
|
||||
delete tmp_sec_handle;
|
||||
}
|
||||
if (value) {
|
||||
(*info_.helper->del_cb)(key(), value);
|
||||
}
|
||||
}
|
||||
|
||||
free(this);
|
||||
delete[] reinterpret_cast<char*>(this);
|
||||
}
|
||||
|
||||
inline size_t CalcuMetaCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) const {
|
||||
if (metadata_charge_policy != kFullChargeCacheMetadata) {
|
||||
return 0;
|
||||
} else {
|
||||
// Calculate the memory usage by metadata
|
||||
inline size_t CalcTotalCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
size_t meta_charge = 0;
|
||||
if (metadata_charge_policy == kFullChargeCacheMetadata) {
|
||||
#ifdef ROCKSDB_MALLOC_USABLE_SIZE
|
||||
return malloc_usable_size(
|
||||
const_cast<void*>(static_cast<const void*>(this)));
|
||||
meta_charge += malloc_usable_size(static_cast<void*>(this));
|
||||
#else
|
||||
// This is the size that is used when a new handle is created.
|
||||
return sizeof(LRUHandle) - 1 + key_length;
|
||||
// This is the size that is used when a new handle is created
|
||||
meta_charge += sizeof(LRUHandle) - 1 + key_length;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the memory usage by metadata.
|
||||
inline void CalcTotalCharge(
|
||||
size_t charge, CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
total_charge = charge + CalcuMetaCharge(metadata_charge_policy);
|
||||
}
|
||||
|
||||
inline size_t GetCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) const {
|
||||
size_t meta_charge = CalcuMetaCharge(metadata_charge_policy);
|
||||
assert(total_charge >= meta_charge);
|
||||
return total_charge - meta_charge;
|
||||
return charge + meta_charge;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -209,7 +231,10 @@ struct LRUHandle : public Cache::Handle {
|
||||
// 4.4.3's builtin hashtable.
|
||||
class LRUHandleTable {
|
||||
public:
|
||||
explicit LRUHandleTable(int max_upper_hash_bits, MemoryAllocator* allocator);
|
||||
// If the table uses more hash bits than `max_upper_hash_bits`,
|
||||
// it will eat into the bits used for sharding, which are constant
|
||||
// for a given LRUHandleTable.
|
||||
explicit LRUHandleTable(int max_upper_hash_bits);
|
||||
~LRUHandleTable();
|
||||
|
||||
LRUHandle* Lookup(const Slice& key, uint32_t hash);
|
||||
@@ -217,8 +242,8 @@ class LRUHandleTable {
|
||||
LRUHandle* Remove(const Slice& key, uint32_t hash);
|
||||
|
||||
template <typename T>
|
||||
void ApplyToEntriesRange(T func, size_t index_begin, size_t index_end) {
|
||||
for (size_t i = index_begin; i < index_end; i++) {
|
||||
void ApplyToEntriesRange(T func, uint32_t index_begin, uint32_t index_end) {
|
||||
for (uint32_t i = index_begin; i < index_end; i++) {
|
||||
LRUHandle* h = list_[i];
|
||||
while (h != nullptr) {
|
||||
auto n = h->next_hash;
|
||||
@@ -231,10 +256,6 @@ class LRUHandleTable {
|
||||
|
||||
int GetLengthBits() const { return length_bits_; }
|
||||
|
||||
size_t GetOccupancyCount() const { return elems_; }
|
||||
|
||||
MemoryAllocator* GetAllocator() const { return allocator_; }
|
||||
|
||||
private:
|
||||
// Return a pointer to slot that points to a cache entry that
|
||||
// matches key/hash. If there is no such cache entry, return a
|
||||
@@ -251,111 +272,113 @@ class LRUHandleTable {
|
||||
// a linked list of cache entries that hash into the bucket.
|
||||
std::unique_ptr<LRUHandle*[]> list_;
|
||||
|
||||
// Number of elements currently in the table.
|
||||
// Number of elements currently in the table
|
||||
uint32_t elems_;
|
||||
|
||||
// Set from max_upper_hash_bits (see constructor).
|
||||
// Set from max_upper_hash_bits (see constructor)
|
||||
const int max_length_bits_;
|
||||
|
||||
// From Cache, needed for delete
|
||||
MemoryAllocator* const allocator_;
|
||||
};
|
||||
|
||||
// A single shard of sharded cache.
|
||||
class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShardBase {
|
||||
class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
public:
|
||||
// NOTE: the eviction_callback ptr is saved, as is it assumed to be kept
|
||||
// alive in Cache.
|
||||
LRUCacheShard(size_t capacity, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio, double low_pri_pool_ratio,
|
||||
bool use_adaptive_mutex,
|
||||
double high_pri_pool_ratio, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
int max_upper_hash_bits, MemoryAllocator* allocator,
|
||||
const Cache::EvictionCallback* eviction_callback);
|
||||
|
||||
public: // Type definitions expected as parameter to ShardedCache
|
||||
using HandleImpl = LRUHandle;
|
||||
using HashVal = uint32_t;
|
||||
using HashCref = uint32_t;
|
||||
|
||||
public: // Function definitions expected as parameter to ShardedCache
|
||||
static inline HashVal ComputeHash(const Slice& key, uint32_t seed) {
|
||||
return Lower32of64(GetSliceNPHash64(key, seed));
|
||||
}
|
||||
int max_upper_hash_bits,
|
||||
const std::shared_ptr<SecondaryCache>& secondary_cache);
|
||||
virtual ~LRUCacheShard() override = default;
|
||||
|
||||
// Separate from constructor so caller can easily make an array of LRUCache
|
||||
// if current usage is more than new capacity, the function will attempt to
|
||||
// free the needed space.
|
||||
void SetCapacity(size_t capacity);
|
||||
// free the needed space
|
||||
virtual void SetCapacity(size_t capacity) override;
|
||||
|
||||
// Set the flag to reject insertion if cache if full.
|
||||
void SetStrictCapacityLimit(bool strict_capacity_limit);
|
||||
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) override;
|
||||
|
||||
// Set percentage of capacity reserved for high-pri cache entries.
|
||||
void SetHighPriorityPoolRatio(double high_pri_pool_ratio);
|
||||
|
||||
// Set percentage of capacity reserved for low-pri cache entries.
|
||||
void SetLowPriorityPoolRatio(double low_pri_pool_ratio);
|
||||
|
||||
// Like Cache methods, but with an extra "hash" parameter.
|
||||
Status Insert(const Slice& key, uint32_t hash, Cache::ObjectPtr value,
|
||||
const Cache::CacheItemHelper* helper, size_t charge,
|
||||
LRUHandle** handle, Cache::Priority priority);
|
||||
|
||||
LRUHandle* CreateStandalone(const Slice& key, uint32_t hash,
|
||||
Cache::ObjectPtr obj,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
size_t charge, bool allow_uncharged);
|
||||
|
||||
LRUHandle* Lookup(const Slice& key, uint32_t hash,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context,
|
||||
Cache::Priority priority, Statistics* stats);
|
||||
|
||||
bool Release(LRUHandle* handle, bool useful, bool erase_if_last_ref);
|
||||
bool Ref(LRUHandle* handle);
|
||||
void Erase(const Slice& key, uint32_t hash);
|
||||
virtual Status Insert(const Slice& key, uint32_t hash, void* value,
|
||||
size_t charge, Cache::DeleterFn deleter,
|
||||
Cache::Handle** handle,
|
||||
Cache::Priority priority) override {
|
||||
return Insert(key, hash, value, charge, deleter, nullptr, handle, priority);
|
||||
}
|
||||
virtual Status Insert(const Slice& key, uint32_t hash, void* value,
|
||||
const Cache::CacheItemHelper* helper, size_t charge,
|
||||
Cache::Handle** handle,
|
||||
Cache::Priority priority) override {
|
||||
assert(helper);
|
||||
return Insert(key, hash, value, charge, nullptr, helper, handle, priority);
|
||||
}
|
||||
// If helper_cb is null, the values of the following arguments don't
|
||||
// matter
|
||||
virtual Cache::Handle* Lookup(const Slice& key, uint32_t hash,
|
||||
const ShardedCache::CacheItemHelper* helper,
|
||||
const ShardedCache::CreateCallback& create_cb,
|
||||
ShardedCache::Priority priority, bool wait,
|
||||
Statistics* stats) override;
|
||||
virtual Cache::Handle* Lookup(const Slice& key, uint32_t hash) override {
|
||||
return Lookup(key, hash, nullptr, nullptr, Cache::Priority::LOW, true,
|
||||
nullptr);
|
||||
}
|
||||
virtual bool Release(Cache::Handle* handle, bool /*useful*/,
|
||||
bool force_erase) override {
|
||||
return Release(handle, force_erase);
|
||||
}
|
||||
virtual bool IsReady(Cache::Handle* /*handle*/) override;
|
||||
virtual void Wait(Cache::Handle* /*handle*/) override {}
|
||||
virtual bool Ref(Cache::Handle* handle) override;
|
||||
virtual bool Release(Cache::Handle* handle,
|
||||
bool force_erase = false) override;
|
||||
virtual void Erase(const Slice& key, uint32_t hash) override;
|
||||
|
||||
// Although in some platforms the update of size_t is atomic, to make sure
|
||||
// GetUsage() and GetPinnedUsage() work correctly under any platform, we'll
|
||||
// protect them with mutex_.
|
||||
|
||||
size_t GetUsage() const;
|
||||
size_t GetPinnedUsage() const;
|
||||
size_t GetOccupancyCount() const;
|
||||
size_t GetTableAddressCount() const;
|
||||
virtual size_t GetUsage() const override;
|
||||
virtual size_t GetPinnedUsage() const override;
|
||||
|
||||
void ApplyToSomeEntries(
|
||||
const std::function<void(const Slice& key, Cache::ObjectPtr value,
|
||||
size_t charge,
|
||||
const Cache::CacheItemHelper* helper)>& callback,
|
||||
size_t average_entries_per_lock, size_t* state);
|
||||
virtual void ApplyToSomeEntries(
|
||||
const std::function<void(const Slice& key, void* value, size_t charge,
|
||||
DeleterFn deleter)>& callback,
|
||||
uint32_t average_entries_per_lock, uint32_t* state) override;
|
||||
|
||||
void EraseUnRefEntries();
|
||||
virtual void EraseUnRefEntries() override;
|
||||
|
||||
public: // other function definitions
|
||||
void TEST_GetLRUList(LRUHandle** lru, LRUHandle** lru_low_pri,
|
||||
LRUHandle** lru_bottom_pri);
|
||||
virtual std::string GetPrintableOptions() const override;
|
||||
|
||||
// Retrieves number of elements in LRU, for unit test purpose only.
|
||||
// Not threadsafe.
|
||||
void TEST_GetLRUList(LRUHandle** lru, LRUHandle** lru_low_pri);
|
||||
|
||||
// Retrieves number of elements in LRU, for unit test purpose only
|
||||
// not threadsafe
|
||||
size_t TEST_GetLRUSize();
|
||||
|
||||
// Retrieves high pri pool ratio
|
||||
// Retrieves high pri pool ratio
|
||||
double GetHighPriPoolRatio();
|
||||
|
||||
// Retrieves low pri pool ratio
|
||||
double GetLowPriPoolRatio();
|
||||
|
||||
void AppendPrintableOptions(std::string& /*str*/) const;
|
||||
|
||||
private:
|
||||
friend class LRUCache;
|
||||
// Insert an item into the hash table and, if handle is null, insert into
|
||||
// the LRU list. Older items are evicted as necessary. Frees `item` on
|
||||
// non-OK status.
|
||||
Status InsertItem(LRUHandle* item, LRUHandle** handle);
|
||||
|
||||
// the LRU list. Older items are evicted as necessary. If the cache is full
|
||||
// and free_handle_on_fail is true, the item is deleted and handle is set to.
|
||||
Status InsertItem(LRUHandle* item, Cache::Handle** handle,
|
||||
bool free_handle_on_fail);
|
||||
Status Insert(const Slice& key, uint32_t hash, void* value, size_t charge,
|
||||
DeleterFn deleter, const Cache::CacheItemHelper* helper,
|
||||
Cache::Handle** handle, Cache::Priority priority);
|
||||
// Promote an item looked up from the secondary cache to the LRU cache. The
|
||||
// item is only inserted into the hash table and not the LRU list, and only
|
||||
// if the cache is not at full capacity, as is the case during Insert. The
|
||||
// caller should hold a reference on the LRUHandle. When the caller releases
|
||||
// the last reference, the item is added to the LRU list.
|
||||
// The item is promoted to the high pri or low pri pool as specified by the
|
||||
// caller in Lookup.
|
||||
void Promote(LRUHandle* e);
|
||||
void LRU_Remove(LRUHandle* e);
|
||||
void LRU_Insert(LRUHandle* e);
|
||||
|
||||
@@ -366,24 +389,15 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShardBase {
|
||||
// Free some space following strict LRU policy until enough space
|
||||
// to hold (usage_ + charge) is freed or the lru list is empty
|
||||
// This function is not thread safe - it needs to be executed while
|
||||
// holding the mutex_.
|
||||
// holding the mutex_
|
||||
void EvictFromLRU(size_t charge, autovector<LRUHandle*>* deleted);
|
||||
|
||||
void NotifyEvicted(const autovector<LRUHandle*>& evicted_handles);
|
||||
|
||||
LRUHandle* CreateHandle(const Slice& key, uint32_t hash,
|
||||
Cache::ObjectPtr value,
|
||||
const Cache::CacheItemHelper* helper, size_t charge);
|
||||
|
||||
// Initialized before use.
|
||||
size_t capacity_;
|
||||
|
||||
// Memory size for entries in high-pri pool.
|
||||
size_t high_pri_pool_usage_;
|
||||
|
||||
// Memory size for entries in low-pri pool.
|
||||
size_t low_pri_pool_usage_;
|
||||
|
||||
// Whether to reject insertion if cache reaches its full capacity.
|
||||
bool strict_capacity_limit_;
|
||||
|
||||
@@ -394,13 +408,6 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShardBase {
|
||||
// Remember the value to avoid recomputing each time.
|
||||
double high_pri_pool_capacity_;
|
||||
|
||||
// Ratio of capacity reserved for low priority cache entries.
|
||||
double low_pri_pool_ratio_;
|
||||
|
||||
// Low-pri pool size, equals to capacity * low_pri_pool_ratio.
|
||||
// Remember the value to avoid recomputing each time.
|
||||
double low_pri_pool_capacity_;
|
||||
|
||||
// Dummy head of LRU list.
|
||||
// lru.prev is newest entry, lru.next is oldest entry.
|
||||
// LRU contains items which can be evicted, ie reference only by cache
|
||||
@@ -409,9 +416,6 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShardBase {
|
||||
// Pointer to head of low-pri pool in LRU list.
|
||||
LRUHandle* lru_low_pri_;
|
||||
|
||||
// Pointer to head of bottom-pri pool in LRU list.
|
||||
LRUHandle* lru_bottom_pri_;
|
||||
|
||||
// ------------^^^^^^^^^^^^^-----------
|
||||
// Not frequently modified data members
|
||||
// ------------------------------------
|
||||
@@ -425,43 +429,53 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShardBase {
|
||||
// ------------vvvvvvvvvvvvv-----------
|
||||
LRUHandleTable table_;
|
||||
|
||||
// Memory size for entries residing in the cache.
|
||||
// Memory size for entries residing in the cache
|
||||
size_t usage_;
|
||||
|
||||
// Memory size for entries residing only in the LRU list.
|
||||
// Memory size for entries residing only in the LRU list
|
||||
size_t lru_usage_;
|
||||
|
||||
// mutex_ protects the following state.
|
||||
// We don't count mutex_ as the cache's internal state so semantically we
|
||||
// don't mind mutex_ invoking the non-const actions.
|
||||
mutable DMutex mutex_;
|
||||
mutable port::Mutex mutex_;
|
||||
|
||||
// A reference to Cache::eviction_callback_
|
||||
const Cache::EvictionCallback& eviction_callback_;
|
||||
std::shared_ptr<SecondaryCache> secondary_cache_;
|
||||
};
|
||||
|
||||
class LRUCache
|
||||
#ifdef NDEBUG
|
||||
final
|
||||
#endif
|
||||
: public ShardedCache<LRUCacheShard> {
|
||||
: public ShardedCache {
|
||||
public:
|
||||
explicit LRUCache(const LRUCacheOptions& opts);
|
||||
const char* Name() const override { return "LRUCache"; }
|
||||
ObjectPtr Value(Handle* handle) override;
|
||||
size_t GetCharge(Handle* handle) const override;
|
||||
const CacheItemHelper* GetCacheItemHelper(Handle* handle) const override;
|
||||
LRUCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator = nullptr,
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy =
|
||||
kDontChargeCacheMetadata,
|
||||
const std::shared_ptr<SecondaryCache>& secondary_cache = nullptr);
|
||||
virtual ~LRUCache();
|
||||
virtual const char* Name() const override { return "LRUCache"; }
|
||||
virtual CacheShard* GetShard(uint32_t shard) override;
|
||||
virtual const CacheShard* GetShard(uint32_t shard) const override;
|
||||
virtual void* Value(Handle* handle) override;
|
||||
virtual size_t GetCharge(Handle* handle) const override;
|
||||
virtual uint32_t GetHash(Handle* handle) const override;
|
||||
virtual DeleterFn GetDeleter(Handle* handle) const override;
|
||||
virtual void DisownData() override;
|
||||
virtual void WaitAll(std::vector<Handle*>& handles) override;
|
||||
|
||||
// Retrieves number of elements in LRU, for unit test purpose only.
|
||||
// Retrieves number of elements in LRU, for unit test purpose only
|
||||
size_t TEST_GetLRUSize();
|
||||
// Retrieves high pri pool ratio.
|
||||
// Retrieves high pri pool ratio
|
||||
double GetHighPriPoolRatio();
|
||||
|
||||
private:
|
||||
LRUCacheShard* shards_ = nullptr;
|
||||
int num_shards_ = 0;
|
||||
std::shared_ptr<SecondaryCache> secondary_cache_;
|
||||
};
|
||||
|
||||
} // namespace lru_cache
|
||||
|
||||
using LRUCache = lru_cache::LRUCache;
|
||||
using LRUHandle = lru_cache::LRUHandle;
|
||||
using LRUCacheShard = lru_cache::LRUCacheShard;
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
Vendored
+444
-1305
File diff suppressed because it is too large
Load Diff
Vendored
+168
@@ -0,0 +1,168 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "cache/lru_secondary_cache.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "memory/memory_allocator.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
|
||||
void DeletionCallback(const Slice& /*key*/, void* obj) {
|
||||
delete reinterpret_cast<CacheAllocationPtr*>(obj);
|
||||
obj = nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
LRUSecondaryCache::LRUSecondaryCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
CompressionType compression_type, uint32_t compress_format_version)
|
||||
: cache_options_(capacity, num_shard_bits, strict_capacity_limit,
|
||||
high_pri_pool_ratio, memory_allocator, use_adaptive_mutex,
|
||||
metadata_charge_policy, compression_type,
|
||||
compress_format_version) {
|
||||
cache_ = NewLRUCache(capacity, num_shard_bits, strict_capacity_limit,
|
||||
high_pri_pool_ratio, memory_allocator,
|
||||
use_adaptive_mutex, metadata_charge_policy);
|
||||
}
|
||||
|
||||
LRUSecondaryCache::~LRUSecondaryCache() { cache_.reset(); }
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> LRUSecondaryCache::Lookup(
|
||||
const Slice& key, const Cache::CreateCallback& create_cb, bool /*wait*/) {
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle;
|
||||
Cache::Handle* lru_handle = cache_->Lookup(key);
|
||||
if (lru_handle == nullptr) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
CacheAllocationPtr* ptr =
|
||||
reinterpret_cast<CacheAllocationPtr*>(cache_->Value(lru_handle));
|
||||
void* value = nullptr;
|
||||
size_t charge = 0;
|
||||
Status s;
|
||||
|
||||
if (cache_options_.compression_type == kNoCompression) {
|
||||
s = create_cb(ptr->get(), cache_->GetCharge(lru_handle), &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;
|
||||
uncompressed = UncompressData(
|
||||
uncompression_info, (char*)ptr->get(), cache_->GetCharge(lru_handle),
|
||||
&uncompressed_size, cache_options_.compress_format_version,
|
||||
cache_options_.memory_allocator.get());
|
||||
|
||||
if (!uncompressed) {
|
||||
cache_->Release(lru_handle, true);
|
||||
return handle;
|
||||
}
|
||||
s = create_cb(uncompressed.get(), uncompressed_size, &value, &charge);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
cache_->Release(lru_handle, true);
|
||||
return handle;
|
||||
}
|
||||
|
||||
handle.reset(new LRUSecondaryCacheResultHandle(value, charge));
|
||||
cache_->Release(lru_handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
Status LRUSecondaryCache::Insert(const Slice& key, void* value,
|
||||
const Cache::CacheItemHelper* helper) {
|
||||
size_t size = (*helper->size_cb)(value);
|
||||
CacheAllocationPtr ptr =
|
||||
AllocateBlock(size, cache_options_.memory_allocator.get());
|
||||
|
||||
Status s = (*helper->saveto_cb)(value, 0, size, ptr.get());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
Slice val(ptr.get(), size);
|
||||
|
||||
std::string compressed_val;
|
||||
if (cache_options_.compression_type != kNoCompression) {
|
||||
CompressionOptions compression_opts;
|
||||
CompressionContext compression_context(cache_options_.compression_type);
|
||||
uint64_t sample_for_compression = 0;
|
||||
CompressionInfo compression_info(
|
||||
compression_opts, compression_context, CompressionDict::GetEmptyDict(),
|
||||
cache_options_.compression_type, sample_for_compression);
|
||||
|
||||
bool success =
|
||||
CompressData(val, compression_info,
|
||||
cache_options_.compress_format_version, &compressed_val);
|
||||
|
||||
if (!success) {
|
||||
return Status::Corruption("Error compressing value.");
|
||||
}
|
||||
|
||||
val = Slice(compressed_val);
|
||||
size = compressed_val.size();
|
||||
ptr = AllocateBlock(size, cache_options_.memory_allocator.get());
|
||||
memcpy(ptr.get(), compressed_val.data(), size);
|
||||
}
|
||||
|
||||
CacheAllocationPtr* buf = new CacheAllocationPtr(std::move(ptr));
|
||||
|
||||
return cache_->Insert(key, buf, size, DeletionCallback);
|
||||
}
|
||||
|
||||
void LRUSecondaryCache::Erase(const Slice& key) { cache_->Erase(key); }
|
||||
|
||||
std::string LRUSecondaryCache::GetPrintableOptions() const {
|
||||
std::string ret;
|
||||
ret.reserve(20000);
|
||||
const int kBufferSize = 200;
|
||||
char buffer[kBufferSize];
|
||||
ret.append(cache_->GetPrintableOptions());
|
||||
snprintf(buffer, kBufferSize, " compression_type : %s\n",
|
||||
CompressionTypeToString(cache_options_.compression_type).c_str());
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " compression_type : %d\n",
|
||||
cache_options_.compress_format_version);
|
||||
ret.append(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache> NewLRUSecondaryCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
CompressionType compression_type, uint32_t compress_format_version) {
|
||||
return std::make_shared<LRUSecondaryCache>(
|
||||
capacity, num_shard_bits, strict_capacity_limit, high_pri_pool_ratio,
|
||||
memory_allocator, use_adaptive_mutex, metadata_charge_policy,
|
||||
compression_type, compress_format_version);
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache> NewLRUSecondaryCache(
|
||||
const LRUSecondaryCacheOptions& opts) {
|
||||
// The secondary_cache is disabled for this LRUCache instance.
|
||||
assert(opts.secondary_cache == nullptr);
|
||||
return NewLRUSecondaryCache(
|
||||
opts.capacity, opts.num_shard_bits, opts.strict_capacity_limit,
|
||||
opts.high_pri_pool_ratio, opts.memory_allocator, opts.use_adaptive_mutex,
|
||||
opts.metadata_charge_policy, opts.compression_type,
|
||||
opts.compress_format_version);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "cache/lru_cache.h"
|
||||
#include "memory/memory_allocator.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "util/compression.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class LRUSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
|
||||
public:
|
||||
LRUSecondaryCacheResultHandle(void* value, size_t size)
|
||||
: value_(value), size_(size) {}
|
||||
virtual ~LRUSecondaryCacheResultHandle() override = default;
|
||||
|
||||
LRUSecondaryCacheResultHandle(const LRUSecondaryCacheResultHandle&) = delete;
|
||||
LRUSecondaryCacheResultHandle& operator=(
|
||||
const LRUSecondaryCacheResultHandle&) = delete;
|
||||
|
||||
bool IsReady() override { return true; }
|
||||
|
||||
void Wait() override {}
|
||||
|
||||
void* Value() override { return value_; }
|
||||
|
||||
size_t Size() override { return size_; }
|
||||
|
||||
private:
|
||||
void* value_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
// The LRUSecondaryCache is a concrete implementation of
|
||||
// rocksdb::SecondaryCache.
|
||||
//
|
||||
// Users can also cast a pointer to it and call methods on
|
||||
// it directly, especially custom methods that may be added
|
||||
// in the future. For example -
|
||||
// std::unique_ptr<rocksdb::SecondaryCache> cache =
|
||||
// NewLRUSecondaryCache(opts);
|
||||
// static_cast<LRUSecondaryCache*>(cache.get())->Erase(key);
|
||||
|
||||
class LRUSecondaryCache : public SecondaryCache {
|
||||
public:
|
||||
LRUSecondaryCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator = nullptr,
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy =
|
||||
kDontChargeCacheMetadata,
|
||||
CompressionType compression_type = CompressionType::kLZ4Compression,
|
||||
uint32_t compress_format_version = 2);
|
||||
virtual ~LRUSecondaryCache() override;
|
||||
|
||||
const char* Name() const override { return "LRUSecondaryCache"; }
|
||||
|
||||
Status Insert(const Slice& key, void* value,
|
||||
const Cache::CacheItemHelper* helper) override;
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CreateCallback& create_cb,
|
||||
bool /*wait*/) override;
|
||||
|
||||
void Erase(const Slice& key) override;
|
||||
|
||||
void WaitAll(std::vector<SecondaryCacheResultHandle*> /*handles*/) override {}
|
||||
|
||||
std::string GetPrintableOptions() const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Cache> cache_;
|
||||
LRUSecondaryCacheOptions cache_options_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+597
@@ -0,0 +1,597 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "cache/lru_secondary_cache.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
#include "memory/jemalloc_nodump_allocator.h"
|
||||
#include "memory/memory_allocator.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/random.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class LRUSecondaryCacheTest : public testing::Test {
|
||||
public:
|
||||
LRUSecondaryCacheTest() : fail_create_(false) {}
|
||||
~LRUSecondaryCacheTest() {}
|
||||
|
||||
protected:
|
||||
class TestItem {
|
||||
public:
|
||||
TestItem(const char* buf, size_t size) : buf_(new char[size]), size_(size) {
|
||||
memcpy(buf_.get(), buf, size);
|
||||
}
|
||||
~TestItem() {}
|
||||
|
||||
char* Buf() { return buf_.get(); }
|
||||
size_t Size() { return size_; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<char[]> buf_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
static size_t SizeCallback(void* obj) {
|
||||
return reinterpret_cast<TestItem*>(obj)->Size();
|
||||
}
|
||||
|
||||
static Status SaveToCallback(void* from_obj, size_t from_offset,
|
||||
size_t length, void* out) {
|
||||
TestItem* item = reinterpret_cast<TestItem*>(from_obj);
|
||||
const char* buf = item->Buf();
|
||||
EXPECT_EQ(length, item->Size());
|
||||
EXPECT_EQ(from_offset, 0);
|
||||
memcpy(out, buf, length);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static void DeletionCallback(const Slice& /*key*/, void* obj) {
|
||||
delete reinterpret_cast<TestItem*>(obj);
|
||||
obj = nullptr;
|
||||
}
|
||||
|
||||
static Cache::CacheItemHelper helper_;
|
||||
|
||||
static Status SaveToCallbackFail(void* /*obj*/, size_t /*offset*/,
|
||||
size_t /*size*/, void* /*out*/) {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
static Cache::CacheItemHelper helper_fail_;
|
||||
|
||||
Cache::CreateCallback test_item_creator = [&](const void* buf, size_t size,
|
||||
void** out_obj,
|
||||
size_t* charge) -> Status {
|
||||
if (fail_create_) {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
*out_obj = reinterpret_cast<void*>(new TestItem((char*)buf, size));
|
||||
*charge = size;
|
||||
return Status::OK();
|
||||
};
|
||||
|
||||
void SetFailCreate(bool fail) { fail_create_ = fail; }
|
||||
|
||||
void BasicTest(bool sec_cache_is_compressed, bool use_jemalloc) {
|
||||
LRUSecondaryCacheOptions opts;
|
||||
opts.capacity = 2048;
|
||||
opts.num_shard_bits = 0;
|
||||
opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
if (use_jemalloc) {
|
||||
JemallocAllocatorOptions jopts;
|
||||
std::shared_ptr<MemoryAllocator> allocator;
|
||||
std::string msg;
|
||||
if (JemallocNodumpAllocator::IsSupported(&msg)) {
|
||||
Status s = NewJemallocNodumpAllocator(jopts, &allocator);
|
||||
if (s.ok()) {
|
||||
opts.memory_allocator = allocator;
|
||||
}
|
||||
} else {
|
||||
ROCKSDB_GTEST_BYPASS("JEMALLOC not supported");
|
||||
}
|
||||
}
|
||||
std::shared_ptr<SecondaryCache> cache = NewLRUSecondaryCache(opts);
|
||||
|
||||
// Lookup an non-existent key.
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle0 =
|
||||
cache->Lookup("k0", test_item_creator, true);
|
||||
ASSERT_EQ(handle0, nullptr);
|
||||
|
||||
Random rnd(301);
|
||||
// Insert and Lookup the first item.
|
||||
std::string str1;
|
||||
test::CompressibleString(&rnd, 0.25, 1000, &str1);
|
||||
TestItem item1(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", &item1, &LRUSecondaryCacheTest::helper_));
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1 =
|
||||
cache->Lookup("k1", test_item_creator, true);
|
||||
ASSERT_NE(handle1, nullptr);
|
||||
// delete reinterpret_cast<TestItem*>(handle1->Value());
|
||||
std::unique_ptr<TestItem> val1 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle1->Value()));
|
||||
ASSERT_NE(val1, nullptr);
|
||||
ASSERT_EQ(memcmp(val1->Buf(), item1.Buf(), item1.Size()), 0);
|
||||
|
||||
// Insert and Lookup the second item.
|
||||
std::string str2;
|
||||
test::CompressibleString(&rnd, 0.5, 1000, &str2);
|
||||
TestItem item2(str2.data(), str2.length());
|
||||
ASSERT_OK(cache->Insert("k2", &item2, &LRUSecondaryCacheTest::helper_));
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle2 =
|
||||
cache->Lookup("k2", test_item_creator, true);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
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);
|
||||
|
||||
// Lookup the first item again to make sure it is still in the cache.
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
|
||||
cache->Lookup("k1", test_item_creator, true);
|
||||
ASSERT_NE(handle1_1, nullptr);
|
||||
std::unique_ptr<TestItem> val1_1 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle1_1->Value()));
|
||||
ASSERT_NE(val1_1, nullptr);
|
||||
ASSERT_EQ(memcmp(val1_1->Buf(), item1.Buf(), item1.Size()), 0);
|
||||
|
||||
std::vector<SecondaryCacheResultHandle*> handles = {handle1.get(),
|
||||
handle2.get()};
|
||||
cache->WaitAll(handles);
|
||||
|
||||
cache->Erase("k1");
|
||||
handle1 = cache->Lookup("k1", test_item_creator, true);
|
||||
ASSERT_EQ(handle1, nullptr);
|
||||
|
||||
cache.reset();
|
||||
}
|
||||
|
||||
void FailsTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
secondary_cache_opts.capacity = 1100;
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
std::shared_ptr<SecondaryCache> cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
|
||||
// Insert and Lookup the first item.
|
||||
Random rnd(301);
|
||||
std::string str1(rnd.RandomString(1000));
|
||||
TestItem item1(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", &item1, &LRUSecondaryCacheTest::helper_));
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1 =
|
||||
cache->Lookup("k1", test_item_creator, true);
|
||||
ASSERT_NE(handle1, nullptr);
|
||||
std::unique_ptr<TestItem> val1 =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle1->Value()));
|
||||
ASSERT_NE(val1, nullptr);
|
||||
ASSERT_EQ(memcmp(val1->Buf(), item1.Buf(), item1.Size()), 0);
|
||||
|
||||
// Insert and Lookup the second item.
|
||||
std::string str2(rnd.RandomString(200));
|
||||
TestItem item2(str2.data(), str2.length());
|
||||
// k1 is evicted.
|
||||
ASSERT_OK(cache->Insert("k2", &item2, &LRUSecondaryCacheTest::helper_));
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
|
||||
cache->Lookup("k1", test_item_creator, true);
|
||||
ASSERT_EQ(handle1_1, nullptr);
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle2 =
|
||||
cache->Lookup("k2", test_item_creator, true);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
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);
|
||||
|
||||
// Create Fails.
|
||||
SetFailCreate(true);
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle2_1 =
|
||||
cache->Lookup("k2", test_item_creator, true);
|
||||
ASSERT_EQ(handle2_1, nullptr);
|
||||
|
||||
// Save Fails.
|
||||
std::string str3 = rnd.RandomString(10);
|
||||
TestItem item3(str3.data(), str3.length());
|
||||
ASSERT_NOK(
|
||||
cache->Insert("k3", &item3, &LRUSecondaryCacheTest::helper_fail_));
|
||||
|
||||
cache.reset();
|
||||
}
|
||||
|
||||
void BasicIntegrationTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
secondary_cache_opts.capacity = 2300;
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
LRUCacheOptions lru_cache_opts(1024, 0, false, 0.5, nullptr,
|
||||
kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
lru_cache_opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lru_cache_opts);
|
||||
std::shared_ptr<Statistics> stats = CreateDBStatistics();
|
||||
|
||||
Random rnd(301);
|
||||
|
||||
std::string str1 = rnd.RandomString(1010);
|
||||
std::string str1_clone{str1};
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// After Insert, lru cache contains k2 and secondary cache contains k1.
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_,
|
||||
str2.length()));
|
||||
|
||||
std::string str3 = rnd.RandomString(1020);
|
||||
TestItem* item3 = new TestItem(str3.data(), str3.length());
|
||||
// After Insert, lru cache contains k3 and secondary cache contains k1 and
|
||||
// k2
|
||||
ASSERT_OK(cache->Insert("k3", item3, &LRUSecondaryCacheTest::helper_,
|
||||
str3.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle =
|
||||
cache->Lookup("k3", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_NE(handle, nullptr);
|
||||
TestItem* val3 = static_cast<TestItem*>(cache->Value(handle));
|
||||
ASSERT_NE(val3, nullptr);
|
||||
ASSERT_EQ(memcmp(val3->Buf(), item3->Buf(), item3->Size()), 0);
|
||||
cache->Release(handle);
|
||||
|
||||
// Lookup an non-existent key.
|
||||
handle =
|
||||
cache->Lookup("k0", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
|
||||
// This Lookup should promote k1 and demote k3, so k2 is evicted from the
|
||||
// secondary cache. The lru cache contains k1 and secondary cache contains
|
||||
// k3. item1 was Free(), so it cannot be compared against the item1.
|
||||
handle =
|
||||
cache->Lookup("k1", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_NE(handle, nullptr);
|
||||
TestItem* val1_1 = static_cast<TestItem*>(cache->Value(handle));
|
||||
ASSERT_NE(val1_1, nullptr);
|
||||
ASSERT_EQ(memcmp(val1_1->Buf(), str1_clone.data(), str1_clone.size()), 0);
|
||||
cache->Release(handle);
|
||||
|
||||
handle =
|
||||
cache->Lookup("k2", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
void BasicIntegrationFailTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
secondary_cache_opts.capacity = 2048;
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
|
||||
LRUCacheOptions opts(1024, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(opts);
|
||||
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
auto item1 =
|
||||
std::unique_ptr<TestItem>(new TestItem(str1.data(), str1.length()));
|
||||
ASSERT_NOK(cache->Insert("k1", item1.get(), nullptr, str1.length()));
|
||||
ASSERT_OK(cache->Insert("k1", item1.get(), &LRUSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
item1.release(); // Appease clang-analyze "potential memory leak"
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup("k2", nullptr, test_item_creator,
|
||||
Cache::Priority::LOW, true);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, false);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
void IntegrationSaveFailTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
secondary_cache_opts.capacity = 2048;
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
|
||||
LRUCacheOptions opts(1024, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(opts);
|
||||
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_fail_,
|
||||
str1.length()));
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k1 should be demoted to the secondary cache.
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_fail_,
|
||||
str2.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_fail_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
// This lookup should fail, since k1 demotion would have failed
|
||||
handle = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_fail_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
// Since k1 didn't get promoted, k2 should still be in cache
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_fail_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
void IntegrationCreateFailTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
secondary_cache_opts.capacity = 2048;
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
|
||||
LRUCacheOptions opts(1024, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(opts);
|
||||
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k1 should be demoted to the secondary cache.
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_,
|
||||
str2.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
SetFailCreate(true);
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
// This lookup should fail, since k1 creation would have failed
|
||||
handle = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_EQ(handle, nullptr);
|
||||
// Since k1 didn't get promoted, k2 should still be in cache
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
void IntegrationFullCapacityTest(bool sec_cache_is_compressed) {
|
||||
LRUSecondaryCacheOptions secondary_cache_opts;
|
||||
|
||||
if (sec_cache_is_compressed) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
} else {
|
||||
secondary_cache_opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
secondary_cache_opts.capacity = 2048;
|
||||
secondary_cache_opts.num_shard_bits = 0;
|
||||
secondary_cache_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache =
|
||||
NewLRUSecondaryCache(secondary_cache_opts);
|
||||
|
||||
LRUCacheOptions opts(1024, 0, /*_strict_capacity_limit=*/true, 0.5, nullptr,
|
||||
kDefaultToAdaptiveMutex, kDontChargeCacheMetadata);
|
||||
opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(opts);
|
||||
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert("k1", item1, &LRUSecondaryCacheTest::helper_,
|
||||
str1.length()));
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k1 should be demoted to the secondary cache.
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_,
|
||||
str2.length()));
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
// k1 promotion should fail due to the block cache being at capacity,
|
||||
// but the lookup should still succeed
|
||||
Cache::Handle* handle2;
|
||||
handle2 = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
// Since k1 didn't get inserted, k2 should still be in cache
|
||||
cache->Release(handle);
|
||||
cache->Release(handle2);
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
bool fail_create_;
|
||||
};
|
||||
|
||||
Cache::CacheItemHelper LRUSecondaryCacheTest::helper_(
|
||||
LRUSecondaryCacheTest::SizeCallback, LRUSecondaryCacheTest::SaveToCallback,
|
||||
LRUSecondaryCacheTest::DeletionCallback);
|
||||
|
||||
Cache::CacheItemHelper LRUSecondaryCacheTest::helper_fail_(
|
||||
LRUSecondaryCacheTest::SizeCallback,
|
||||
LRUSecondaryCacheTest::SaveToCallbackFail,
|
||||
LRUSecondaryCacheTest::DeletionCallback);
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicTestWithNoCompression) {
|
||||
BasicTest(false, false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicTestWithMemoryAllocatorAndNoCompression) {
|
||||
BasicTest(false, true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicTestWithCompression) {
|
||||
BasicTest(true, false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicTestWithMemoryAllocatorAndCompression) {
|
||||
BasicTest(true, true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, FailsTestWithNoCompression) { FailsTest(false); }
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, FailsTestWithCompression) { FailsTest(true); }
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicIntegrationTestWithNoCompression) {
|
||||
BasicIntegrationTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicIntegrationTestWithCompression) {
|
||||
BasicIntegrationTest(true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicIntegrationFailTestWithNoCompression) {
|
||||
BasicIntegrationFailTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicIntegrationFailTestWithCompression) {
|
||||
BasicIntegrationFailTest(true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationSaveFailTestWithNoCompression) {
|
||||
IntegrationSaveFailTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationSaveFailTestWithCompression) {
|
||||
IntegrationSaveFailTest(true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationCreateFailTestWithNoCompression) {
|
||||
IntegrationCreateFailTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationCreateFailTestWithCompression) {
|
||||
IntegrationCreateFailTest(true);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationFullCapacityTestWithNoCompression) {
|
||||
IntegrationFullCapacityTest(false);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, IntegrationFullCapacityTestWithCompression) {
|
||||
IntegrationFullCapacityTest(true);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Vendored
-740
@@ -1,740 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "cache/secondary_cache_adapter.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "cache/tiered_secondary_cache.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/cast_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
// A distinct pointer value for marking "dummy" cache entries
|
||||
struct Dummy {
|
||||
char val[7] = "kDummy";
|
||||
};
|
||||
const Dummy kDummy{};
|
||||
Cache::ObjectPtr const kDummyObj = const_cast<Dummy*>(&kDummy);
|
||||
const char* kTieredCacheName = "TieredCache";
|
||||
} // namespace
|
||||
|
||||
// When CacheWithSecondaryAdapter is constructed with the distribute_cache_res
|
||||
// parameter set to true, it manages the entire memory budget across the
|
||||
// primary and secondary cache. The secondary cache is assumed to be in
|
||||
// memory, such as the CompressedSecondaryCache. When a placeholder entry
|
||||
// is inserted by a CacheReservationManager instance to reserve memory,
|
||||
// the CacheWithSecondaryAdapter ensures that the reservation is distributed
|
||||
// proportionally across the primary/secondary caches.
|
||||
//
|
||||
// The primary block cache is initially sized to the sum of the primary cache
|
||||
// budget + teh secondary cache budget, as follows -
|
||||
// |--------- Primary Cache Configured Capacity -----------|
|
||||
// |---Secondary Cache Budget----|----Primary Cache Budget-----|
|
||||
//
|
||||
// A ConcurrentCacheReservationManager member in the CacheWithSecondaryAdapter,
|
||||
// pri_cache_res_,
|
||||
// is used to help with tracking the distribution of memory reservations.
|
||||
// Initially, it accounts for the entire secondary cache budget as a
|
||||
// reservation against the primary cache. This shrinks the usable capacity of
|
||||
// the primary cache to the budget that the user originally desired.
|
||||
//
|
||||
// |--Reservation for Sec Cache--|-Pri Cache Usable Capacity---|
|
||||
//
|
||||
// When a reservation placeholder is inserted into the adapter, it is inserted
|
||||
// directly into the primary cache. This means the entire charge of the
|
||||
// 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 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.
|
||||
//
|
||||
// For example, if the pri/sec ratio is 70/30, and the combined capacity is
|
||||
// 100MB, the intermediate and final state after inserting a reservation
|
||||
// placeholder for 10MB would be as follows -
|
||||
//
|
||||
// |-Reservation for Sec Cache-|-Pri Cache Usable Capacity-|---R---|
|
||||
// 1. After inserting the placeholder in primary
|
||||
// |------- 30MB -------------|------- 60MB -------------|-10MB--|
|
||||
// 2. After deflating the secondary and adjusting the reservation for
|
||||
// secondary against the primary
|
||||
// |------- 27MB -------------|------- 63MB -------------|-10MB--|
|
||||
//
|
||||
// Likewise, when the user inserted placeholder is released, the secondary
|
||||
// cache Inflate() method is called to grow it, and the pri_cache_res_
|
||||
// reservation is increased by an equal amount.
|
||||
//
|
||||
// Another way of implementing this would have been to simply split the user
|
||||
// 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.
|
||||
//
|
||||
CacheWithSecondaryAdapter::CacheWithSecondaryAdapter(
|
||||
std::shared_ptr<Cache> target,
|
||||
std::shared_ptr<SecondaryCache> secondary_cache,
|
||||
TieredAdmissionPolicy adm_policy, bool distribute_cache_res)
|
||||
: CacheWrapper(std::move(target)),
|
||||
secondary_cache_(std::move(secondary_cache)),
|
||||
adm_policy_(adm_policy),
|
||||
distribute_cache_res_(distribute_cache_res),
|
||||
placeholder_usage_(0),
|
||||
reserved_usage_(0),
|
||||
sec_reserved_(0) {
|
||||
target_->SetEvictionCallback(
|
||||
[this](const Slice& key, Handle* handle, bool was_hit) {
|
||||
return EvictionHandler(key, handle, was_hit);
|
||||
});
|
||||
if (distribute_cache_res_) {
|
||||
size_t sec_capacity = 0;
|
||||
pri_cache_res_ = std::make_shared<ConcurrentCacheReservationManager>(
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
target_));
|
||||
Status s = secondary_cache_->GetCapacity(sec_capacity);
|
||||
assert(s.ok());
|
||||
// Initially, the primary cache is sized to uncompressed cache budget plsu
|
||||
// compressed secondary cache budget. The secondary cache budget is then
|
||||
// taken away from the primary cache through cache reservations. Later,
|
||||
// when a placeholder entry is inserted by the caller, its inserted
|
||||
// into the primary cache and the portion that should be assigned to the
|
||||
// secondary cache is freed from the reservation.
|
||||
s = pri_cache_res_->UpdateCacheReservation(sec_capacity);
|
||||
assert(s.ok());
|
||||
sec_cache_res_ratio_ = (double)sec_capacity / target_->GetCapacity();
|
||||
}
|
||||
}
|
||||
|
||||
CacheWithSecondaryAdapter::~CacheWithSecondaryAdapter() {
|
||||
// `*this` will be destroyed before `*target_`, so we have to prevent
|
||||
// use after free
|
||||
target_->SetEvictionCallback({});
|
||||
#ifndef NDEBUG
|
||||
if (distribute_cache_res_) {
|
||||
size_t sec_capacity = 0;
|
||||
Status s = secondary_cache_->GetCapacity(sec_capacity);
|
||||
assert(s.ok());
|
||||
assert(placeholder_usage_ == 0);
|
||||
assert(reserved_usage_ == 0);
|
||||
assert(pri_cache_res_->GetTotalMemoryUsed() == sec_capacity);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
|
||||
bool CacheWithSecondaryAdapter::EvictionHandler(const Slice& key,
|
||||
Handle* handle, bool was_hit) {
|
||||
auto helper = GetCacheItemHelper(handle);
|
||||
if (helper->IsSecondaryCacheCompatible() &&
|
||||
adm_policy_ != TieredAdmissionPolicy::kAdmPolicyThreeQueue) {
|
||||
auto obj = target_->Value(handle);
|
||||
// Ignore dummy entry
|
||||
if (obj != kDummyObj) {
|
||||
bool hit = false;
|
||||
if (adm_policy_ == TieredAdmissionPolicy::kAdmPolicyAllowCacheHits) {
|
||||
hit = was_hit;
|
||||
}
|
||||
// Spill into secondary cache.
|
||||
secondary_cache_->Insert(key, obj, helper, hit).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
// Never takes ownership of obj
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CacheWithSecondaryAdapter::ProcessDummyResult(Cache::Handle** handle,
|
||||
bool erase) {
|
||||
if (*handle && target_->Value(*handle) == kDummyObj) {
|
||||
target_->Release(*handle, erase);
|
||||
*handle = nullptr;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CacheWithSecondaryAdapter::CleanupCacheObject(
|
||||
ObjectPtr obj, const CacheItemHelper* helper) {
|
||||
if (helper->del_cb) {
|
||||
helper->del_cb(obj, memory_allocator());
|
||||
}
|
||||
}
|
||||
|
||||
Cache::Handle* CacheWithSecondaryAdapter::Promote(
|
||||
std::unique_ptr<SecondaryCacheResultHandle>&& secondary_handle,
|
||||
const Slice& key, const CacheItemHelper* helper, Priority priority,
|
||||
Statistics* stats, bool found_dummy_entry, bool kept_in_sec_cache) {
|
||||
assert(secondary_handle->IsReady());
|
||||
|
||||
ObjectPtr obj = secondary_handle->Value();
|
||||
if (!obj) {
|
||||
// Nothing found.
|
||||
return nullptr;
|
||||
}
|
||||
// Found something.
|
||||
switch (helper->role) {
|
||||
case CacheEntryRole::kFilterBlock:
|
||||
RecordTick(stats, SECONDARY_CACHE_FILTER_HITS);
|
||||
break;
|
||||
case CacheEntryRole::kIndexBlock:
|
||||
RecordTick(stats, SECONDARY_CACHE_INDEX_HITS);
|
||||
break;
|
||||
case CacheEntryRole::kDataBlock:
|
||||
RecordTick(stats, SECONDARY_CACHE_DATA_HITS);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
PERF_COUNTER_ADD(secondary_cache_hit_count, 1);
|
||||
RecordTick(stats, SECONDARY_CACHE_HITS);
|
||||
|
||||
// Note: SecondaryCache::Size() is really charge (from the CreateCallback)
|
||||
size_t charge = secondary_handle->Size();
|
||||
Handle* result = nullptr;
|
||||
// Insert into primary cache, possibly as a standalone+dummy entries.
|
||||
if (secondary_cache_->SupportForceErase() && !found_dummy_entry) {
|
||||
// Create standalone and insert dummy
|
||||
// Allow standalone to be created even if cache is full, to avoid
|
||||
// reading the entry from storage.
|
||||
result =
|
||||
CreateStandalone(key, obj, helper, charge, /*allow_uncharged*/ true);
|
||||
assert(result);
|
||||
PERF_COUNTER_ADD(block_cache_standalone_handle_count, 1);
|
||||
|
||||
// Insert dummy to record recent use
|
||||
// TODO: try to avoid case where inserting this dummy could overwrite a
|
||||
// regular entry
|
||||
Status s = Insert(key, kDummyObj, &kNoopCacheItemHelper, /*charge=*/0,
|
||||
/*handle=*/nullptr, priority);
|
||||
s.PermitUncheckedError();
|
||||
// Nothing to do or clean up on dummy insertion failure
|
||||
} else {
|
||||
// Insert regular entry into primary cache.
|
||||
// Don't allow it to spill into secondary cache again if it was kept there.
|
||||
Status s = Insert(
|
||||
key, obj, kept_in_sec_cache ? helper->without_secondary_compat : helper,
|
||||
charge, &result, priority);
|
||||
if (s.ok()) {
|
||||
assert(result);
|
||||
PERF_COUNTER_ADD(block_cache_real_handle_count, 1);
|
||||
} else {
|
||||
// Create standalone result instead, even if cache is full, to avoid
|
||||
// reading the entry from storage.
|
||||
result =
|
||||
CreateStandalone(key, obj, helper, charge, /*allow_uncharged*/ true);
|
||||
assert(result);
|
||||
PERF_COUNTER_ADD(block_cache_standalone_handle_count, 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Status CacheWithSecondaryAdapter::Insert(const Slice& key, ObjectPtr value,
|
||||
const CacheItemHelper* helper,
|
||||
size_t charge, Handle** handle,
|
||||
Priority priority,
|
||||
const Slice& compressed_value,
|
||||
CompressionType type) {
|
||||
Status s = target_->Insert(key, value, helper, charge, handle, priority);
|
||||
if (s.ok() && value == nullptr && distribute_cache_res_ && handle) {
|
||||
charge = target_->GetCharge(*handle);
|
||||
|
||||
MutexLock l(&cache_res_mutex_);
|
||||
placeholder_usage_ += charge;
|
||||
// Check if total placeholder reservation is more than the overall
|
||||
// cache capacity. If it is, then we don't try to charge the
|
||||
// secondary cache because we don't want to overcharge it (beyond
|
||||
// its capacity).
|
||||
// In order to make this a bit more lightweight, we also check if
|
||||
// the difference between placeholder_usage_ and reserved_usage_ is
|
||||
// atleast kReservationChunkSize and avoid any adjustments if not.
|
||||
if ((placeholder_usage_ <= target_->GetCapacity()) &&
|
||||
((placeholder_usage_ - reserved_usage_) >= kReservationChunkSize)) {
|
||||
reserved_usage_ = placeholder_usage_ & ~(kReservationChunkSize - 1);
|
||||
size_t new_sec_reserved =
|
||||
static_cast<size_t>(reserved_usage_ * sec_cache_res_ratio_);
|
||||
size_t sec_charge = new_sec_reserved - sec_reserved_;
|
||||
s = secondary_cache_->Deflate(sec_charge);
|
||||
assert(s.ok());
|
||||
s = pri_cache_res_->UpdateCacheReservation(sec_charge,
|
||||
/*increase=*/false);
|
||||
assert(s.ok());
|
||||
sec_reserved_ += sec_charge;
|
||||
}
|
||||
}
|
||||
// Warm up the secondary cache with the compressed block. The secondary
|
||||
// cache may choose to ignore it based on the admission policy.
|
||||
if (value != nullptr && !compressed_value.empty() &&
|
||||
adm_policy_ == TieredAdmissionPolicy::kAdmPolicyThreeQueue) {
|
||||
Status status = secondary_cache_->InsertSaved(key, compressed_value, type);
|
||||
assert(status.ok() || status.IsNotSupported());
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Cache::Handle* CacheWithSecondaryAdapter::Lookup(const Slice& key,
|
||||
const CacheItemHelper* helper,
|
||||
CreateContext* create_context,
|
||||
Priority priority,
|
||||
Statistics* stats) {
|
||||
// NOTE: we could just StartAsyncLookup() and Wait(), but this should be a bit
|
||||
// more efficient
|
||||
Handle* result =
|
||||
target_->Lookup(key, helper, create_context, priority, stats);
|
||||
bool secondary_compatible = helper && helper->IsSecondaryCacheCompatible();
|
||||
bool found_dummy_entry =
|
||||
ProcessDummyResult(&result, /*erase=*/secondary_compatible);
|
||||
if (!result && secondary_compatible) {
|
||||
// Try our secondary cache
|
||||
bool kept_in_sec_cache = false;
|
||||
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle =
|
||||
secondary_cache_->Lookup(key, helper, create_context, /*wait*/ true,
|
||||
found_dummy_entry, stats,
|
||||
/*out*/ kept_in_sec_cache);
|
||||
if (secondary_handle) {
|
||||
result = Promote(std::move(secondary_handle), key, helper, priority,
|
||||
stats, found_dummy_entry, kept_in_sec_cache);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CacheWithSecondaryAdapter::Release(Handle* handle,
|
||||
bool erase_if_last_ref) {
|
||||
if (erase_if_last_ref) {
|
||||
ObjectPtr v = target_->Value(handle);
|
||||
if (v == nullptr && distribute_cache_res_) {
|
||||
size_t charge = target_->GetCharge(handle);
|
||||
|
||||
MutexLock l(&cache_res_mutex_);
|
||||
placeholder_usage_ -= charge;
|
||||
// Check if total placeholder reservation is more than the overall
|
||||
// cache capacity. If it is, then we do nothing as reserved_usage_ must
|
||||
// be already maxed out
|
||||
if ((placeholder_usage_ <= target_->GetCapacity()) &&
|
||||
(placeholder_usage_ < reserved_usage_)) {
|
||||
// Adjust reserved_usage_ in chunks of kReservationChunkSize, so
|
||||
// we don't hit this slow path too often.
|
||||
reserved_usage_ = placeholder_usage_ & ~(kReservationChunkSize - 1);
|
||||
size_t new_sec_reserved =
|
||||
static_cast<size_t>(reserved_usage_ * sec_cache_res_ratio_);
|
||||
size_t sec_charge = sec_reserved_ - new_sec_reserved;
|
||||
Status s = secondary_cache_->Inflate(sec_charge);
|
||||
assert(s.ok());
|
||||
s = pri_cache_res_->UpdateCacheReservation(sec_charge,
|
||||
/*increase=*/true);
|
||||
assert(s.ok());
|
||||
sec_reserved_ -= sec_charge;
|
||||
}
|
||||
}
|
||||
}
|
||||
return target_->Release(handle, erase_if_last_ref);
|
||||
}
|
||||
|
||||
Cache::ObjectPtr CacheWithSecondaryAdapter::Value(Handle* handle) {
|
||||
ObjectPtr v = target_->Value(handle);
|
||||
// TODO with stacked secondaries: might fail in EvictionHandler
|
||||
assert(v != kDummyObj);
|
||||
return v;
|
||||
}
|
||||
|
||||
void CacheWithSecondaryAdapter::StartAsyncLookupOnMySecondary(
|
||||
AsyncLookupHandle& async_handle) {
|
||||
assert(!async_handle.IsPending());
|
||||
assert(async_handle.result_handle == nullptr);
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle =
|
||||
secondary_cache_->Lookup(
|
||||
async_handle.key, async_handle.helper, async_handle.create_context,
|
||||
/*wait*/ false, async_handle.found_dummy_entry, async_handle.stats,
|
||||
/*out*/ async_handle.kept_in_sec_cache);
|
||||
if (secondary_handle) {
|
||||
// TODO with stacked secondaries: Check & process if already ready?
|
||||
async_handle.pending_handle = secondary_handle.release();
|
||||
async_handle.pending_cache = secondary_cache_.get();
|
||||
}
|
||||
}
|
||||
|
||||
void CacheWithSecondaryAdapter::StartAsyncLookup(
|
||||
AsyncLookupHandle& async_handle) {
|
||||
target_->StartAsyncLookup(async_handle);
|
||||
if (!async_handle.IsPending()) {
|
||||
bool secondary_compatible =
|
||||
async_handle.helper &&
|
||||
async_handle.helper->IsSecondaryCacheCompatible();
|
||||
async_handle.found_dummy_entry |= ProcessDummyResult(
|
||||
&async_handle.result_handle, /*erase=*/secondary_compatible);
|
||||
|
||||
if (async_handle.Result() == nullptr && secondary_compatible) {
|
||||
// Not found and not pending on another secondary cache
|
||||
StartAsyncLookupOnMySecondary(async_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CacheWithSecondaryAdapter::WaitAll(AsyncLookupHandle* async_handles,
|
||||
size_t count) {
|
||||
if (count == 0) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
// Requests that are pending on *my* secondary cache, at the start of this
|
||||
// function
|
||||
std::vector<AsyncLookupHandle*> my_pending;
|
||||
// Requests that are pending on an "inner" secondary cache (managed somewhere
|
||||
// under target_), as of the start of this function
|
||||
std::vector<AsyncLookupHandle*> inner_pending;
|
||||
|
||||
// Initial accounting of pending handles, excluding those already handled
|
||||
// by "outer" secondary caches. (See cur->pending_cache = nullptr.)
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
AsyncLookupHandle* cur = async_handles + i;
|
||||
if (cur->pending_cache) {
|
||||
assert(cur->IsPending());
|
||||
assert(cur->helper);
|
||||
assert(cur->helper->IsSecondaryCacheCompatible());
|
||||
if (cur->pending_cache == secondary_cache_.get()) {
|
||||
my_pending.push_back(cur);
|
||||
// Mark as "to be handled by this caller"
|
||||
cur->pending_cache = nullptr;
|
||||
} else {
|
||||
// Remember as potentially needing a lookup in my secondary
|
||||
inner_pending.push_back(cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait on inner-most cache lookups first
|
||||
// TODO with stacked secondaries: because we are not using proper
|
||||
// async/await constructs here yet, there is a false synchronization point
|
||||
// here where all the results at one level are needed before initiating
|
||||
// any lookups at the next level. Probably not a big deal, but worth noting.
|
||||
if (!inner_pending.empty()) {
|
||||
target_->WaitAll(async_handles, count);
|
||||
}
|
||||
|
||||
// For those that failed to find something, convert to lookup in my
|
||||
// secondary cache.
|
||||
for (AsyncLookupHandle* cur : inner_pending) {
|
||||
if (cur->Result() == nullptr) {
|
||||
// Not found, try my secondary
|
||||
StartAsyncLookupOnMySecondary(*cur);
|
||||
if (cur->IsPending()) {
|
||||
assert(cur->pending_cache == secondary_cache_.get());
|
||||
my_pending.push_back(cur);
|
||||
// Mark as "to be handled by this caller"
|
||||
cur->pending_cache = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait on all lookups on my secondary cache
|
||||
{
|
||||
std::vector<SecondaryCacheResultHandle*> my_secondary_handles;
|
||||
for (AsyncLookupHandle* cur : my_pending) {
|
||||
my_secondary_handles.push_back(cur->pending_handle);
|
||||
}
|
||||
secondary_cache_->WaitAll(std::move(my_secondary_handles));
|
||||
}
|
||||
|
||||
// Process results
|
||||
for (AsyncLookupHandle* cur : my_pending) {
|
||||
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle(
|
||||
cur->pending_handle);
|
||||
cur->pending_handle = nullptr;
|
||||
cur->result_handle = Promote(
|
||||
std::move(secondary_handle), cur->key, cur->helper, cur->priority,
|
||||
cur->stats, cur->found_dummy_entry, cur->kept_in_sec_cache);
|
||||
assert(cur->pending_cache == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
std::string CacheWithSecondaryAdapter::GetPrintableOptions() const {
|
||||
std::string str = target_->GetPrintableOptions();
|
||||
str.append(" secondary_cache:\n");
|
||||
str.append(secondary_cache_->GetPrintableOptions());
|
||||
return str;
|
||||
}
|
||||
|
||||
const char* CacheWithSecondaryAdapter::Name() const {
|
||||
if (distribute_cache_res_) {
|
||||
return kTieredCacheName;
|
||||
} else {
|
||||
// To the user, at least for now, configure the underlying cache with
|
||||
// a secondary cache. So we pretend to be that cache
|
||||
return target_->Name();
|
||||
}
|
||||
}
|
||||
|
||||
// Update the total cache capacity. If we're distributing cache reservations
|
||||
// to both primary and secondary, then update the pri_cache_res_reservation
|
||||
// as well. At the moment, we don't have a good way of handling the case
|
||||
// where the new capacity < total cache reservations.
|
||||
void CacheWithSecondaryAdapter::SetCapacity(size_t capacity) {
|
||||
size_t sec_capacity = static_cast<size_t>(
|
||||
capacity * (distribute_cache_res_ ? sec_cache_res_ratio_ : 0.0));
|
||||
size_t old_sec_capacity = 0;
|
||||
|
||||
if (distribute_cache_res_) {
|
||||
MutexLock m(&cache_res_mutex_);
|
||||
|
||||
Status s = secondary_cache_->GetCapacity(old_sec_capacity);
|
||||
if (!s.ok()) {
|
||||
return;
|
||||
}
|
||||
if (old_sec_capacity > sec_capacity) {
|
||||
// We're shrinking the cache. We do things in the following order to
|
||||
// avoid a temporary spike in usage over the configured capacity -
|
||||
// 1. Lower the secondary cache capacity
|
||||
// 2. Credit an equal amount (by decreasing pri_cache_res_) to the
|
||||
// primary cache
|
||||
// 3. Decrease the primary cache capacity to the total budget
|
||||
s = secondary_cache_->SetCapacity(sec_capacity);
|
||||
if (s.ok()) {
|
||||
if (placeholder_usage_ > capacity) {
|
||||
// Adjust reserved_usage_ down
|
||||
reserved_usage_ = capacity & ~(kReservationChunkSize - 1);
|
||||
}
|
||||
size_t new_sec_reserved =
|
||||
static_cast<size_t>(reserved_usage_ * sec_cache_res_ratio_);
|
||||
s = pri_cache_res_->UpdateCacheReservation(
|
||||
(old_sec_capacity - sec_capacity) -
|
||||
(sec_reserved_ - new_sec_reserved),
|
||||
/*increase=*/false);
|
||||
sec_reserved_ = new_sec_reserved;
|
||||
assert(s.ok());
|
||||
target_->SetCapacity(capacity);
|
||||
}
|
||||
} else {
|
||||
// We're expanding the cache. Do it in the following order to avoid
|
||||
// unnecessary evictions -
|
||||
// 1. Increase the primary cache capacity to total budget
|
||||
// 2. Reserve additional memory in primary on behalf of secondary (by
|
||||
// increasing pri_cache_res_ reservation)
|
||||
// 3. Increase secondary cache capacity
|
||||
target_->SetCapacity(capacity);
|
||||
s = pri_cache_res_->UpdateCacheReservation(
|
||||
sec_capacity - old_sec_capacity,
|
||||
/*increase=*/true);
|
||||
assert(s.ok());
|
||||
s = secondary_cache_->SetCapacity(sec_capacity);
|
||||
assert(s.ok());
|
||||
}
|
||||
} else {
|
||||
// No cache reservation distribution. Just set the primary cache capacity.
|
||||
target_->SetCapacity(capacity);
|
||||
}
|
||||
}
|
||||
|
||||
Status CacheWithSecondaryAdapter::GetSecondaryCacheCapacity(
|
||||
size_t& size) const {
|
||||
return secondary_cache_->GetCapacity(size);
|
||||
}
|
||||
|
||||
Status CacheWithSecondaryAdapter::GetSecondaryCachePinnedUsage(
|
||||
size_t& size) const {
|
||||
Status s;
|
||||
if (distribute_cache_res_) {
|
||||
MutexLock m(&cache_res_mutex_);
|
||||
size_t capacity = 0;
|
||||
s = secondary_cache_->GetCapacity(capacity);
|
||||
if (s.ok()) {
|
||||
size = capacity - pri_cache_res_->GetTotalMemoryUsed();
|
||||
} else {
|
||||
size = 0;
|
||||
}
|
||||
} else {
|
||||
size = 0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// Update the secondary/primary allocation ratio (remember, the primary
|
||||
// capacity is the total memory budget when distribute_cache_res_ is true).
|
||||
// When the ratio changes, we may accumulate some error in the calculations
|
||||
// for secondary cache inflate/deflate and pri_cache_res_ reservations.
|
||||
// This is due to the rounding of the reservation amount.
|
||||
//
|
||||
// We rely on the current pri_cache_res_ total memory used to estimate the
|
||||
// new secondary cache reservation after the ratio change. For this reason,
|
||||
// once the ratio is lowered to 0.0 (effectively disabling the secondary
|
||||
// cache and pri_cache_res_ total mem used going down to 0), we cannot
|
||||
// increase the ratio and re-enable it, We might remove this limitation
|
||||
// in the future.
|
||||
Status CacheWithSecondaryAdapter::UpdateCacheReservationRatio(
|
||||
double compressed_secondary_ratio) {
|
||||
if (!distribute_cache_res_) {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
MutexLock m(&cache_res_mutex_);
|
||||
size_t pri_capacity = target_->GetCapacity();
|
||||
size_t sec_capacity =
|
||||
static_cast<size_t>(pri_capacity * compressed_secondary_ratio);
|
||||
size_t old_sec_capacity;
|
||||
Status s = secondary_cache_->GetCapacity(old_sec_capacity);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// Calculate the new secondary cache reservation
|
||||
// reserved_usage_ will never be > the cache capacity, so we don't
|
||||
// have to worry about adjusting it here.
|
||||
sec_cache_res_ratio_ = compressed_secondary_ratio;
|
||||
size_t new_sec_reserved =
|
||||
static_cast<size_t>(reserved_usage_ * sec_cache_res_ratio_);
|
||||
if (sec_capacity > old_sec_capacity) {
|
||||
// We're increasing the ratio, thus ending up with a larger secondary
|
||||
// cache and a smaller usable primary cache capacity. Similar to
|
||||
// SetCapacity(), we try to avoid a temporary increase in total usage
|
||||
// beyond the configured capacity -
|
||||
// 1. A higher secondary cache ratio means it gets a higher share of
|
||||
// cache reservations. So first account for that by deflating the
|
||||
// secondary cache
|
||||
// 2. Increase pri_cache_res_ reservation to reflect the new secondary
|
||||
// cache utilization (increase in capacity - increase in share of cache
|
||||
// reservation)
|
||||
// 3. Increase secondary cache capacity
|
||||
s = secondary_cache_->Deflate(new_sec_reserved - sec_reserved_);
|
||||
assert(s.ok());
|
||||
s = pri_cache_res_->UpdateCacheReservation(
|
||||
(sec_capacity - old_sec_capacity) - (new_sec_reserved - sec_reserved_),
|
||||
/*increase=*/true);
|
||||
assert(s.ok());
|
||||
sec_reserved_ = new_sec_reserved;
|
||||
s = secondary_cache_->SetCapacity(sec_capacity);
|
||||
assert(s.ok());
|
||||
} else {
|
||||
// We're shrinking the ratio. Try to avoid unnecessary evictions -
|
||||
// 1. Lower the secondary cache capacity
|
||||
// 2. Decrease pri_cache_res_ reservation to relect lower secondary
|
||||
// cache utilization (decrease in capacity - decrease in share of cache
|
||||
// reservations)
|
||||
// 3. Inflate the secondary cache to give it back the reduction in its
|
||||
// share of cache reservations
|
||||
s = secondary_cache_->SetCapacity(sec_capacity);
|
||||
if (s.ok()) {
|
||||
s = pri_cache_res_->UpdateCacheReservation(
|
||||
(old_sec_capacity - sec_capacity) -
|
||||
(sec_reserved_ - new_sec_reserved),
|
||||
/*increase=*/false);
|
||||
assert(s.ok());
|
||||
s = secondary_cache_->Inflate(sec_reserved_ - new_sec_reserved);
|
||||
assert(s.ok());
|
||||
sec_reserved_ = new_sec_reserved;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Status CacheWithSecondaryAdapter::UpdateAdmissionPolicy(
|
||||
TieredAdmissionPolicy adm_policy) {
|
||||
adm_policy_ = adm_policy;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewTieredCache(const TieredCacheOptions& _opts) {
|
||||
if (!_opts.cache_opts) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TieredCacheOptions opts = _opts;
|
||||
{
|
||||
bool valid_adm_policy = true;
|
||||
|
||||
switch (_opts.adm_policy) {
|
||||
case TieredAdmissionPolicy::kAdmPolicyAuto:
|
||||
// Select an appropriate default policy
|
||||
if (opts.adm_policy == TieredAdmissionPolicy::kAdmPolicyAuto) {
|
||||
if (opts.nvm_sec_cache) {
|
||||
opts.adm_policy = TieredAdmissionPolicy::kAdmPolicyThreeQueue;
|
||||
} else {
|
||||
opts.adm_policy = TieredAdmissionPolicy::kAdmPolicyPlaceholder;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TieredAdmissionPolicy::kAdmPolicyPlaceholder:
|
||||
case TieredAdmissionPolicy::kAdmPolicyAllowCacheHits:
|
||||
if (opts.nvm_sec_cache) {
|
||||
valid_adm_policy = false;
|
||||
}
|
||||
break;
|
||||
case TieredAdmissionPolicy::kAdmPolicyThreeQueue:
|
||||
if (!opts.nvm_sec_cache) {
|
||||
valid_adm_policy = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
valid_adm_policy = false;
|
||||
}
|
||||
if (!valid_adm_policy) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> cache;
|
||||
if (opts.cache_type == PrimaryCacheType::kCacheTypeLRU) {
|
||||
LRUCacheOptions cache_opts =
|
||||
*(static_cast_with_check<LRUCacheOptions, ShardedCacheOptions>(
|
||||
opts.cache_opts));
|
||||
cache_opts.capacity = opts.total_capacity;
|
||||
cache_opts.secondary_cache = nullptr;
|
||||
cache = cache_opts.MakeSharedCache();
|
||||
} else if (opts.cache_type == PrimaryCacheType::kCacheTypeHCC) {
|
||||
HyperClockCacheOptions cache_opts =
|
||||
*(static_cast_with_check<HyperClockCacheOptions, ShardedCacheOptions>(
|
||||
opts.cache_opts));
|
||||
cache_opts.capacity = opts.total_capacity;
|
||||
cache_opts.secondary_cache = nullptr;
|
||||
cache = cache_opts.MakeSharedCache();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
std::shared_ptr<SecondaryCache> sec_cache;
|
||||
opts.comp_cache_opts.capacity = static_cast<size_t>(
|
||||
opts.total_capacity * opts.compressed_secondary_ratio);
|
||||
sec_cache = NewCompressedSecondaryCache(opts.comp_cache_opts);
|
||||
|
||||
if (opts.nvm_sec_cache) {
|
||||
if (opts.adm_policy == TieredAdmissionPolicy::kAdmPolicyThreeQueue) {
|
||||
sec_cache = std::make_shared<TieredSecondaryCache>(
|
||||
sec_cache, opts.nvm_sec_cache,
|
||||
TieredAdmissionPolicy::kAdmPolicyThreeQueue);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_shared<CacheWithSecondaryAdapter>(
|
||||
cache, sec_cache, opts.adm_policy, /*distribute_cache_res=*/true);
|
||||
}
|
||||
|
||||
Status UpdateTieredCache(const std::shared_ptr<Cache>& cache,
|
||||
int64_t total_capacity,
|
||||
double compressed_secondary_ratio,
|
||||
TieredAdmissionPolicy adm_policy) {
|
||||
if (!cache || strcmp(cache->Name(), kTieredCacheName)) {
|
||||
return Status::InvalidArgument();
|
||||
}
|
||||
CacheWithSecondaryAdapter* tiered_cache =
|
||||
static_cast<CacheWithSecondaryAdapter*>(cache.get());
|
||||
|
||||
Status s;
|
||||
if (total_capacity > 0) {
|
||||
tiered_cache->SetCapacity(total_capacity);
|
||||
}
|
||||
if (compressed_secondary_ratio >= 0.0 && compressed_secondary_ratio <= 1.0) {
|
||||
s = tiered_cache->UpdateCacheReservationRatio(compressed_secondary_ratio);
|
||||
}
|
||||
if (adm_policy < TieredAdmissionPolicy::kAdmPolicyMax) {
|
||||
s = tiered_cache->UpdateAdmissionPolicy(adm_policy);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
-103
@@ -1,103 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cache/cache_reservation_manager.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class CacheWithSecondaryAdapter : public CacheWrapper {
|
||||
public:
|
||||
explicit CacheWithSecondaryAdapter(
|
||||
std::shared_ptr<Cache> target,
|
||||
std::shared_ptr<SecondaryCache> secondary_cache,
|
||||
TieredAdmissionPolicy adm_policy = TieredAdmissionPolicy::kAdmPolicyAuto,
|
||||
bool distribute_cache_res = false);
|
||||
|
||||
~CacheWithSecondaryAdapter() override;
|
||||
|
||||
Status Insert(
|
||||
const Slice& key, ObjectPtr value, const CacheItemHelper* helper,
|
||||
size_t charge, Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW,
|
||||
const Slice& compressed_value = Slice(),
|
||||
CompressionType type = CompressionType::kNoCompression) override;
|
||||
|
||||
Handle* Lookup(const Slice& key, const CacheItemHelper* helper,
|
||||
CreateContext* create_context,
|
||||
Priority priority = Priority::LOW,
|
||||
Statistics* stats = nullptr) override;
|
||||
|
||||
using Cache::Release;
|
||||
bool Release(Handle* handle, bool erase_if_last_ref = false) override;
|
||||
|
||||
ObjectPtr Value(Handle* handle) override;
|
||||
|
||||
void StartAsyncLookup(AsyncLookupHandle& async_handle) override;
|
||||
|
||||
void WaitAll(AsyncLookupHandle* async_handles, size_t count) override;
|
||||
|
||||
std::string GetPrintableOptions() const override;
|
||||
|
||||
const char* Name() const override;
|
||||
|
||||
void SetCapacity(size_t capacity) override;
|
||||
|
||||
Status GetSecondaryCacheCapacity(size_t& size) const override;
|
||||
|
||||
Status GetSecondaryCachePinnedUsage(size_t& size) const override;
|
||||
|
||||
Status UpdateCacheReservationRatio(double ratio);
|
||||
|
||||
Status UpdateAdmissionPolicy(TieredAdmissionPolicy adm_policy);
|
||||
|
||||
Cache* TEST_GetCache() { return target_.get(); }
|
||||
|
||||
SecondaryCache* TEST_GetSecondaryCache() { return secondary_cache_.get(); }
|
||||
|
||||
private:
|
||||
static constexpr size_t kReservationChunkSize = 1 << 20;
|
||||
|
||||
bool EvictionHandler(const Slice& key, Handle* handle, bool was_hit);
|
||||
|
||||
void StartAsyncLookupOnMySecondary(AsyncLookupHandle& async_handle);
|
||||
|
||||
Handle* Promote(
|
||||
std::unique_ptr<SecondaryCacheResultHandle>&& secondary_handle,
|
||||
const Slice& key, const CacheItemHelper* helper, Priority priority,
|
||||
Statistics* stats, bool found_dummy_entry, bool kept_in_sec_cache);
|
||||
|
||||
bool ProcessDummyResult(Cache::Handle** handle, bool erase);
|
||||
|
||||
void CleanupCacheObject(ObjectPtr obj, const CacheItemHelper* helper);
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache_;
|
||||
TieredAdmissionPolicy adm_policy_;
|
||||
// Whether to proportionally distribute cache memory reservations, i.e
|
||||
// placeholder entries with null value and a non-zero charge, across
|
||||
// the primary and secondary caches.
|
||||
bool distribute_cache_res_;
|
||||
// A cache reservation manager to keep track of secondary cache memory
|
||||
// usage by reserving equivalent capacity against the primary cache
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> pri_cache_res_;
|
||||
// Fraction of a cache memory reservation to be assigned to the secondary
|
||||
// cache
|
||||
double sec_cache_res_ratio_;
|
||||
// Mutex for use when managing cache memory reservations. Should not be used
|
||||
// for other purposes, as it may risk causing deadlocks.
|
||||
mutable port::Mutex cache_res_mutex_;
|
||||
// Total memory reserved by placeholder entriesin the cache
|
||||
size_t placeholder_usage_;
|
||||
// Total placeholoder memory charged to both the primary and secondary
|
||||
// caches. Will be <= placeholder_usage_.
|
||||
size_t reserved_usage_;
|
||||
// Amount of memory reserved in the secondary cache. This should be
|
||||
// reserved_usage_ * sec_cache_res_ratio_ in steady state.
|
||||
size_t sec_reserved_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+156
-71
@@ -13,102 +13,189 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "env/unique_id_gen.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/math.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
// The generated seeds must fit in 31 bits so that
|
||||
// ShardedCacheOptions::hash_seed can be set to it explicitly, for
|
||||
// diagnostic/debugging purposes.
|
||||
constexpr uint32_t kSeedMask = 0x7fffffff;
|
||||
uint32_t DetermineSeed(int32_t hash_seed_option) {
|
||||
if (hash_seed_option >= 0) {
|
||||
// User-specified exact seed
|
||||
return static_cast<uint32_t>(hash_seed_option);
|
||||
}
|
||||
static SemiStructuredUniqueIdGen gen;
|
||||
if (hash_seed_option == ShardedCacheOptions::kHostHashSeed) {
|
||||
std::string hostname;
|
||||
Status s = Env::Default()->GetHostNameString(&hostname);
|
||||
if (s.ok()) {
|
||||
return GetSliceHash(hostname) & kSeedMask;
|
||||
} else {
|
||||
// Fall back on something stable within the process.
|
||||
return BitwiseAnd(gen.GetBaseUpper(), kSeedMask);
|
||||
}
|
||||
} else {
|
||||
// for kQuasiRandomHashSeed and fallback
|
||||
uint32_t val = gen.GenerateNext<uint32_t>() & kSeedMask;
|
||||
// Perform some 31-bit bijective transformations so that we get
|
||||
// quasirandom, not just incrementing. (An incrementing seed from a
|
||||
// random starting point would be fine, but hard to describe in a name.)
|
||||
// See https://en.wikipedia.org/wiki/Quasirandom and using a murmur-like
|
||||
// transformation here for our bijection in the lower 31 bits.
|
||||
// See https://en.wikipedia.org/wiki/MurmurHash
|
||||
val *= /*31-bit prime*/ 1150630961;
|
||||
val ^= (val & kSeedMask) >> 17;
|
||||
val *= /*31-bit prime*/ 1320603883;
|
||||
return val & kSeedMask;
|
||||
}
|
||||
|
||||
inline uint32_t HashSlice(const Slice& s) {
|
||||
return Lower32of64(GetSliceNPHash64(s));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ShardedCacheBase::ShardedCacheBase(const ShardedCacheOptions& opts)
|
||||
: Cache(opts.memory_allocator),
|
||||
last_id_(1),
|
||||
shard_mask_((uint32_t{1} << opts.num_shard_bits) - 1),
|
||||
hash_seed_(DetermineSeed(opts.hash_seed)),
|
||||
strict_capacity_limit_(opts.strict_capacity_limit),
|
||||
capacity_(opts.capacity) {}
|
||||
ShardedCache::ShardedCache(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit,
|
||||
std::shared_ptr<MemoryAllocator> allocator)
|
||||
: Cache(std::move(allocator)),
|
||||
shard_mask_((uint32_t{1} << num_shard_bits) - 1),
|
||||
capacity_(capacity),
|
||||
strict_capacity_limit_(strict_capacity_limit),
|
||||
last_id_(1) {}
|
||||
|
||||
size_t ShardedCacheBase::ComputePerShardCapacity(size_t capacity) const {
|
||||
void ShardedCache::SetCapacity(size_t capacity) {
|
||||
uint32_t num_shards = GetNumShards();
|
||||
return (capacity + (num_shards - 1)) / num_shards;
|
||||
const size_t per_shard = (capacity + (num_shards - 1)) / num_shards;
|
||||
MutexLock l(&capacity_mutex_);
|
||||
for (uint32_t s = 0; s < num_shards; s++) {
|
||||
GetShard(s)->SetCapacity(per_shard);
|
||||
}
|
||||
capacity_ = capacity;
|
||||
}
|
||||
|
||||
size_t ShardedCacheBase::GetPerShardCapacity() const {
|
||||
return ComputePerShardCapacity(GetCapacity());
|
||||
void ShardedCache::SetStrictCapacityLimit(bool strict_capacity_limit) {
|
||||
uint32_t num_shards = GetNumShards();
|
||||
MutexLock l(&capacity_mutex_);
|
||||
for (uint32_t s = 0; s < num_shards; s++) {
|
||||
GetShard(s)->SetStrictCapacityLimit(strict_capacity_limit);
|
||||
}
|
||||
strict_capacity_limit_ = strict_capacity_limit;
|
||||
}
|
||||
|
||||
uint64_t ShardedCacheBase::NewId() {
|
||||
Status ShardedCache::Insert(const Slice& key, void* value, size_t charge,
|
||||
DeleterFn deleter, Handle** handle,
|
||||
Priority priority) {
|
||||
uint32_t hash = HashSlice(key);
|
||||
return GetShard(Shard(hash))
|
||||
->Insert(key, hash, value, charge, deleter, handle, priority);
|
||||
}
|
||||
|
||||
Status ShardedCache::Insert(const Slice& key, void* value,
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
Handle** handle, Priority priority) {
|
||||
uint32_t hash = HashSlice(key);
|
||||
if (!helper) {
|
||||
return Status::InvalidArgument();
|
||||
}
|
||||
return GetShard(Shard(hash))
|
||||
->Insert(key, hash, value, helper, charge, handle, priority);
|
||||
}
|
||||
|
||||
Cache::Handle* ShardedCache::Lookup(const Slice& key, Statistics* /*stats*/) {
|
||||
uint32_t hash = HashSlice(key);
|
||||
return GetShard(Shard(hash))->Lookup(key, hash);
|
||||
}
|
||||
|
||||
Cache::Handle* ShardedCache::Lookup(const Slice& key,
|
||||
const CacheItemHelper* helper,
|
||||
const CreateCallback& create_cb,
|
||||
Priority priority, bool wait,
|
||||
Statistics* stats) {
|
||||
uint32_t hash = HashSlice(key);
|
||||
return GetShard(Shard(hash))
|
||||
->Lookup(key, hash, helper, create_cb, priority, wait, stats);
|
||||
}
|
||||
|
||||
bool ShardedCache::IsReady(Handle* handle) {
|
||||
uint32_t hash = GetHash(handle);
|
||||
return GetShard(Shard(hash))->IsReady(handle);
|
||||
}
|
||||
|
||||
void ShardedCache::Wait(Handle* handle) {
|
||||
uint32_t hash = GetHash(handle);
|
||||
GetShard(Shard(hash))->Wait(handle);
|
||||
}
|
||||
|
||||
bool ShardedCache::Ref(Handle* handle) {
|
||||
uint32_t hash = GetHash(handle);
|
||||
return GetShard(Shard(hash))->Ref(handle);
|
||||
}
|
||||
|
||||
bool ShardedCache::Release(Handle* handle, bool force_erase) {
|
||||
uint32_t hash = GetHash(handle);
|
||||
return GetShard(Shard(hash))->Release(handle, force_erase);
|
||||
}
|
||||
|
||||
bool ShardedCache::Release(Handle* handle, bool useful, bool force_erase) {
|
||||
uint32_t hash = GetHash(handle);
|
||||
return GetShard(Shard(hash))->Release(handle, useful, force_erase);
|
||||
}
|
||||
|
||||
void ShardedCache::Erase(const Slice& key) {
|
||||
uint32_t hash = HashSlice(key);
|
||||
GetShard(Shard(hash))->Erase(key, hash);
|
||||
}
|
||||
|
||||
uint64_t ShardedCache::NewId() {
|
||||
return last_id_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
size_t ShardedCacheBase::GetCapacity() const {
|
||||
MutexLock l(&config_mutex_);
|
||||
size_t ShardedCache::GetCapacity() const {
|
||||
MutexLock l(&capacity_mutex_);
|
||||
return capacity_;
|
||||
}
|
||||
|
||||
Status ShardedCacheBase::GetSecondaryCacheCapacity(size_t& size) const {
|
||||
size = 0;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status ShardedCacheBase::GetSecondaryCachePinnedUsage(size_t& size) const {
|
||||
size = 0;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
bool ShardedCacheBase::HasStrictCapacityLimit() const {
|
||||
MutexLock l(&config_mutex_);
|
||||
bool ShardedCache::HasStrictCapacityLimit() const {
|
||||
MutexLock l(&capacity_mutex_);
|
||||
return strict_capacity_limit_;
|
||||
}
|
||||
|
||||
size_t ShardedCacheBase::GetUsage(Handle* handle) const {
|
||||
size_t ShardedCache::GetUsage() const {
|
||||
// We will not lock the cache when getting the usage from shards.
|
||||
uint32_t num_shards = GetNumShards();
|
||||
size_t usage = 0;
|
||||
for (uint32_t s = 0; s < num_shards; s++) {
|
||||
usage += GetShard(s)->GetUsage();
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
size_t ShardedCache::GetUsage(Handle* handle) const {
|
||||
return GetCharge(handle);
|
||||
}
|
||||
|
||||
std::string ShardedCacheBase::GetPrintableOptions() const {
|
||||
size_t ShardedCache::GetPinnedUsage() const {
|
||||
// We will not lock the cache when getting the usage from shards.
|
||||
uint32_t num_shards = GetNumShards();
|
||||
size_t usage = 0;
|
||||
for (uint32_t s = 0; s < num_shards; s++) {
|
||||
usage += GetShard(s)->GetPinnedUsage();
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
void ShardedCache::ApplyToAllEntries(
|
||||
const std::function<void(const Slice& key, void* value, size_t charge,
|
||||
DeleterFn deleter)>& callback,
|
||||
const ApplyToAllEntriesOptions& opts) {
|
||||
uint32_t num_shards = GetNumShards();
|
||||
// Iterate over part of each shard, rotating between shards, to
|
||||
// minimize impact on latency of concurrent operations.
|
||||
std::unique_ptr<uint32_t[]> states(new uint32_t[num_shards]{});
|
||||
|
||||
uint32_t aepl_in_32 = static_cast<uint32_t>(
|
||||
std::min(size_t{UINT32_MAX}, opts.average_entries_per_lock));
|
||||
aepl_in_32 = std::min(aepl_in_32, uint32_t{1});
|
||||
|
||||
bool remaining_work;
|
||||
do {
|
||||
remaining_work = false;
|
||||
for (uint32_t s = 0; s < num_shards; s++) {
|
||||
if (states[s] != UINT32_MAX) {
|
||||
GetShard(s)->ApplyToSomeEntries(callback, aepl_in_32, &states[s]);
|
||||
remaining_work |= states[s] != UINT32_MAX;
|
||||
}
|
||||
}
|
||||
} while (remaining_work);
|
||||
}
|
||||
|
||||
void ShardedCache::EraseUnRefEntries() {
|
||||
uint32_t num_shards = GetNumShards();
|
||||
for (uint32_t s = 0; s < num_shards; s++) {
|
||||
GetShard(s)->EraseUnRefEntries();
|
||||
}
|
||||
}
|
||||
|
||||
std::string ShardedCache::GetPrintableOptions() const {
|
||||
std::string ret;
|
||||
ret.reserve(20000);
|
||||
const int kBufferSize = 200;
|
||||
char buffer[kBufferSize];
|
||||
{
|
||||
MutexLock l(&config_mutex_);
|
||||
MutexLock l(&capacity_mutex_);
|
||||
snprintf(buffer, kBufferSize, " capacity : %" ROCKSDB_PRIszt "\n",
|
||||
capacity_);
|
||||
ret.append(buffer);
|
||||
@@ -122,12 +209,12 @@ std::string ShardedCacheBase::GetPrintableOptions() const {
|
||||
snprintf(buffer, kBufferSize, " memory_allocator : %s\n",
|
||||
memory_allocator() ? memory_allocator()->Name() : "None");
|
||||
ret.append(buffer);
|
||||
AppendPrintableOptions(ret);
|
||||
ret.append(GetShard(0)->GetPrintableOptions());
|
||||
return ret;
|
||||
}
|
||||
|
||||
int GetDefaultCacheShardBits(size_t capacity, size_t min_shard_size) {
|
||||
int GetDefaultCacheShardBits(size_t capacity) {
|
||||
int num_shard_bits = 0;
|
||||
size_t min_shard_size = 512L * 1024L; // Every shard is at least 512KB.
|
||||
size_t num_shards = capacity / min_shard_size;
|
||||
while (num_shards >>= 1) {
|
||||
if (++num_shard_bits >= 6) {
|
||||
@@ -138,10 +225,8 @@ int GetDefaultCacheShardBits(size_t capacity, size_t min_shard_size) {
|
||||
return num_shard_bits;
|
||||
}
|
||||
|
||||
int ShardedCacheBase::GetNumShardBits() const {
|
||||
return BitsSetToOne(shard_mask_);
|
||||
}
|
||||
int ShardedCache::GetNumShardBits() const { return BitsSetToOne(shard_mask_); }
|
||||
|
||||
uint32_t ShardedCacheBase::GetNumShards() const { return shard_mask_ + 1; }
|
||||
uint32_t ShardedCache::GetNumShards() const { return shard_mask_ + 1; }
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
Vendored
+92
-282
@@ -10,313 +10,123 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "port/lang.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "rocksdb/cache.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Optional base class for classes implementing the CacheShard concept
|
||||
class CacheShardBase {
|
||||
// Single cache shard interface.
|
||||
class CacheShard {
|
||||
public:
|
||||
explicit CacheShardBase(CacheMetadataChargePolicy metadata_charge_policy)
|
||||
: metadata_charge_policy_(metadata_charge_policy) {}
|
||||
CacheShard() = default;
|
||||
virtual ~CacheShard() = default;
|
||||
|
||||
using DeleterFn = Cache::DeleterFn;
|
||||
|
||||
// Expected by concept CacheShard (TODO with C++20 support)
|
||||
// Some Defaults
|
||||
std::string GetPrintableOptions() const { return ""; }
|
||||
using HashVal = uint64_t;
|
||||
using HashCref = uint64_t;
|
||||
static inline HashVal ComputeHash(const Slice& key, uint32_t seed) {
|
||||
return GetSliceNPHash64(key, seed);
|
||||
}
|
||||
static inline uint32_t HashPieceForSharding(HashCref hash) {
|
||||
return Lower32of64(hash);
|
||||
}
|
||||
void AppendPrintableOptions(std::string& /*str*/) const {}
|
||||
|
||||
// Must be provided for concept CacheShard (TODO with C++20 support)
|
||||
/*
|
||||
struct HandleImpl { // for concept HandleImpl
|
||||
HashVal hash;
|
||||
HashCref GetHash() const;
|
||||
...
|
||||
};
|
||||
Status Insert(const Slice& key, HashCref hash, Cache::ObjectPtr value,
|
||||
const Cache::CacheItemHelper* helper, size_t charge,
|
||||
HandleImpl** handle, Cache::Priority priority,
|
||||
bool standalone) = 0;
|
||||
Handle* CreateStandalone(const Slice& key, HashCref hash, ObjectPtr obj,
|
||||
const CacheItemHelper* helper,
|
||||
size_t charge, bool allow_uncharged) = 0;
|
||||
HandleImpl* Lookup(const Slice& key, HashCref hash,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context,
|
||||
Cache::Priority priority,
|
||||
Statistics* stats) = 0;
|
||||
bool Release(HandleImpl* handle, bool useful, bool erase_if_last_ref) = 0;
|
||||
bool Ref(HandleImpl* handle) = 0;
|
||||
void Erase(const Slice& key, HashCref hash) = 0;
|
||||
void SetCapacity(size_t capacity) = 0;
|
||||
void SetStrictCapacityLimit(bool strict_capacity_limit) = 0;
|
||||
size_t GetUsage() const = 0;
|
||||
size_t GetPinnedUsage() const = 0;
|
||||
size_t GetOccupancyCount() const = 0;
|
||||
size_t GetTableAddressCount() const = 0;
|
||||
virtual Status Insert(const Slice& key, uint32_t hash, void* value,
|
||||
size_t charge, DeleterFn deleter,
|
||||
Cache::Handle** handle, Cache::Priority priority) = 0;
|
||||
virtual Status Insert(const Slice& key, uint32_t hash, void* value,
|
||||
const Cache::CacheItemHelper* helper, size_t charge,
|
||||
Cache::Handle** handle, Cache::Priority priority) = 0;
|
||||
virtual Cache::Handle* Lookup(const Slice& key, uint32_t hash) = 0;
|
||||
virtual Cache::Handle* Lookup(const Slice& key, uint32_t hash,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
const Cache::CreateCallback& create_cb,
|
||||
Cache::Priority priority, bool wait,
|
||||
Statistics* stats) = 0;
|
||||
virtual bool Release(Cache::Handle* handle, bool useful,
|
||||
bool force_erase) = 0;
|
||||
virtual bool IsReady(Cache::Handle* handle) = 0;
|
||||
virtual void Wait(Cache::Handle* handle) = 0;
|
||||
virtual bool Ref(Cache::Handle* handle) = 0;
|
||||
virtual bool Release(Cache::Handle* handle, bool force_erase) = 0;
|
||||
virtual void Erase(const Slice& key, uint32_t hash) = 0;
|
||||
virtual void SetCapacity(size_t capacity) = 0;
|
||||
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) = 0;
|
||||
virtual size_t GetUsage() const = 0;
|
||||
virtual size_t GetPinnedUsage() const = 0;
|
||||
// Handles iterating over roughly `average_entries_per_lock` entries, using
|
||||
// `state` to somehow record where it last ended up. Caller initially uses
|
||||
// *state == 0 and implementation sets *state = SIZE_MAX to indicate
|
||||
// *state == 0 and implementation sets *state = UINT32_MAX to indicate
|
||||
// completion.
|
||||
void ApplyToSomeEntries(
|
||||
const std::function<void(const Slice& key, ObjectPtr value,
|
||||
size_t charge,
|
||||
const Cache::CacheItemHelper* helper)>& callback,
|
||||
size_t average_entries_per_lock, size_t* state) = 0;
|
||||
void EraseUnRefEntries() = 0;
|
||||
*/
|
||||
virtual void ApplyToSomeEntries(
|
||||
const std::function<void(const Slice& key, void* value, size_t charge,
|
||||
DeleterFn deleter)>& callback,
|
||||
uint32_t average_entries_per_lock, uint32_t* state) = 0;
|
||||
virtual void EraseUnRefEntries() = 0;
|
||||
virtual std::string GetPrintableOptions() const { return ""; }
|
||||
void set_metadata_charge_policy(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
metadata_charge_policy_ = metadata_charge_policy;
|
||||
}
|
||||
|
||||
protected:
|
||||
const CacheMetadataChargePolicy metadata_charge_policy_;
|
||||
CacheMetadataChargePolicy metadata_charge_policy_ = kDontChargeCacheMetadata;
|
||||
};
|
||||
|
||||
// Portions of ShardedCache that do not depend on the template parameter
|
||||
class ShardedCacheBase : public Cache {
|
||||
// Generic cache interface which shards cache by hash of keys. 2^num_shard_bits
|
||||
// shards will be created, with capacity split evenly to each of the shards.
|
||||
// Keys are sharded by the highest num_shard_bits bits of hash value.
|
||||
class ShardedCache : public Cache {
|
||||
public:
|
||||
explicit ShardedCacheBase(const ShardedCacheOptions& opts);
|
||||
virtual ~ShardedCacheBase() = default;
|
||||
ShardedCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator = nullptr);
|
||||
virtual ~ShardedCache() = default;
|
||||
virtual CacheShard* GetShard(uint32_t shard) = 0;
|
||||
virtual const CacheShard* GetShard(uint32_t shard) const = 0;
|
||||
|
||||
virtual uint32_t GetHash(Handle* handle) const = 0;
|
||||
|
||||
virtual void SetCapacity(size_t capacity) override;
|
||||
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) override;
|
||||
|
||||
virtual Status Insert(const Slice& key, void* value, size_t charge,
|
||||
DeleterFn deleter, Handle** handle,
|
||||
Priority priority) override;
|
||||
virtual Status Insert(const Slice& key, void* value,
|
||||
const CacheItemHelper* helper, size_t chargge,
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override;
|
||||
virtual Handle* Lookup(const Slice& key, Statistics* stats) override;
|
||||
virtual Handle* Lookup(const Slice& key, const CacheItemHelper* helper,
|
||||
const CreateCallback& create_cb, Priority priority,
|
||||
bool wait, Statistics* stats = nullptr) override;
|
||||
virtual bool Release(Handle* handle, bool useful,
|
||||
bool force_erase = false) override;
|
||||
virtual bool IsReady(Handle* handle) override;
|
||||
virtual void Wait(Handle* handle) override;
|
||||
virtual bool Ref(Handle* handle) override;
|
||||
virtual bool Release(Handle* handle, bool force_erase = false) override;
|
||||
virtual void Erase(const Slice& key) override;
|
||||
virtual uint64_t NewId() override;
|
||||
virtual size_t GetCapacity() const override;
|
||||
virtual bool HasStrictCapacityLimit() const override;
|
||||
virtual size_t GetUsage() const override;
|
||||
virtual size_t GetUsage(Handle* handle) const override;
|
||||
virtual size_t GetPinnedUsage() const override;
|
||||
virtual void ApplyToAllEntries(
|
||||
const std::function<void(const Slice& key, void* value, size_t charge,
|
||||
DeleterFn deleter)>& callback,
|
||||
const ApplyToAllEntriesOptions& opts) override;
|
||||
virtual void EraseUnRefEntries() override;
|
||||
virtual std::string GetPrintableOptions() const override;
|
||||
|
||||
int GetNumShardBits() const;
|
||||
uint32_t GetNumShards() const;
|
||||
|
||||
uint64_t NewId() override;
|
||||
|
||||
bool HasStrictCapacityLimit() const override;
|
||||
size_t GetCapacity() const override;
|
||||
Status GetSecondaryCacheCapacity(size_t& size) const override;
|
||||
Status GetSecondaryCachePinnedUsage(size_t& size) const override;
|
||||
|
||||
using Cache::GetUsage;
|
||||
size_t GetUsage(Handle* handle) const override;
|
||||
std::string GetPrintableOptions() const override;
|
||||
|
||||
uint32_t GetHashSeed() const override { return hash_seed_; }
|
||||
|
||||
protected: // fns
|
||||
virtual void AppendPrintableOptions(std::string& str) const = 0;
|
||||
size_t GetPerShardCapacity() const;
|
||||
size_t ComputePerShardCapacity(size_t capacity) const;
|
||||
|
||||
protected: // data
|
||||
std::atomic<uint64_t> last_id_; // For NewId
|
||||
const uint32_t shard_mask_;
|
||||
const uint32_t hash_seed_;
|
||||
|
||||
// Dynamic configuration parameters, guarded by config_mutex_
|
||||
bool strict_capacity_limit_;
|
||||
size_t capacity_;
|
||||
mutable port::Mutex config_mutex_;
|
||||
};
|
||||
|
||||
// Generic cache interface that shards cache by hash of keys. 2^num_shard_bits
|
||||
// shards will be created, with capacity split evenly to each of the shards.
|
||||
// Keys are typically sharded by the lowest num_shard_bits bits of hash value
|
||||
// so that the upper bits of the hash value can keep a stable ordering of
|
||||
// table entries even as the table grows (using more upper hash bits).
|
||||
// See CacheShardBase above for what is expected of the CacheShard parameter.
|
||||
template <class CacheShard>
|
||||
class ShardedCache : public ShardedCacheBase {
|
||||
public:
|
||||
using HashVal = typename CacheShard::HashVal;
|
||||
using HashCref = typename CacheShard::HashCref;
|
||||
using HandleImpl = typename CacheShard::HandleImpl;
|
||||
|
||||
explicit ShardedCache(const ShardedCacheOptions& opts)
|
||||
: ShardedCacheBase(opts),
|
||||
shards_(static_cast<CacheShard*>(port::cacheline_aligned_alloc(
|
||||
sizeof(CacheShard) * GetNumShards()))),
|
||||
destroy_shards_in_dtor_(false) {}
|
||||
|
||||
virtual ~ShardedCache() {
|
||||
if (destroy_shards_in_dtor_) {
|
||||
ForEachShard([](CacheShard* cs) { cs->~CacheShard(); });
|
||||
}
|
||||
port::cacheline_aligned_free(shards_);
|
||||
}
|
||||
|
||||
CacheShard& GetShard(HashCref hash) {
|
||||
return shards_[CacheShard::HashPieceForSharding(hash) & shard_mask_];
|
||||
}
|
||||
|
||||
const CacheShard& GetShard(HashCref hash) const {
|
||||
return shards_[CacheShard::HashPieceForSharding(hash) & shard_mask_];
|
||||
}
|
||||
|
||||
void SetCapacity(size_t capacity) override {
|
||||
MutexLock l(&config_mutex_);
|
||||
capacity_ = capacity;
|
||||
auto per_shard = ComputePerShardCapacity(capacity);
|
||||
ForEachShard([=](CacheShard* cs) { cs->SetCapacity(per_shard); });
|
||||
}
|
||||
|
||||
void SetStrictCapacityLimit(bool s_c_l) override {
|
||||
MutexLock l(&config_mutex_);
|
||||
strict_capacity_limit_ = s_c_l;
|
||||
ForEachShard(
|
||||
[s_c_l](CacheShard* cs) { cs->SetStrictCapacityLimit(s_c_l); });
|
||||
}
|
||||
|
||||
Status Insert(
|
||||
const Slice& key, ObjectPtr obj, const CacheItemHelper* helper,
|
||||
size_t charge, Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW,
|
||||
const Slice& /*compressed_value*/ = Slice(),
|
||||
CompressionType /*type*/ = CompressionType::kNoCompression) override {
|
||||
assert(helper);
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
auto h_out = reinterpret_cast<HandleImpl**>(handle);
|
||||
return GetShard(hash).Insert(key, hash, obj, helper, charge, h_out,
|
||||
priority);
|
||||
}
|
||||
|
||||
Handle* CreateStandalone(const Slice& key, ObjectPtr obj,
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
bool allow_uncharged) override {
|
||||
assert(helper);
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
HandleImpl* result = GetShard(hash).CreateStandalone(
|
||||
key, hash, obj, helper, charge, allow_uncharged);
|
||||
return static_cast<Handle*>(result);
|
||||
}
|
||||
|
||||
Handle* Lookup(const Slice& key, const CacheItemHelper* helper = nullptr,
|
||||
CreateContext* create_context = nullptr,
|
||||
Priority priority = Priority::LOW,
|
||||
Statistics* stats = nullptr) override {
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
HandleImpl* result = GetShard(hash).Lookup(key, hash, helper,
|
||||
create_context, priority, stats);
|
||||
return static_cast<Handle*>(result);
|
||||
}
|
||||
|
||||
void Erase(const Slice& key) override {
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
GetShard(hash).Erase(key, hash);
|
||||
}
|
||||
|
||||
bool Release(Handle* handle, bool useful,
|
||||
bool erase_if_last_ref = false) override {
|
||||
auto h = static_cast<HandleImpl*>(handle);
|
||||
return GetShard(h->GetHash()).Release(h, useful, erase_if_last_ref);
|
||||
}
|
||||
bool Ref(Handle* handle) override {
|
||||
auto h = static_cast<HandleImpl*>(handle);
|
||||
return GetShard(h->GetHash()).Ref(h);
|
||||
}
|
||||
bool Release(Handle* handle, bool erase_if_last_ref = false) override {
|
||||
return Release(handle, true /*useful*/, erase_if_last_ref);
|
||||
}
|
||||
using ShardedCacheBase::GetUsage;
|
||||
size_t GetUsage() const override {
|
||||
return SumOverShards2(&CacheShard::GetUsage);
|
||||
}
|
||||
size_t GetPinnedUsage() const override {
|
||||
return SumOverShards2(&CacheShard::GetPinnedUsage);
|
||||
}
|
||||
size_t GetOccupancyCount() const override {
|
||||
return SumOverShards2(&CacheShard::GetOccupancyCount);
|
||||
}
|
||||
size_t GetTableAddressCount() const override {
|
||||
return SumOverShards2(&CacheShard::GetTableAddressCount);
|
||||
}
|
||||
void ApplyToAllEntries(
|
||||
const std::function<void(const Slice& key, ObjectPtr value, size_t charge,
|
||||
const CacheItemHelper* helper)>& callback,
|
||||
const ApplyToAllEntriesOptions& opts) override {
|
||||
uint32_t num_shards = GetNumShards();
|
||||
// Iterate over part of each shard, rotating between shards, to
|
||||
// minimize impact on latency of concurrent operations.
|
||||
std::unique_ptr<size_t[]> states(new size_t[num_shards]{});
|
||||
|
||||
size_t aepl = opts.average_entries_per_lock;
|
||||
aepl = std::min(aepl, size_t{1});
|
||||
|
||||
bool remaining_work;
|
||||
do {
|
||||
remaining_work = false;
|
||||
for (uint32_t i = 0; i < num_shards; i++) {
|
||||
if (states[i] != SIZE_MAX) {
|
||||
shards_[i].ApplyToSomeEntries(callback, aepl, &states[i]);
|
||||
remaining_work |= states[i] != SIZE_MAX;
|
||||
}
|
||||
}
|
||||
} while (remaining_work);
|
||||
}
|
||||
|
||||
void EraseUnRefEntries() override {
|
||||
ForEachShard([](CacheShard* cs) { cs->EraseUnRefEntries(); });
|
||||
}
|
||||
|
||||
void DisownData() override {
|
||||
// Leak data only if that won't generate an ASAN/valgrind warning.
|
||||
if (!kMustFreeHeapAllocations) {
|
||||
destroy_shards_in_dtor_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
inline void ForEachShard(const std::function<void(CacheShard*)>& fn) {
|
||||
uint32_t num_shards = GetNumShards();
|
||||
for (uint32_t i = 0; i < num_shards; i++) {
|
||||
fn(shards_ + i);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ForEachShard(
|
||||
const std::function<void(const CacheShard*)>& fn) const {
|
||||
uint32_t num_shards = GetNumShards();
|
||||
for (uint32_t i = 0; i < num_shards; i++) {
|
||||
fn(shards_ + i);
|
||||
}
|
||||
}
|
||||
|
||||
inline size_t SumOverShards(
|
||||
const std::function<size_t(CacheShard&)>& fn) const {
|
||||
uint32_t num_shards = GetNumShards();
|
||||
size_t result = 0;
|
||||
for (uint32_t i = 0; i < num_shards; i++) {
|
||||
result += fn(shards_[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline size_t SumOverShards2(size_t (CacheShard::*fn)() const) const {
|
||||
return SumOverShards([fn](CacheShard& cs) { return (cs.*fn)(); });
|
||||
}
|
||||
|
||||
// Must be called exactly once by derived class constructor
|
||||
void InitShards(const std::function<void(CacheShard*)>& placement_new) {
|
||||
ForEachShard(placement_new);
|
||||
destroy_shards_in_dtor_ = true;
|
||||
}
|
||||
|
||||
void AppendPrintableOptions(std::string& str) const override {
|
||||
shards_[0].AppendPrintableOptions(str);
|
||||
}
|
||||
inline uint32_t Shard(uint32_t hash) { return hash & shard_mask_; }
|
||||
|
||||
private:
|
||||
CacheShard* const shards_;
|
||||
bool destroy_shards_in_dtor_;
|
||||
const uint32_t shard_mask_;
|
||||
mutable port::Mutex capacity_mutex_;
|
||||
size_t capacity_;
|
||||
bool strict_capacity_limit_;
|
||||
std::atomic<uint64_t> last_id_;
|
||||
};
|
||||
|
||||
// 512KB is traditional minimum shard size.
|
||||
int GetDefaultCacheShardBits(size_t capacity,
|
||||
size_t min_shard_size = 512U * 1024U);
|
||||
extern int GetDefaultCacheShardBits(size_t capacity);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
Vendored
-125
@@ -1,125 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "cache/tiered_secondary_cache.h"
|
||||
|
||||
#include "monitoring/statistics_impl.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Creation callback for use in the lookup path. It calls the upper layer
|
||||
// create_cb to create the object, and optionally calls the compressed
|
||||
// secondary cache InsertSaved to save the compressed block. If
|
||||
// advise_erase is set, it means the primary cache wants the block to be
|
||||
// erased in the secondary cache, so we skip calling InsertSaved.
|
||||
//
|
||||
// For the time being, we assume that all blocks in the nvm tier belong to
|
||||
// the primary block cache (i.e CacheTier::kVolatileTier). That can be changed
|
||||
// if we implement demotion from the compressed secondary cache to the nvm
|
||||
// cache in the future.
|
||||
Status TieredSecondaryCache::MaybeInsertAndCreate(
|
||||
const Slice& data, CompressionType type, CacheTier source,
|
||||
Cache::CreateContext* ctx, MemoryAllocator* allocator,
|
||||
Cache::ObjectPtr* out_obj, size_t* out_charge) {
|
||||
TieredSecondaryCache::CreateContext* context =
|
||||
static_cast<TieredSecondaryCache::CreateContext*>(ctx);
|
||||
assert(source == CacheTier::kVolatileTier);
|
||||
if (!context->advise_erase && type != kNoCompression) {
|
||||
// Attempt to insert into compressed secondary cache
|
||||
// TODO: Don't hardcode the source
|
||||
context->comp_sec_cache->InsertSaved(*context->key, data, type, source)
|
||||
.PermitUncheckedError();
|
||||
RecordTick(context->stats, COMPRESSED_SECONDARY_CACHE_PROMOTIONS);
|
||||
} else {
|
||||
RecordTick(context->stats, COMPRESSED_SECONDARY_CACHE_PROMOTION_SKIPS);
|
||||
}
|
||||
// Primary cache will accept the object, so call its helper to create
|
||||
// the object
|
||||
return context->helper->create_cb(data, type, source, context->inner_ctx,
|
||||
allocator, out_obj, out_charge);
|
||||
}
|
||||
|
||||
// The lookup first looks up in the compressed secondary cache. If its a miss,
|
||||
// then the nvm cache lookup is called. The cache item helper and create
|
||||
// context are wrapped in order to intercept the creation callback to make
|
||||
// the decision on promoting to the compressed secondary cache.
|
||||
std::unique_ptr<SecondaryCacheResultHandle> TieredSecondaryCache::Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool wait, bool advise_erase,
|
||||
Statistics* stats, bool& kept_in_sec_cache) {
|
||||
bool dummy = false;
|
||||
std::unique_ptr<SecondaryCacheResultHandle> result =
|
||||
target()->Lookup(key, helper, create_context, wait, advise_erase, stats,
|
||||
/*kept_in_sec_cache=*/dummy);
|
||||
// We never want the item to spill back into the secondary cache
|
||||
kept_in_sec_cache = true;
|
||||
if (result) {
|
||||
assert(result->IsReady());
|
||||
return result;
|
||||
}
|
||||
|
||||
// If wait is true, then we can be a bit more efficient and avoid a memory
|
||||
// allocation for the CReateContext.
|
||||
const Cache::CacheItemHelper* outer_helper =
|
||||
TieredSecondaryCache::GetHelper();
|
||||
if (wait) {
|
||||
TieredSecondaryCache::CreateContext ctx;
|
||||
ctx.key = &key;
|
||||
ctx.advise_erase = advise_erase;
|
||||
ctx.helper = helper;
|
||||
ctx.inner_ctx = create_context;
|
||||
ctx.comp_sec_cache = target();
|
||||
ctx.stats = stats;
|
||||
|
||||
return nvm_sec_cache_->Lookup(key, outer_helper, &ctx, wait, advise_erase,
|
||||
stats, kept_in_sec_cache);
|
||||
}
|
||||
|
||||
// If wait is false, i.e its an async lookup, we have to allocate a result
|
||||
// handle for tracking purposes. Embed the CreateContext inside the handle
|
||||
// so we need only allocate memory once instead of twice.
|
||||
std::unique_ptr<ResultHandle> handle(new ResultHandle());
|
||||
handle->ctx()->key = &key;
|
||||
handle->ctx()->advise_erase = advise_erase;
|
||||
handle->ctx()->helper = helper;
|
||||
handle->ctx()->inner_ctx = create_context;
|
||||
handle->ctx()->comp_sec_cache = target();
|
||||
handle->ctx()->stats = stats;
|
||||
handle->SetInnerHandle(
|
||||
nvm_sec_cache_->Lookup(key, outer_helper, handle->ctx(), wait,
|
||||
advise_erase, stats, kept_in_sec_cache));
|
||||
if (!handle->inner_handle()) {
|
||||
handle.reset();
|
||||
} else {
|
||||
result.reset(handle.release());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Call the nvm cache WaitAll to complete the lookups
|
||||
void TieredSecondaryCache::WaitAll(
|
||||
std::vector<SecondaryCacheResultHandle*> handles) {
|
||||
std::vector<SecondaryCacheResultHandle*> nvm_handles;
|
||||
std::vector<ResultHandle*> my_handles;
|
||||
nvm_handles.reserve(handles.size());
|
||||
for (auto handle : handles) {
|
||||
// The handle could belong to the compressed secondary cache. Skip if
|
||||
// that's the case.
|
||||
if (handle->IsReady()) {
|
||||
continue;
|
||||
}
|
||||
ResultHandle* hdl = static_cast<ResultHandle*>(handle);
|
||||
nvm_handles.push_back(hdl->inner_handle());
|
||||
my_handles.push_back(hdl);
|
||||
}
|
||||
nvm_sec_cache_->WaitAll(nvm_handles);
|
||||
for (auto handle : my_handles) {
|
||||
assert(handle->inner_handle()->IsReady());
|
||||
handle->Complete();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
-158
@@ -1,158 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// A SecondaryCache that implements stacking of a compressed secondary cache
|
||||
// and a non-volatile (local flash) cache. It implements an admission
|
||||
// policy of warming the bottommost tier (local flash) with compressed
|
||||
// blocks from the SST on misses, and on hits in the bottommost tier,
|
||||
// promoting to the compressed and/or primary block cache. The admission
|
||||
// policies of the primary block cache and compressed secondary cache remain
|
||||
// unchanged - promote on second access. There is no demotion ofablocks
|
||||
// evicted from a tier. They are just discarded.
|
||||
//
|
||||
// In order to properly handle compressed blocks directly read from SSTs, and
|
||||
// to allow writeback of blocks compressed by the compressed secondary
|
||||
// cache in the future, we make use of the compression type and source
|
||||
// cache tier arguments in InsertSaved.
|
||||
class TieredSecondaryCache : public SecondaryCacheWrapper {
|
||||
public:
|
||||
TieredSecondaryCache(std::shared_ptr<SecondaryCache> comp_sec_cache,
|
||||
std::shared_ptr<SecondaryCache> nvm_sec_cache,
|
||||
TieredAdmissionPolicy adm_policy)
|
||||
: SecondaryCacheWrapper(comp_sec_cache), nvm_sec_cache_(nvm_sec_cache) {
|
||||
#ifndef NDEBUG
|
||||
assert(adm_policy == TieredAdmissionPolicy::kAdmPolicyThreeQueue);
|
||||
#else
|
||||
(void)adm_policy;
|
||||
#endif
|
||||
}
|
||||
|
||||
~TieredSecondaryCache() override {}
|
||||
|
||||
const char* Name() const override { return "TieredSecondaryCache"; }
|
||||
|
||||
// This is a no-op as we currently don't allow demotion (i.e
|
||||
// insertion by the upper layer) of evicted blocks.
|
||||
Status Insert(const Slice& /*key*/, Cache::ObjectPtr /*obj*/,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
bool /*force_insert*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Warm up the nvm tier directly
|
||||
Status InsertSaved(const Slice& key, const Slice& saved,
|
||||
CompressionType type = CompressionType::kNoCompression,
|
||||
CacheTier source = CacheTier::kVolatileTier) override {
|
||||
return nvm_sec_cache_->InsertSaved(key, saved, type, source);
|
||||
}
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool wait, bool advise_erase,
|
||||
Statistics* stats, bool& kept_in_sec_cache) override;
|
||||
|
||||
void WaitAll(std::vector<SecondaryCacheResultHandle*> handles) override;
|
||||
|
||||
private:
|
||||
struct CreateContext : public Cache::CreateContext {
|
||||
const Slice* key;
|
||||
bool advise_erase;
|
||||
const Cache::CacheItemHelper* helper;
|
||||
Cache::CreateContext* inner_ctx;
|
||||
std::shared_ptr<SecondaryCacheResultHandle> inner_handle;
|
||||
SecondaryCache* comp_sec_cache;
|
||||
Statistics* stats;
|
||||
};
|
||||
|
||||
class ResultHandle : public SecondaryCacheResultHandle {
|
||||
public:
|
||||
~ResultHandle() override {}
|
||||
|
||||
bool IsReady() override {
|
||||
if (inner_handle_ && inner_handle_->IsReady()) {
|
||||
Complete();
|
||||
}
|
||||
return ready_;
|
||||
}
|
||||
|
||||
void Wait() override {
|
||||
inner_handle_->Wait();
|
||||
Complete();
|
||||
}
|
||||
|
||||
size_t Size() override { return size_; }
|
||||
|
||||
Cache::ObjectPtr Value() override { return value_; }
|
||||
|
||||
void Complete() {
|
||||
size_ = inner_handle_->Size();
|
||||
value_ = inner_handle_->Value();
|
||||
inner_handle_.reset();
|
||||
ready_ = true;
|
||||
}
|
||||
|
||||
void SetInnerHandle(std::unique_ptr<SecondaryCacheResultHandle>&& handle) {
|
||||
inner_handle_ = std::move(handle);
|
||||
}
|
||||
|
||||
void SetSize(size_t size) { size_ = size; }
|
||||
|
||||
void SetValue(Cache::ObjectPtr val) { value_ = val; }
|
||||
|
||||
CreateContext* ctx() { return &ctx_; }
|
||||
|
||||
SecondaryCacheResultHandle* inner_handle() { return inner_handle_.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<SecondaryCacheResultHandle> inner_handle_;
|
||||
CreateContext ctx_;
|
||||
size_t size_;
|
||||
Cache::ObjectPtr value_;
|
||||
bool ready_ = false;
|
||||
};
|
||||
|
||||
static void NoopDelete(Cache::ObjectPtr /*obj*/,
|
||||
MemoryAllocator* /*allocator*/) {
|
||||
assert(false);
|
||||
}
|
||||
static size_t ZeroSize(Cache::ObjectPtr /*obj*/) {
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
static Status NoopSaveTo(Cache::ObjectPtr /*from_obj*/,
|
||||
size_t /*from_offset*/, size_t /*length*/,
|
||||
char* /*out_buf*/) {
|
||||
assert(false);
|
||||
return Status::OK();
|
||||
}
|
||||
static Status MaybeInsertAndCreate(const Slice& data, CompressionType type,
|
||||
CacheTier source,
|
||||
Cache::CreateContext* ctx,
|
||||
MemoryAllocator* allocator,
|
||||
Cache::ObjectPtr* out_obj,
|
||||
size_t* out_charge);
|
||||
|
||||
static const Cache::CacheItemHelper* GetHelper() {
|
||||
const static Cache::CacheItemHelper basic_helper(CacheEntryRole::kMisc,
|
||||
&NoopDelete);
|
||||
const static Cache::CacheItemHelper maybe_insert_and_create_helper{
|
||||
CacheEntryRole::kMisc, &NoopDelete, &ZeroSize,
|
||||
&NoopSaveTo, &MaybeInsertAndCreate, &basic_helper,
|
||||
};
|
||||
return &maybe_insert_and_create_helper;
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache> comp_sec_cache_;
|
||||
std::shared_ptr<SecondaryCache> nvm_sec_cache_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
-831
@@ -1,831 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
#include "cache/compressed_secondary_cache.h"
|
||||
#include "cache/secondary_cache_adapter.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "typed_cache.h"
|
||||
#include "util/random.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class TestSecondaryCache : public SecondaryCache {
|
||||
public:
|
||||
explicit TestSecondaryCache(size_t capacity, bool ready_before_wait)
|
||||
: cache_(NewLRUCache(capacity, 0, false, 0.5 /* high_pri_pool_ratio */,
|
||||
nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata)),
|
||||
ready_before_wait_(ready_before_wait),
|
||||
num_insert_saved_(0),
|
||||
num_hits_(0),
|
||||
num_misses_(0) {}
|
||||
|
||||
const char* Name() const override { return "TestSecondaryCache"; }
|
||||
|
||||
Status Insert(const Slice& /*key*/, Cache::ObjectPtr /*value*/,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
bool /*force_insert*/) override {
|
||||
assert(false);
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
Status InsertSaved(const Slice& key, const Slice& saved,
|
||||
CompressionType type = kNoCompression,
|
||||
CacheTier source = CacheTier::kVolatileTier) override {
|
||||
CheckCacheKeyCommonPrefix(key);
|
||||
size_t size;
|
||||
char* buf;
|
||||
Status s;
|
||||
|
||||
num_insert_saved_++;
|
||||
size = saved.size();
|
||||
buf = new char[size + sizeof(uint64_t) + 2 * sizeof(uint16_t)];
|
||||
EncodeFixed64(buf, size);
|
||||
buf += sizeof(uint64_t);
|
||||
EncodeFixed16(buf, type);
|
||||
buf += sizeof(uint16_t);
|
||||
EncodeFixed16(buf, (uint16_t)source);
|
||||
buf += sizeof(uint16_t);
|
||||
memcpy(buf, saved.data(), size);
|
||||
buf -= sizeof(uint64_t) + 2 * sizeof(uint16_t);
|
||||
if (!s.ok()) {
|
||||
delete[] buf;
|
||||
return s;
|
||||
}
|
||||
return cache_.Insert(key, buf, size);
|
||||
}
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool wait, bool /*advise_erase*/,
|
||||
Statistics* /*stats*/, bool& kept_in_sec_cache) override {
|
||||
std::string key_str = key.ToString();
|
||||
TEST_SYNC_POINT_CALLBACK("TestSecondaryCache::Lookup", &key_str);
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle;
|
||||
kept_in_sec_cache = false;
|
||||
|
||||
TypedHandle* handle = cache_.Lookup(key);
|
||||
if (handle) {
|
||||
num_hits_++;
|
||||
Cache::ObjectPtr value = nullptr;
|
||||
size_t charge = 0;
|
||||
Status s;
|
||||
char* ptr = cache_.Value(handle);
|
||||
CompressionType type;
|
||||
CacheTier source;
|
||||
size_t size = DecodeFixed64(ptr);
|
||||
ptr += sizeof(uint64_t);
|
||||
type = static_cast<CompressionType>(DecodeFixed16(ptr));
|
||||
ptr += sizeof(uint16_t);
|
||||
source = static_cast<CacheTier>(DecodeFixed16(ptr));
|
||||
assert(source == CacheTier::kVolatileTier);
|
||||
ptr += sizeof(uint16_t);
|
||||
s = helper->create_cb(Slice(ptr, size), type, source, create_context,
|
||||
/*alloc*/ nullptr, &value, &charge);
|
||||
if (s.ok()) {
|
||||
secondary_handle.reset(new TestSecondaryCacheResultHandle(
|
||||
cache_.get(), handle, value, charge,
|
||||
/*ready=*/wait || ready_before_wait_));
|
||||
kept_in_sec_cache = true;
|
||||
} else {
|
||||
cache_.Release(handle);
|
||||
}
|
||||
} else {
|
||||
num_misses_++;
|
||||
}
|
||||
return secondary_handle;
|
||||
}
|
||||
|
||||
bool SupportForceErase() const override { return false; }
|
||||
|
||||
void Erase(const Slice& /*key*/) override {}
|
||||
|
||||
void WaitAll(std::vector<SecondaryCacheResultHandle*> handles) override {
|
||||
for (SecondaryCacheResultHandle* handle : handles) {
|
||||
TestSecondaryCacheResultHandle* sec_handle =
|
||||
static_cast<TestSecondaryCacheResultHandle*>(handle);
|
||||
EXPECT_FALSE(sec_handle->IsReady());
|
||||
sec_handle->SetReady();
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetPrintableOptions() const override { return ""; }
|
||||
|
||||
uint32_t num_insert_saved() { return num_insert_saved_; }
|
||||
|
||||
uint32_t num_hits() { return num_hits_; }
|
||||
|
||||
uint32_t num_misses() { return num_misses_; }
|
||||
|
||||
void CheckCacheKeyCommonPrefix(const Slice& key) {
|
||||
Slice current_prefix(key.data(), OffsetableCacheKey::kCommonPrefixSize);
|
||||
if (ckey_prefix_.empty()) {
|
||||
ckey_prefix_ = current_prefix.ToString();
|
||||
} else {
|
||||
EXPECT_EQ(ckey_prefix_, current_prefix.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
class TestSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
|
||||
public:
|
||||
TestSecondaryCacheResultHandle(Cache* cache, Cache::Handle* handle,
|
||||
Cache::ObjectPtr value, size_t size,
|
||||
bool ready)
|
||||
: cache_(cache),
|
||||
handle_(handle),
|
||||
value_(value),
|
||||
size_(size),
|
||||
is_ready_(ready) {}
|
||||
|
||||
~TestSecondaryCacheResultHandle() override { cache_->Release(handle_); }
|
||||
|
||||
bool IsReady() override { return is_ready_; }
|
||||
|
||||
void Wait() override {}
|
||||
|
||||
Cache::ObjectPtr Value() override {
|
||||
assert(is_ready_);
|
||||
return value_;
|
||||
}
|
||||
|
||||
size_t Size() override { return Value() ? size_ : 0; }
|
||||
|
||||
void SetReady() { is_ready_ = true; }
|
||||
|
||||
private:
|
||||
Cache* cache_;
|
||||
Cache::Handle* handle_;
|
||||
Cache::ObjectPtr value_;
|
||||
size_t size_;
|
||||
bool is_ready_;
|
||||
};
|
||||
|
||||
using SharedCache =
|
||||
BasicTypedSharedCacheInterface<char[], CacheEntryRole::kMisc>;
|
||||
using TypedHandle = SharedCache::TypedHandle;
|
||||
SharedCache cache_;
|
||||
bool ready_before_wait_;
|
||||
uint32_t num_insert_saved_;
|
||||
uint32_t num_hits_;
|
||||
uint32_t num_misses_;
|
||||
std::string ckey_prefix_;
|
||||
};
|
||||
|
||||
class DBTieredSecondaryCacheTest : public DBTestBase {
|
||||
public:
|
||||
DBTieredSecondaryCacheTest()
|
||||
: DBTestBase("db_tiered_secondary_cache_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
std::shared_ptr<Cache> NewCache(
|
||||
size_t pri_capacity, size_t compressed_capacity, size_t nvm_capacity,
|
||||
TieredAdmissionPolicy adm_policy = TieredAdmissionPolicy::kAdmPolicyAuto,
|
||||
bool ready_before_wait = false) {
|
||||
LRUCacheOptions lru_opts;
|
||||
TieredCacheOptions opts;
|
||||
lru_opts.capacity = 0;
|
||||
lru_opts.num_shard_bits = 0;
|
||||
lru_opts.high_pri_pool_ratio = 0;
|
||||
opts.cache_opts = &lru_opts;
|
||||
opts.cache_type = PrimaryCacheType::kCacheTypeLRU;
|
||||
opts.comp_cache_opts.capacity = 0;
|
||||
opts.comp_cache_opts.num_shard_bits = 0;
|
||||
opts.total_capacity = pri_capacity + compressed_capacity;
|
||||
opts.compressed_secondary_ratio = compressed_secondary_ratio_ =
|
||||
(double)compressed_capacity / opts.total_capacity;
|
||||
if (nvm_capacity > 0) {
|
||||
nvm_sec_cache_.reset(
|
||||
new TestSecondaryCache(nvm_capacity, ready_before_wait));
|
||||
opts.nvm_sec_cache = nvm_sec_cache_;
|
||||
}
|
||||
opts.adm_policy = adm_policy;
|
||||
cache_ = NewTieredCache(opts);
|
||||
assert(cache_ != nullptr);
|
||||
|
||||
return cache_;
|
||||
}
|
||||
|
||||
void ClearPrimaryCache() {
|
||||
ASSERT_EQ(UpdateTieredCache(cache_, -1, 1.0), Status::OK());
|
||||
ASSERT_EQ(UpdateTieredCache(cache_, -1, compressed_secondary_ratio_),
|
||||
Status::OK());
|
||||
}
|
||||
|
||||
TestSecondaryCache* nvm_sec_cache() { return nvm_sec_cache_.get(); }
|
||||
|
||||
CompressedSecondaryCache* compressed_secondary_cache() {
|
||||
return static_cast<CompressedSecondaryCache*>(
|
||||
static_cast<CacheWithSecondaryAdapter*>(cache_.get())
|
||||
->TEST_GetSecondaryCache());
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Cache> cache_;
|
||||
std::shared_ptr<TestSecondaryCache> nvm_sec_cache_;
|
||||
double compressed_secondary_ratio_;
|
||||
};
|
||||
|
||||
// In this test, the block size is set to 4096. Each value is 1007 bytes, so
|
||||
// each data block contains exactly 4 KV pairs. Metadata blocks are not
|
||||
// cached, so we can accurately estimate the cache usage.
|
||||
TEST_F(DBTieredSecondaryCacheTest, BasicTest) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
// We want a block cache of size 5KB, and a compressed secondary cache of
|
||||
// size 5KB. However, we specify a block cache size of 256KB here in order
|
||||
// to take into account the cache reservation in the block cache on
|
||||
// behalf of the compressed cache. The unit of cache reservation is 256KB.
|
||||
// The effective block cache capacity will be calculated as 256 + 5 = 261KB,
|
||||
// and 256KB will be reserved for the compressed cache, leaving 5KB for
|
||||
// the primary block cache. We only have to worry about this here because
|
||||
// the cache size is so small.
|
||||
table_options.block_cache = NewCache(256 * 1024, 5 * 1024, 256 * 1024);
|
||||
table_options.block_size = 4 * 1024;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
// Disable paranoid_file_checks so that flush will not read back the newly
|
||||
// written file
|
||||
options.paranoid_file_checks = false;
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
const int N = 256;
|
||||
for (int i = 0; i < N; i++) {
|
||||
std::string p_v;
|
||||
test::CompressibleString(&rnd, 0.5, 1007, &p_v);
|
||||
ASSERT_OK(Put(Key(i), p_v));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// The first 2 Gets, for keys 0 and 5, will load the corresponding data
|
||||
// blocks as they will be cache misses. The nvm secondary cache will be
|
||||
// warmed up with the compressed blocks
|
||||
std::string v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 1u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 1u);
|
||||
|
||||
v = Get(Key(5));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
|
||||
// At this point, the nvm cache is warmed up with the data blocks for 0
|
||||
// and 5. The next Get will lookup the block in nvm and will be a hit.
|
||||
// It will be created as a standalone entry in memory, and a placeholder
|
||||
// will be inserted in the primary and compressed caches.
|
||||
v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 1u);
|
||||
|
||||
// For this Get, the primary and compressed only have placeholders for
|
||||
// the required data block. So we will lookup the nvm cache and find the
|
||||
// block there. This time, the block will be promoted to the primary
|
||||
// block cache. No promotion to the compressed secondary cache happens,
|
||||
// and it will retain the placeholder.
|
||||
v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 2u);
|
||||
|
||||
// This Get will find the data block in the primary cache.
|
||||
v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 2u);
|
||||
|
||||
// We repeat the sequence for key 5. This will end up evicting the block
|
||||
// for 0 from the in-memory cache.
|
||||
v = Get(Key(5));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 3u);
|
||||
|
||||
v = Get(Key(5));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
|
||||
|
||||
v = Get(Key(5));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
|
||||
|
||||
// This Get for key 0 will find the data block in nvm. Since the compressed
|
||||
// cache still has the placeholder, the block (compressed) will be
|
||||
// admitted. It is theh inserted into the primary as a standalone entry.
|
||||
v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 5u);
|
||||
|
||||
// This Get for key 0 will find the data block in the compressed secondary
|
||||
// cache.
|
||||
v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 5u);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
// This test is very similar to BasicTest, except it calls MultiGet rather
|
||||
// than Get, in order to exercise the async lookup and WaitAll path.
|
||||
TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewCache(260 * 1024, 10 * 1024, 256 * 1024);
|
||||
table_options.block_size = 4 * 1024;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
const int N = 256;
|
||||
for (int i = 0; i < N; i++) {
|
||||
std::string p_v;
|
||||
test::CompressibleString(&rnd, 0.5, 1007, &p_v);
|
||||
ASSERT_OK(Put(Key(i), p_v));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(12));
|
||||
keys.push_back(Key(16));
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 3u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 6u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 6u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(12));
|
||||
keys.push_back(Key(16));
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 9u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(12));
|
||||
keys.push_back(Key(16));
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 12u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(12));
|
||||
keys.push_back(Key(16));
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 12u);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewCache(250 * 1024, 20 * 1024, 256 * 1024);
|
||||
table_options.block_size = 4 * 1024;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
const int N = 256;
|
||||
for (int i = 0; i < N; i++) {
|
||||
std::string p_v;
|
||||
test::CompressibleString(&rnd, 0.5, 1007, &p_v);
|
||||
ASSERT_OK(Put(Key(i), p_v));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(12));
|
||||
keys.push_back(Key(16));
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
|
||||
// Insert placeholders for 4 in primary and compressed
|
||||
std::string val = Get(Key(4));
|
||||
|
||||
// Force placeholder 4 out of primary
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(24));
|
||||
keys.push_back(Key(28));
|
||||
keys.push_back(Key(32));
|
||||
keys.push_back(Key(36));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 10u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 10u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 1u);
|
||||
|
||||
// Now read 4 again. This will create a placeholder in primary, and insert
|
||||
// in compressed secondary since it already has a placeholder
|
||||
val = Get(Key(4));
|
||||
|
||||
// Now read 0, 4 and 8. While 4 is already in the compressed secondary
|
||||
// cache, 0 and 8 will be read asynchronously from the nvm tier. The
|
||||
// WaitAll will be called for all 3 blocks.
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 10u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 10u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewCache(250 * 1024, 20 * 1024, 256 * 1024,
|
||||
TieredAdmissionPolicy::kAdmPolicyAuto,
|
||||
/*ready_before_wait=*/true);
|
||||
table_options.block_size = 4 * 1024;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
const int N = 256;
|
||||
for (int i = 0; i < N; i++) {
|
||||
std::string p_v;
|
||||
test::CompressibleString(&rnd, 0.5, 1007, &p_v);
|
||||
ASSERT_OK(Put(Key(i), p_v));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 3u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(12));
|
||||
keys.push_back(Key(16));
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 6u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 3u);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 6u);
|
||||
|
||||
ClearPrimaryCache();
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(32));
|
||||
keys.push_back(Key(36));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 8u);
|
||||
|
||||
keys.clear();
|
||||
values.clear();
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(32));
|
||||
keys.push_back(Key(36));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 8u);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
// This test is for iteration. It iterates through a set of keys in two
|
||||
// passes. First pass loads the compressed blocks into the nvm tier, and
|
||||
// the second pass should hit all of those blocks.
|
||||
TEST_F(DBTieredSecondaryCacheTest, IterateTest) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewCache(250 * 1024, 10 * 1024, 256 * 1024);
|
||||
table_options.block_size = 4 * 1024;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
const int N = 256;
|
||||
for (int i = 0; i < N; i++) {
|
||||
std::string p_v;
|
||||
test::CompressibleString(&rnd, 0.5, 1007, &p_v);
|
||||
ASSERT_OK(Put(Key(i), p_v));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ReadOptions ro;
|
||||
ro.readahead_size = 256 * 1024;
|
||||
auto iter = dbfull()->NewIterator(ro);
|
||||
iter->SeekToFirst();
|
||||
for (int i = 0; i < 31; ++i) {
|
||||
ASSERT_EQ(Key(i), iter->key().ToString());
|
||||
ASSERT_EQ(1007, iter->value().size());
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
delete iter;
|
||||
|
||||
iter = dbfull()->NewIterator(ro);
|
||||
iter->SeekToFirst();
|
||||
for (int i = 0; i < 31; ++i) {
|
||||
ASSERT_EQ(Key(i), iter->key().ToString());
|
||||
ASSERT_EQ(1007, iter->value().size());
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 8u);
|
||||
delete iter;
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
class DBTieredAdmPolicyTest
|
||||
: public DBTieredSecondaryCacheTest,
|
||||
public testing::WithParamInterface<TieredAdmissionPolicy> {};
|
||||
|
||||
TEST_P(DBTieredAdmPolicyTest, CompressedOnlyTest) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
// We want a block cache of size 10KB, and a compressed secondary cache of
|
||||
// size 10KB. However, we specify a block cache size of 256KB here in order
|
||||
// to take into account the cache reservation in the block cache on
|
||||
// behalf of the compressed cache. The unit of cache reservation is 256KB.
|
||||
// The effective block cache capacity will be calculated as 256 + 10 = 266KB,
|
||||
// and 256KB will be reserved for the compressed cache, leaving 10KB for
|
||||
// the primary block cache. We only have to worry about this here because
|
||||
// the cache size is so small.
|
||||
table_options.block_cache = NewCache(256 * 1024, 10 * 1024, 0, GetParam());
|
||||
table_options.block_size = 4 * 1024;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
size_t comp_cache_usage = compressed_secondary_cache()->TEST_GetUsage();
|
||||
// Disable paranoid_file_checks so that flush will not read back the newly
|
||||
// written file
|
||||
options.paranoid_file_checks = false;
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
const int N = 256;
|
||||
for (int i = 0; i < N; i++) {
|
||||
std::string p_v;
|
||||
test::CompressibleString(&rnd, 0.5, 1007, &p_v);
|
||||
ASSERT_OK(Put(Key(i), p_v));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// The first 2 Gets, for keys 0 and 5, will load the corresponding data
|
||||
// blocks as they will be cache misses. Since this is a 2-tier cache (
|
||||
// primary and compressed), no warm-up should happen with the compressed
|
||||
// blocks.
|
||||
std::string v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
|
||||
v = Get(Key(5));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
|
||||
ASSERT_EQ(compressed_secondary_cache()->TEST_GetUsage(), comp_cache_usage);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTieredAdmPolicyTest, DBTieredAdmPolicyTest,
|
||||
::testing::Values(TieredAdmissionPolicy::kAdmPolicyAuto,
|
||||
TieredAdmissionPolicy::kAdmPolicyPlaceholder,
|
||||
TieredAdmissionPolicy::kAdmPolicyAllowCacheHits));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Vendored
-380
@@ -1,380 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
// APIs for accessing Cache in a type-safe and convenient way. Cache is kept
|
||||
// at a low, thin level of abstraction so that different implementations can
|
||||
// be plugged in, but these wrappers provide clean, convenient access to the
|
||||
// most common operations.
|
||||
//
|
||||
// A number of template classes are needed for sharing common structure. The
|
||||
// key classes are these:
|
||||
//
|
||||
// * PlaceholderCacheInterface - Used for making cache reservations, with
|
||||
// entries that have a charge but no value.
|
||||
// * BasicTypedCacheInterface<TValue> - Used for primary cache storage of
|
||||
// objects of type TValue.
|
||||
// * FullTypedCacheHelper<TValue, TCreateContext> - Used for secondary cache
|
||||
// compatible storage of objects of type TValue.
|
||||
// * For each of these, there's a "Shared" version
|
||||
// (e.g. FullTypedSharedCacheInterface) that holds a shared_ptr to the Cache,
|
||||
// rather than assuming external ownership by holding only a raw `Cache*`.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "cache/cache_helpers.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/advanced_options.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// For future consideration:
|
||||
// * Pass in value to Insert with std::unique_ptr& to simplify ownership
|
||||
// transfer logic in callers
|
||||
// * Make key type a template parameter (e.g. useful for table cache)
|
||||
// * Closer integration with CacheHandleGuard (opt-in, so not always
|
||||
// paying the extra overhead)
|
||||
|
||||
#define CACHE_TYPE_DEFS() \
|
||||
using Priority = Cache::Priority; \
|
||||
using Handle = Cache::Handle; \
|
||||
using ObjectPtr = Cache::ObjectPtr; \
|
||||
using CreateContext = Cache::CreateContext; \
|
||||
using CacheItemHelper = Cache::CacheItemHelper /* caller ; */
|
||||
|
||||
template <typename CachePtr>
|
||||
class BaseCacheInterface {
|
||||
public:
|
||||
CACHE_TYPE_DEFS();
|
||||
|
||||
/*implicit*/ BaseCacheInterface(CachePtr cache) : cache_(std::move(cache)) {}
|
||||
|
||||
inline void Release(Handle* handle) { cache_->Release(handle); }
|
||||
|
||||
inline void ReleaseAndEraseIfLastRef(Handle* handle) {
|
||||
cache_->Release(handle, /*erase_if_last_ref*/ true);
|
||||
}
|
||||
|
||||
inline void RegisterReleaseAsCleanup(Handle* handle, Cleanable& cleanable) {
|
||||
cleanable.RegisterCleanup(&ReleaseCacheHandleCleanup, get(), handle);
|
||||
}
|
||||
|
||||
inline Cache* get() const { return &*cache_; }
|
||||
|
||||
explicit inline operator bool() const noexcept { return cache_ != nullptr; }
|
||||
|
||||
protected:
|
||||
CachePtr cache_;
|
||||
};
|
||||
|
||||
// PlaceholderCacheInterface - Used for making cache reservations, with
|
||||
// entries that have a charge but no value. CacheEntryRole is required as
|
||||
// a template parameter.
|
||||
template <CacheEntryRole kRole, typename CachePtr = Cache*>
|
||||
class PlaceholderCacheInterface : public BaseCacheInterface<CachePtr> {
|
||||
public:
|
||||
CACHE_TYPE_DEFS();
|
||||
using BaseCacheInterface<CachePtr>::BaseCacheInterface;
|
||||
|
||||
inline Status Insert(const Slice& key, size_t charge, Handle** handle) {
|
||||
return this->cache_->Insert(key, /*value=*/nullptr, GetHelper(), charge,
|
||||
handle);
|
||||
}
|
||||
|
||||
static const Cache::CacheItemHelper* GetHelper() {
|
||||
static const Cache::CacheItemHelper kHelper{kRole};
|
||||
return &kHelper;
|
||||
}
|
||||
};
|
||||
|
||||
template <CacheEntryRole kRole>
|
||||
using PlaceholderSharedCacheInterface =
|
||||
PlaceholderCacheInterface<kRole, std::shared_ptr<Cache>>;
|
||||
|
||||
template <class TValue>
|
||||
class BasicTypedCacheHelperFns {
|
||||
public:
|
||||
CACHE_TYPE_DEFS();
|
||||
// E.g. char* for char[]
|
||||
using TValuePtr = std::remove_extent_t<TValue>*;
|
||||
|
||||
protected:
|
||||
inline static ObjectPtr UpCastValue(TValuePtr value) { return value; }
|
||||
inline static TValuePtr DownCastValue(ObjectPtr value) {
|
||||
return static_cast<TValuePtr>(value);
|
||||
}
|
||||
|
||||
static void Delete(ObjectPtr value, MemoryAllocator* allocator) {
|
||||
// FIXME: Currently, no callers actually allocate the ObjectPtr objects
|
||||
// using the custom allocator, just subobjects that keep a reference to
|
||||
// the allocator themselves (with CacheAllocationPtr).
|
||||
if (/*DISABLED*/ false && allocator) {
|
||||
if constexpr (std::is_destructible_v<TValue>) {
|
||||
DownCastValue(value)->~TValue();
|
||||
}
|
||||
allocator->Deallocate(value);
|
||||
} else {
|
||||
// Like delete but properly handles TValue=char[] etc.
|
||||
std::default_delete<TValue>{}(DownCastValue(value));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// In its own class to try to minimize the number of distinct CacheItemHelper
|
||||
// instances (e.g. don't vary by CachePtr)
|
||||
template <class TValue, CacheEntryRole kRole>
|
||||
class BasicTypedCacheHelper : public BasicTypedCacheHelperFns<TValue> {
|
||||
public:
|
||||
static const Cache::CacheItemHelper* GetBasicHelper() {
|
||||
static const Cache::CacheItemHelper kHelper{kRole,
|
||||
&BasicTypedCacheHelper::Delete};
|
||||
return &kHelper;
|
||||
}
|
||||
};
|
||||
|
||||
// BasicTypedCacheInterface - Used for primary cache storage of objects of
|
||||
// type TValue, which can be cleaned up with std::default_delete<TValue>. The
|
||||
// role is provided by TValue::kCacheEntryRole or given in an optional
|
||||
// template parameter.
|
||||
template <class TValue, CacheEntryRole kRole = TValue::kCacheEntryRole,
|
||||
typename CachePtr = Cache*>
|
||||
class BasicTypedCacheInterface : public BaseCacheInterface<CachePtr>,
|
||||
public BasicTypedCacheHelper<TValue, kRole> {
|
||||
public:
|
||||
CACHE_TYPE_DEFS();
|
||||
using typename BasicTypedCacheHelperFns<TValue>::TValuePtr;
|
||||
struct TypedHandle : public Handle {};
|
||||
using BasicTypedCacheHelper<TValue, kRole>::GetBasicHelper;
|
||||
// ctor
|
||||
using BaseCacheInterface<CachePtr>::BaseCacheInterface;
|
||||
struct TypedAsyncLookupHandle : public Cache::AsyncLookupHandle {
|
||||
TypedHandle* Result() {
|
||||
return static_cast<TypedHandle*>(Cache::AsyncLookupHandle::Result());
|
||||
}
|
||||
};
|
||||
|
||||
inline Status Insert(const Slice& key, TValuePtr value, size_t charge,
|
||||
TypedHandle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) {
|
||||
auto untyped_handle = reinterpret_cast<Handle**>(handle);
|
||||
return this->cache_->Insert(
|
||||
key, BasicTypedCacheHelperFns<TValue>::UpCastValue(value),
|
||||
GetBasicHelper(), charge, untyped_handle, priority);
|
||||
}
|
||||
|
||||
inline TypedHandle* Lookup(const Slice& key, Statistics* stats = nullptr) {
|
||||
return static_cast<TypedHandle*>(this->cache_->BasicLookup(key, stats));
|
||||
}
|
||||
|
||||
inline void StartAsyncLookup(TypedAsyncLookupHandle& async_handle) {
|
||||
assert(async_handle.helper == nullptr);
|
||||
this->cache_->StartAsyncLookup(async_handle);
|
||||
}
|
||||
|
||||
inline CacheHandleGuard<TValue> Guard(TypedHandle* handle) {
|
||||
if (handle) {
|
||||
return CacheHandleGuard<TValue>(&*this->cache_, handle);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
inline std::shared_ptr<TValue> SharedGuard(TypedHandle* handle) {
|
||||
if (handle) {
|
||||
return MakeSharedCacheHandleGuard<TValue>(&*this->cache_, handle);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
inline TValuePtr Value(TypedHandle* handle) {
|
||||
return BasicTypedCacheHelperFns<TValue>::DownCastValue(
|
||||
this->cache_->Value(handle));
|
||||
}
|
||||
};
|
||||
|
||||
// BasicTypedSharedCacheInterface - Like BasicTypedCacheInterface but with a
|
||||
// shared_ptr<Cache> for keeping Cache alive.
|
||||
template <class TValue, CacheEntryRole kRole = TValue::kCacheEntryRole>
|
||||
using BasicTypedSharedCacheInterface =
|
||||
BasicTypedCacheInterface<TValue, kRole, std::shared_ptr<Cache>>;
|
||||
|
||||
// TValue must implement ContentSlice() and ~TValue
|
||||
// TCreateContext must implement Create(std::unique_ptr<TValue>*, ...)
|
||||
template <class TValue, class TCreateContext>
|
||||
class FullTypedCacheHelperFns : public BasicTypedCacheHelperFns<TValue> {
|
||||
public:
|
||||
CACHE_TYPE_DEFS();
|
||||
|
||||
protected:
|
||||
using typename BasicTypedCacheHelperFns<TValue>::TValuePtr;
|
||||
using BasicTypedCacheHelperFns<TValue>::DownCastValue;
|
||||
using BasicTypedCacheHelperFns<TValue>::UpCastValue;
|
||||
|
||||
static size_t Size(ObjectPtr v) {
|
||||
TValuePtr value = DownCastValue(v);
|
||||
auto slice = value->ContentSlice();
|
||||
return slice.size();
|
||||
}
|
||||
|
||||
static Status SaveTo(ObjectPtr v, size_t from_offset, size_t length,
|
||||
char* out) {
|
||||
TValuePtr value = DownCastValue(v);
|
||||
auto slice = value->ContentSlice();
|
||||
assert(from_offset < slice.size());
|
||||
assert(from_offset + length <= slice.size());
|
||||
std::copy_n(slice.data() + from_offset, length, out);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static Status Create(const Slice& data, CompressionType type,
|
||||
CacheTier source, CreateContext* context,
|
||||
MemoryAllocator* allocator, ObjectPtr* out_obj,
|
||||
size_t* out_charge) {
|
||||
std::unique_ptr<TValue> value = nullptr;
|
||||
if (source != CacheTier::kVolatileTier) {
|
||||
return Status::InvalidArgument();
|
||||
}
|
||||
if constexpr (sizeof(TCreateContext) > 0) {
|
||||
TCreateContext* tcontext = static_cast<TCreateContext*>(context);
|
||||
tcontext->Create(&value, out_charge, data, type, allocator);
|
||||
} else {
|
||||
TCreateContext::Create(&value, out_charge, data, type, allocator);
|
||||
}
|
||||
*out_obj = UpCastValue(value.release());
|
||||
return Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
// In its own class to try to minimize the number of distinct CacheItemHelper
|
||||
// instances (e.g. don't vary by CachePtr)
|
||||
template <class TValue, class TCreateContext, CacheEntryRole kRole>
|
||||
class FullTypedCacheHelper
|
||||
: public FullTypedCacheHelperFns<TValue, TCreateContext> {
|
||||
public:
|
||||
static const Cache::CacheItemHelper* GetFullHelper() {
|
||||
static const Cache::CacheItemHelper kHelper{
|
||||
kRole,
|
||||
&FullTypedCacheHelper::Delete,
|
||||
&FullTypedCacheHelper::Size,
|
||||
&FullTypedCacheHelper::SaveTo,
|
||||
&FullTypedCacheHelper::Create,
|
||||
BasicTypedCacheHelper<TValue, kRole>::GetBasicHelper()};
|
||||
return &kHelper;
|
||||
}
|
||||
};
|
||||
|
||||
// FullTypedCacheHelper - Used for secondary cache compatible storage of
|
||||
// objects of type TValue. In addition to BasicTypedCacheInterface constraints,
|
||||
// we require TValue::ContentSlice() to return persistable data. This
|
||||
// simplifies usage for the normal case of simple secondary cache compatibility
|
||||
// (can give you a Slice to the data already in memory). In addition to
|
||||
// TCreateContext performing the role of Cache::CreateContext, it is also
|
||||
// expected to provide a function Create(std::unique_ptr<TValue>* value,
|
||||
// size_t* out_charge, const Slice& data, MemoryAllocator* allocator) for
|
||||
// creating new TValue.
|
||||
template <class TValue, class TCreateContext,
|
||||
CacheEntryRole kRole = TValue::kCacheEntryRole,
|
||||
typename CachePtr = Cache*>
|
||||
class FullTypedCacheInterface
|
||||
: public BasicTypedCacheInterface<TValue, kRole, CachePtr>,
|
||||
public FullTypedCacheHelper<TValue, TCreateContext, kRole> {
|
||||
public:
|
||||
CACHE_TYPE_DEFS();
|
||||
using typename BasicTypedCacheInterface<TValue, kRole, CachePtr>::TypedHandle;
|
||||
using typename BasicTypedCacheInterface<TValue, kRole,
|
||||
CachePtr>::TypedAsyncLookupHandle;
|
||||
using typename BasicTypedCacheHelperFns<TValue>::TValuePtr;
|
||||
using BasicTypedCacheHelper<TValue, kRole>::GetBasicHelper;
|
||||
using FullTypedCacheHelper<TValue, TCreateContext, kRole>::GetFullHelper;
|
||||
using BasicTypedCacheHelperFns<TValue>::UpCastValue;
|
||||
using BasicTypedCacheHelperFns<TValue>::DownCastValue;
|
||||
// ctor
|
||||
using BasicTypedCacheInterface<TValue, kRole,
|
||||
CachePtr>::BasicTypedCacheInterface;
|
||||
|
||||
// Insert with SecondaryCache compatibility (subject to CacheTier).
|
||||
// (Basic Insert() also inherited.)
|
||||
inline Status InsertFull(
|
||||
const Slice& key, TValuePtr value, size_t charge,
|
||||
TypedHandle** handle = nullptr, Priority priority = Priority::LOW,
|
||||
CacheTier lowest_used_cache_tier = CacheTier::kNonVolatileBlockTier,
|
||||
const Slice& compressed = Slice(),
|
||||
CompressionType type = CompressionType::kNoCompression) {
|
||||
auto untyped_handle = reinterpret_cast<Handle**>(handle);
|
||||
auto helper = lowest_used_cache_tier > CacheTier::kVolatileTier
|
||||
? GetFullHelper()
|
||||
: GetBasicHelper();
|
||||
return this->cache_->Insert(key, UpCastValue(value), helper, charge,
|
||||
untyped_handle, priority, compressed, type);
|
||||
}
|
||||
|
||||
// Like SecondaryCache::InsertSaved, with SecondaryCache compatibility
|
||||
// (subject to CacheTier).
|
||||
inline Status InsertSaved(
|
||||
const Slice& key, const Slice& data, TCreateContext* create_context,
|
||||
Priority priority = Priority::LOW,
|
||||
CacheTier lowest_used_cache_tier = CacheTier::kNonVolatileBlockTier,
|
||||
size_t* out_charge = nullptr) {
|
||||
ObjectPtr value;
|
||||
size_t charge;
|
||||
Status st = GetFullHelper()->create_cb(
|
||||
data, kNoCompression, CacheTier::kVolatileTier, create_context,
|
||||
this->cache_->memory_allocator(), &value, &charge);
|
||||
if (out_charge) {
|
||||
*out_charge = charge;
|
||||
}
|
||||
if (st.ok()) {
|
||||
st = InsertFull(key, DownCastValue(value), charge, nullptr /*handle*/,
|
||||
priority, lowest_used_cache_tier);
|
||||
} else {
|
||||
GetFullHelper()->del_cb(value, this->cache_->memory_allocator());
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
// Lookup with SecondaryCache support (subject to CacheTier).
|
||||
// (Basic Lookup() also inherited.)
|
||||
inline TypedHandle* LookupFull(
|
||||
const Slice& key, TCreateContext* create_context = nullptr,
|
||||
Priority priority = Priority::LOW, Statistics* stats = nullptr,
|
||||
CacheTier lowest_used_cache_tier = CacheTier::kNonVolatileBlockTier) {
|
||||
if (lowest_used_cache_tier > CacheTier::kVolatileTier) {
|
||||
return static_cast<TypedHandle*>(this->cache_->Lookup(
|
||||
key, GetFullHelper(), create_context, priority, stats));
|
||||
} else {
|
||||
return BasicTypedCacheInterface<TValue, kRole, CachePtr>::Lookup(key,
|
||||
stats);
|
||||
}
|
||||
}
|
||||
|
||||
inline void StartAsyncLookupFull(
|
||||
TypedAsyncLookupHandle& async_handle,
|
||||
CacheTier lowest_used_cache_tier = CacheTier::kNonVolatileBlockTier) {
|
||||
if (lowest_used_cache_tier > CacheTier::kVolatileTier) {
|
||||
async_handle.helper = GetFullHelper();
|
||||
this->cache_->StartAsyncLookup(async_handle);
|
||||
} else {
|
||||
BasicTypedCacheInterface<TValue, kRole, CachePtr>::StartAsyncLookup(
|
||||
async_handle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// FullTypedSharedCacheInterface - Like FullTypedCacheInterface but with a
|
||||
// shared_ptr<Cache> for keeping Cache alive.
|
||||
template <class TValue, class TCreateContext,
|
||||
CacheEntryRole kRole = TValue::kCacheEntryRole>
|
||||
using FullTypedSharedCacheInterface =
|
||||
FullTypedCacheInterface<TValue, TCreateContext, kRole,
|
||||
std::shared_ptr<Cache>>;
|
||||
|
||||
#undef CACHE_TYPE_DEFS
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -14,7 +14,7 @@ find_library(zstd_LIBRARIES
|
||||
HINTS ${zstd_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(zstd DEFAULT_MSG zstd_LIBRARIES ZSTD_INCLUDE_DIRS)
|
||||
find_package_handle_standard_args(zstd DEFAULT_MSG zstd_LIBRARIES zstd_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
zstd_LIBRARIES
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user