mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 11:32:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10dfc6acb8 | ||
|
|
d46d4b7f82 | ||
|
|
a127d1533b | ||
|
|
928ebd5b8b | ||
|
|
afcf52a996 | ||
|
|
e688e7b235 |
No files matched your search
@@ -0,0 +1,9 @@
|
||||
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,17 +0,0 @@
|
||||
name: setup-jdk-on-macos
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set up JDK on macos
|
||||
run: |-
|
||||
set -euo pipefail
|
||||
|
||||
JAVA_HOME="$(/usr/libexec/java_home -v 21)"
|
||||
|
||||
echo "JAVA_HOME=${JAVA_HOME}" >> "$GITHUB_ENV"
|
||||
echo "JAVAC_ARGS=--release 8" >> "$GITHUB_ENV"
|
||||
echo "${JAVA_HOME}/bin" >> "$GITHUB_PATH"
|
||||
echo "Using JDK at ${JAVA_HOME}"
|
||||
"${JAVA_HOME}/bin/java" -version
|
||||
"${JAVA_HOME}/bin/javac" -version
|
||||
shell: bash
|
||||
@@ -11,49 +11,8 @@ inputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Detect Visual Studio generator
|
||||
id: detect_vs
|
||||
shell: pwsh
|
||||
run: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$vswhere = Join-Path ([Environment]::GetFolderPath("ProgramFilesX86")) "Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
if (!(Test-Path $vswhere)) {
|
||||
throw "vswhere.exe was not found at $vswhere"
|
||||
}
|
||||
|
||||
$vsInstallationsJson = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -format json -utf8
|
||||
$vsInstallations = @($vsInstallationsJson | ConvertFrom-Json)
|
||||
if ($vsInstallations.Count -eq 0) {
|
||||
throw "No Visual Studio instance with C++ tools was found."
|
||||
}
|
||||
|
||||
$vsInstallation = $vsInstallations[0]
|
||||
$vsMajor = [int]$vsInstallation.catalog.productLineVersion
|
||||
if ($vsMajor -eq 0) {
|
||||
$vsMajor = [int]($vsInstallation.installationVersion.Split(".")[0])
|
||||
}
|
||||
|
||||
$generatorLine = cmake --help | Where-Object {
|
||||
$_ -match "^\*?\s*Visual Studio $vsMajor\s+\d{4}\s*="
|
||||
} | Select-Object -First 1
|
||||
if (!$generatorLine) {
|
||||
throw "CMake does not support a Visual Studio generator for VS major version $vsMajor."
|
||||
}
|
||||
|
||||
$cmakeGenerator = ($generatorLine -replace "^\*?\s*", "" -replace "\s*=.*$", "").Trim()
|
||||
$vsVersionRange = "[{0}.0,{1}.0)" -f $vsMajor, ($vsMajor + 1)
|
||||
|
||||
echo "Detected Visual Studio at $($vsInstallation.installationPath)"
|
||||
echo "Using CMake generator: $cmakeGenerator"
|
||||
echo "Using setup-msbuild vs-version: $vsVersionRange"
|
||||
|
||||
"CMAKE_GENERATOR=$cmakeGenerator" >> $env:GITHUB_ENV
|
||||
"vs_version=$vsVersionRange" >> $env:GITHUB_OUTPUT
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
with:
|
||||
vs-version: ${{ steps.detect_vs.outputs.vs_version }}
|
||||
uses: microsoft/setup-msbuild@v1.3.1
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
@@ -73,23 +32,15 @@ runs:
|
||||
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.2.2
|
||||
SNAPPY_INCLUDE: ${{ github.workspace }}/thirdparty/snappy-1.2.2;${{ github.workspace }}/thirdparty/snappy-1.2.2/build
|
||||
SNAPPY_LIB_DEBUG: ${{ github.workspace }}/thirdparty/snappy-1.2.2/build/Debug/snappy.lib
|
||||
run: |-
|
||||
# NOTE: if ... Exit $LASTEXITCODE lines needed to exit and report failure
|
||||
echo ===================== Install Dependencies =====================
|
||||
if (!$env:JAVA_HOME -or !(Test-Path (Join-Path $env:JAVA_HOME "bin\javac.exe"))) {
|
||||
$javac = Get-Command javac -ErrorAction SilentlyContinue
|
||||
if (!$javac) {
|
||||
Write-Error "javac was not found on PATH and JAVA_HOME is not set."
|
||||
Exit 1
|
||||
}
|
||||
$env:JAVA_HOME = Split-Path (Split-Path $javac.Source -Parent) -Parent
|
||||
}
|
||||
echo "JAVA_HOME=$env:JAVA_HOME"
|
||||
& "$env:JAVA_HOME\bin\java.exe" -version
|
||||
& "$env:JAVA_HOME\bin\javac.exe" -version
|
||||
choco install liberica8jdk -y
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
mkdir $Env:THIRDPARTY_HOME
|
||||
cd $Env:THIRDPARTY_HOME
|
||||
echo "Building Snappy dependency..."
|
||||
@@ -102,11 +53,11 @@ runs:
|
||||
cd build
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" .. -DSNAPPY_BUILD_TESTS=OFF -DSNAPPY_BUILD_BENCHMARKS=OFF
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
cmake --build . --config Debug --parallel 32 -- /p:Platform=x64
|
||||
msbuild Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
echo ======================== Build RocksDB =========================
|
||||
cd ${{ github.workspace }}
|
||||
$env:Path = "$env:JAVA_HOME\bin;" + $env:Path
|
||||
$env:Path = $env:JAVA_HOME + ";" + $env:Path
|
||||
mkdir build
|
||||
cd build
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DWIN_CI=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DXPRESS=1 -DJNI=1 ..
|
||||
@@ -114,7 +65,7 @@ runs:
|
||||
cd ..
|
||||
echo "Building with VS version: $Env:CMAKE_GENERATOR"
|
||||
# use more parallel processes than the number of processes available, as most of the compile command would be cache hit
|
||||
cmake --build build --config Debug --parallel 32 -- /p:LinkIncremental=false /p:Platform=x64
|
||||
msbuild build/rocksdb.sln /m:32 /p:LinkIncremental=false -property:Configuration=Debug -property:Platform=x64
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
echo ========================= Test RocksDB =========================
|
||||
$suiteRun = "${{ inputs.suite-run }}"
|
||||
|
||||
@@ -113,10 +113,11 @@ jobs:
|
||||
- run: "USE_FOLLY=1 LIB_MODE=static DEBUG_LEVEL=0 V=1 make -j20 release"
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 -DCMAKE_BUILD_TYPE=Release .. && make VERBOSE=1 -j20 && ctest -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-windows-msvc-avx2:
|
||||
build-windows-vs2022-avx2:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: windows-8-core
|
||||
runs-on: windows-2022
|
||||
env:
|
||||
CMAKE_GENERATOR: Visual Studio 17 2022
|
||||
CMAKE_PORTABLE: AVX2
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
|
||||
@@ -314,23 +314,6 @@ jobs:
|
||||
- run: CC=clang-21 CXX=clang++-21 USE_CLANG=1 make -j32 all microbench
|
||||
- run: make clean
|
||||
- run: CC=clang-21 CXX=clang++-21 USE_CLANG=1 DEBUG_LEVEL=0 make -j32 release
|
||||
- name: Install clang-format-21 for C API gen checks
|
||||
run: apt-get update && apt-get install -y clang-format-21
|
||||
- name: Check C API codegen (link-complete, backward-compatible, up to date)
|
||||
# Run the make target in STRICT mode so this core CI job HARD-FAILS rather
|
||||
# than silently skipping if a prerequisite (clang++, clang-format, or the
|
||||
# compat baseline ref) is ever missing. See `make check-c-api-gen`.
|
||||
run: |
|
||||
# The container checkout is owned by a different user, so git refuses to
|
||||
# operate on it ("dubious ownership") -- mark it safe for the `git fetch`
|
||||
# and the `git show` the compat checker runs.
|
||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
git config --global --add safe.directory '*'
|
||||
git fetch --no-tags --depth=1 origin main
|
||||
CXX=clang++-21 make check-c-api-gen \
|
||||
CHECK_C_API_GEN_STRICT=1 \
|
||||
API_COMPAT_REF=FETCH_HEAD \
|
||||
CLANG_FORMAT_BINARY=clang-format-21
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
@@ -542,8 +525,8 @@ jobs:
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================== Windows with Tests ======================= #
|
||||
# NOTE: some windows jobs are in "nightly" to save resources
|
||||
build-windows-msvc:
|
||||
if: needs.config.outputs.only_job == 'build-windows-msvc' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
build-windows-vs2022:
|
||||
if: needs.config.outputs.only_job == 'build-windows-vs2022' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on: windows-8-core
|
||||
strategy:
|
||||
@@ -560,6 +543,7 @@ jobs:
|
||||
suite_run: ""
|
||||
run_java: "true"
|
||||
env:
|
||||
CMAKE_GENERATOR: Visual Studio 17 2022
|
||||
CMAKE_PORTABLE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
@@ -619,6 +603,7 @@ jobs:
|
||||
needs: config
|
||||
runs-on: macos-15-xlarge
|
||||
env:
|
||||
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
@@ -630,7 +615,7 @@ jobs:
|
||||
cache-key-prefix: macos-java
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/setup-jdk-on-macos"
|
||||
- uses: "./.github/actions/install-jdk8-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
@@ -646,6 +631,8 @@ jobs:
|
||||
if: needs.config.outputs.only_job == 'build-macos-java-static' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on: macos-15-xlarge
|
||||
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
|
||||
@@ -656,7 +643,7 @@ jobs:
|
||||
cache-key-prefix: macos-java-static
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/setup-jdk-on-macos"
|
||||
- uses: "./.github/actions/install-jdk8-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
@@ -672,6 +659,8 @@ jobs:
|
||||
if: needs.config.outputs.only_job == 'build-macos-java-static-universal' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on: macos-15-xlarge
|
||||
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
|
||||
@@ -682,7 +671,7 @@ jobs:
|
||||
cache-key-prefix: macos-java-static-universal
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/setup-jdk-on-macos"
|
||||
- uses: "./.github/actions/install-jdk8-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
make_config.mk
|
||||
rocksdb.pc
|
||||
.build_signature
|
||||
build_tools/__pycache__/
|
||||
|
||||
*.a
|
||||
*.arc
|
||||
@@ -90,8 +88,6 @@ fbcode/
|
||||
fbcode
|
||||
buckifier/*.pyc
|
||||
buckifier/__pycache__
|
||||
tools/__pycache__/
|
||||
tools/c_api_gen/__pycache__/
|
||||
.arcconfig
|
||||
|
||||
compile_commands.json
|
||||
|
||||
@@ -35,7 +35,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"db/blob/blob_file_partition_manager.cc",
|
||||
"db/blob/blob_file_reader.cc",
|
||||
"db/blob/blob_garbage_meter.cc",
|
||||
"db/blob/blob_gen2_format.cc",
|
||||
"db/blob/blob_log_format.cc",
|
||||
"db/blob/blob_log_sequential_reader.cc",
|
||||
"db/blob/blob_log_writer.cc",
|
||||
@@ -5796,5 +5795,3 @@ cpp_unittest_wrapper(name="write_unprepared_transaction_test",
|
||||
|
||||
|
||||
export_file(name = "tools/db_crashtest.py")
|
||||
|
||||
export_file(name = "tools/fault_injection_log_parser.py")
|
||||
@@ -38,7 +38,7 @@ This document provides guidance for generating and reviewing code in the RocksDB
|
||||
|
||||
**SIMD and Vectorization:** Leverage SIMD instructions (SSE, AVX) for data-parallel operations when appropriate. Structure data to enable auto-vectorization by the compiler. Consider explicit SIMD intrinsics for critical hot paths like checksum computation, encoding/decoding, and bulk data processing.
|
||||
|
||||
**Branch Prediction:** Minimize unpredictable branches in hot paths. Use `LIKELY`/`UNLIKELY` macros to hint branch prediction, e.g. for error cases and other rare or otherwise costly cases, but NOT for predicting popular configurations. Consider branchless alternatives for simple conditionals. Order switch cases and if-else chains by frequency.
|
||||
**Branch Prediction:** Minimize unpredictable branches in hot paths. Use `LIKELY`/`UNLIKELY` macros to hint branch prediction. Consider branchless alternatives for simple conditionals. Order switch cases and if-else chains by frequency.
|
||||
|
||||
**Memory and Resource Management:** Be mindful of memory allocations, especially in hot paths. Use RAII patterns, smart pointers, and RocksDB's memory management utilities appropriately.
|
||||
|
||||
@@ -244,15 +244,26 @@ from an implementation detail instead of an explicit option.
|
||||
/usr/local/bin/python3 buckifier/buckify_rocksdb.py to update it
|
||||
* For -j in make command, use the number of CPU cores to decide it.
|
||||
|
||||
### Avoiding mixed build modes with Make (use `AUTO_CLEAN=1`)
|
||||
### When to run `make clean` (avoid mixing build modes)
|
||||
|
||||
Object files are written to the same paths regardless of build flags, so
|
||||
reusing objects from a prior build with different flags causes confusing
|
||||
linker errors, etc. This problem is essentially avoidable by ALWAYS using
|
||||
`AUTO_CLEAN=1 make -j<n> <something>` for manual make invocations. This
|
||||
will automatically clean object files if the build parameters/flavor have
|
||||
changed. The `build_tools/rockstest.sh` / `rocksptest.sh` helpers described
|
||||
below set `AUTO_CLEAN=1` for you.
|
||||
The Makefile does **not** track build mode, so object files from a prior
|
||||
build are silently reused even when compiled with different flags, leading
|
||||
to confusing linker errors, sanitizer false negatives, ODR violations, or
|
||||
"phantom" bugs.
|
||||
|
||||
Run `make clean` before switching any of these:
|
||||
* **`ASSERT_STATUS_CHECKED=1` ↔ unset** — changes the `Status` class layout (ABI break).
|
||||
* **Sanitizer builds** — toggling any of `COMPILE_WITH_ASAN=1`,
|
||||
`COMPILE_WITH_UBSAN=1`, `COMPILE_WITH_TSAN=1` on/off.
|
||||
* `DEBUG_LEVEL=0` (release) ↔ `DEBUG_LEVEL=1` (debug, default for `make dbg`).
|
||||
* Different compilers, `OPT` levels, or other flags affecting codegen/ABI.
|
||||
|
||||
**Notable exception:** `DEBUG_LEVEL=2` can be safely mixed with
|
||||
`DEBUG_LEVEL=1` — rebuild a subset of files with `DEBUG_LEVEL=2` to get
|
||||
extra/more accurate runtime checks for those files without a full clean.
|
||||
|
||||
When in doubt, `make clean` is cheap insurance compared to chasing a
|
||||
phantom bug.
|
||||
|
||||
### Source checks
|
||||
* Run `make check-sources` before committing. This catches non-ASCII
|
||||
@@ -261,22 +272,6 @@ below set `AUTO_CLEAN=1` for you.
|
||||
smart quotes, etc.) in comments or strings -- use ASCII equivalents
|
||||
(`--` instead of em dash, `'` instead of smart quote, etc.).
|
||||
|
||||
### License headers
|
||||
* Every new source file needs a license header. For a file that does **not**
|
||||
carry an outside/third-party copyright, use the standard Meta dual-licensed
|
||||
header (the dual-license designation is required -- a bare
|
||||
"All Rights Reserved" copyright is not an acceptable open-source header):
|
||||
```
|
||||
// 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).
|
||||
```
|
||||
Use a `#` comment prefix instead of `//` for shell, Python, and Makefile
|
||||
fragments.
|
||||
* Files derived from an external source (e.g. LevelDB) keep their original
|
||||
upstream copyright line in addition to the header above.
|
||||
|
||||
### RTTI and dynamic_cast
|
||||
* Production code and `db_stress` must build in **release mode
|
||||
(`-fno-rtti`)**. Do not use `dynamic_cast` anywhere except unit tests.
|
||||
@@ -314,22 +309,13 @@ rather than relying on libstdc++ transitive includes.
|
||||
* Don't use sleep to wait for certain events to happen. This will cause test to
|
||||
be flaky. Instead, use sync point to synchronize thread progress.
|
||||
* Cap unit test execution with 60 seconds timeout.
|
||||
* To build and run unit tests locally, prefer these helper scripts:
|
||||
* `build_tools/rocksptest.sh <test_binary> [more_binaries...] [args...]`
|
||||
builds the binary(ies) with parallel make and `AUTO_CLEAN=1` and runs
|
||||
them under gtest-parallel, sharding the test cases across CPUs. Prefer
|
||||
this whenever running more than a couple of test cases, e.g.
|
||||
`build_tools/rocksptest.sh table_test` or
|
||||
`build_tools/rocksptest.sh db_test env_test --gtest_filter=*Foo*`.
|
||||
* `build_tools/rockstest.sh <test_binary> [args...]` builds with parallel
|
||||
make and `AUTO_CLEAN=1` and runs the binary directly (serially).
|
||||
Use it only for a very small number of test cases, e.g.
|
||||
`build_tools/rockstest.sh db_test --gtest_filter=*MixedSlowdown*`.
|
||||
* After writing a test, stress-test for flakiness (AUTO_CLEAN handles the
|
||||
rebuild needed by the `COERCE_CONTEXT_SWITCH=1` flag change):
|
||||
* When there are multiple unit tests need to be executed, try to use
|
||||
gtest_parallel.py if available. E.g.
|
||||
python3 ${GTEST_PARALLEL}/gtest_parallel.py ./table_test
|
||||
* After writing a test, stress-test for flakiness:
|
||||
```bash
|
||||
COERCE_CONTEXT_SWITCH=1 build_tools/rockstest.sh {test_binary} -r100 \
|
||||
--gtest_filter="*YourTestName*"
|
||||
COERCE_CONTEXT_SWITCH=1 make {test_binary}
|
||||
./{test_binary} --gtest_filter="*YourTestName*" --gtest_repeat=5
|
||||
```
|
||||
* For CI-style flaky tests that do not reproduce with `gtest_parallel.py`,
|
||||
`--gtest_repeat`, or normal coerce-mode runs, inspect
|
||||
@@ -386,13 +372,12 @@ rather than relying on libstdc++ transitive includes.
|
||||
* Blog post authors must be defined in `docs/_data/authors.yml` to be displayed
|
||||
|
||||
### Final verification of the change
|
||||
* Execute `AUTO_CLEAN=1 make check` to build all of the changes and execute all
|
||||
of the tests. `AUTO_CLEAN=1` ensures a clean rebuild if your previous build
|
||||
used different parameters. Note that executing all of the tests could take
|
||||
multiple minutes.
|
||||
* Run `AUTO_CLEAN=1 ASSERT_STATUS_CHECKED=1 make check` to verify all Status
|
||||
objects are properly checked. This catches missing error handling that can
|
||||
lead to silent data corruption.
|
||||
* Execute make clean to clean all of the changes.
|
||||
* Execute make check to build all of the changes and execute all of the tests.
|
||||
Note that executing all of the tests could take multiple minutes.
|
||||
* Run `ASSERT_STATUS_CHECKED=1 make check` to verify all Status objects are
|
||||
properly checked. This catches missing error handling that can lead to
|
||||
silent data corruption.
|
||||
|
||||
### Monitoring make check progress
|
||||
* Use `make check-progress` to get machine-parseable JSON progress while
|
||||
@@ -400,7 +385,7 @@ rather than relying on libstdc++ transitive includes.
|
||||
builds without timeout issues.
|
||||
* Run `make check` in background, then poll progress:
|
||||
```bash
|
||||
AUTO_CLEAN=1 make check &
|
||||
make check &
|
||||
# Poll periodically:
|
||||
make check-progress
|
||||
```
|
||||
@@ -425,10 +410,9 @@ rather than relying on libstdc++ transitive includes.
|
||||
|
||||
### Executing benchmark using db_bench
|
||||
* Since the goal is to measure performance, we need to build a release binary
|
||||
using `AUTO_CLEAN=1 DEBUG_LEVEL=0 make db_bench`. If there is an engine
|
||||
crash due to a bug, switch back to a debug build with
|
||||
`AUTO_CLEAN=1 make dbg`; `AUTO_CLEAN=1` handles the release<->debug rebuild
|
||||
automatically.
|
||||
using `make clean && DEBUG_LEVEL=0 make db_bench`. If there is an engine
|
||||
crash due to bug, we need to switch back to debug build. Make sure to run
|
||||
`make clean` before running `make dbg`.
|
||||
|
||||
### Formatting code
|
||||
* After making change, use `make format-auto` to auto-apply formatting without
|
||||
|
||||
+7
-1
@@ -420,6 +420,13 @@ if(WITH_NUMA)
|
||||
list(APPEND THIRDPARTY_LIBS NUMA::NUMA)
|
||||
endif()
|
||||
|
||||
option(WITH_TBB "build with Threading Building Blocks (TBB)" OFF)
|
||||
if(WITH_TBB)
|
||||
find_package(TBB REQUIRED)
|
||||
add_definitions(-DTBB)
|
||||
list(APPEND THIRDPARTY_LIBS TBB::TBB)
|
||||
endif()
|
||||
|
||||
# Stall notifications eat some performance from inserts
|
||||
option(DISABLE_STALL_NOTIF "Build with stall notifications" OFF)
|
||||
if(DISABLE_STALL_NOTIF)
|
||||
@@ -768,7 +775,6 @@ set(SOURCES
|
||||
db/blob/blob_file_meta.cc
|
||||
db/blob/blob_file_reader.cc
|
||||
db/blob/blob_garbage_meter.cc
|
||||
db/blob/blob_gen2_format.cc
|
||||
db/blob/blob_log_format.cc
|
||||
db/blob/blob_log_sequential_reader.cc
|
||||
db/blob/blob_log_writer.cc
|
||||
|
||||
+4
-8
@@ -1,16 +1,12 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 11.6.0 (07/02/2026)
|
||||
### New Features
|
||||
* Added EXPERIMENTAL embedded blob SST support through `SstFileWriter::OpenWithEmbeddedBlobs()`, storing eligible large values as same-file blob records in block-based SST files and resolving them transparently for reads. This niche feature currently supports uncompressed embedded blobs only; compression options are placeholders and compression support is deferred to follow-up work.
|
||||
|
||||
### Public API Changes
|
||||
* Expanded the C API (`include/rocksdb/c.h`) with a large set of new `rocksdb_*` functions, mostly option getters/setters plus table-properties, job/event-listener, and metadata accessors, a WAL filter, a ReadOptions table filter, and a backup exclude-files callback. Many are now produced by a new semi-automated generator (`tools/c_api_gen/`) from the C++ headers; `include/rocksdb/c.h` remains a single self-contained header and the signatures of pre-existing functions are unchanged.
|
||||
|
||||
## 11.5.2 (06/25/2026)
|
||||
### Bug Fixes
|
||||
* Reverted PR14831 that made range_lock_manager aware of reverse-order CF
|
||||
* Fixed a bug in `RandomAccessFileReader::ReadAsync` where an already-aligned direct-IO read request with a null `scratch` and a caller-provided `aligned_buf` would take the "already aligned" fast path and submit the null buffer to the underlying async read (e.g. a null iovec base to io_uring, failing with EFAULT). This could surface as spurious iterator failures during async prefetch (MultiScan with async IO) on direct-IO databases. The async path now allocates a backing buffer in this case, matching the synchronous `Read` path.
|
||||
|
||||
## 11.5.1 (06/24/2026)
|
||||
### Bug Fixes
|
||||
* Fixed a bug where closing a read-only DB instance could delete live SST files created by a concurrent read-write DB sharing the same directory.
|
||||
|
||||
## 11.5.0 (06/16/2026)
|
||||
|
||||
@@ -288,7 +288,7 @@ endif
|
||||
endif
|
||||
|
||||
export JAVAC_ARGS
|
||||
CLEAN_FILES += rocksdb.pc
|
||||
CLEAN_FILES += make_config.mk rocksdb.pc
|
||||
|
||||
ifeq ($(V), 1)
|
||||
$(info $(shell uname -a))
|
||||
@@ -764,6 +764,7 @@ util/build_version.cc: $(filter-out $(OBJ_DIR)/util/build_version.o, $(LIB_OBJEC
|
||||
$(AM_V_at)$(gen_build_version) > $@
|
||||
endif
|
||||
CLEAN_FILES += util/build_version.cc
|
||||
|
||||
default: all
|
||||
|
||||
#-----------------------------------------------
|
||||
@@ -808,8 +809,7 @@ 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 clang-tidy \
|
||||
check-c-api-c_test
|
||||
uninstall analyze tools tools_lib check-headers checkout_folly clang-tidy
|
||||
|
||||
# Auto-configure git hooks on first build so developers do not need to run
|
||||
# "make install-hooks" manually. This is a no-op if already set.
|
||||
@@ -1127,89 +1127,8 @@ ifndef SKIP_FORMAT_BUCK_CHECKS
|
||||
$(MAKE) check-buck-targets
|
||||
$(MAKE) check-sources
|
||||
$(MAKE) check-workflow-yaml
|
||||
$(MAKE) check-c-api-gen
|
||||
endif
|
||||
|
||||
# Check that the auto-generated C API files are up to date. It regenerates the
|
||||
# fragments and the inlined c.h/c.cc and compares them against a snapshot of the
|
||||
# checked-in copies (no net change when everything is up to date). It requires
|
||||
# clang++ (libclang, used to parse the C++ headers) and clang-format. When those
|
||||
# are unavailable the staleness/compat sub-checks are SKIPPED with a message so
|
||||
# `make check` still works without the codegen toolchain; the link-completeness
|
||||
# sub-check always runs (it needs no toolchain).
|
||||
#
|
||||
# Pin the formatter to match CI by setting CLANG_FORMAT_BINARY, e.g.:
|
||||
# make check-c-api-gen CLANG_FORMAT_BINARY=clang-format-21
|
||||
# This target is part of `make check` and is skipped by SKIP_FORMAT_BUCK_CHECKS.
|
||||
#
|
||||
# Set CHECK_C_API_GEN_STRICT=1 to turn every "skip" below into a hard error, so a
|
||||
# core CI job that runs this target cannot silently regress to a no-op if a
|
||||
# prerequisite (clang++, or the compat baseline ref) goes missing. The dedicated
|
||||
# build-linux-clang-21-no_test_run CI job runs this target with the flag set.
|
||||
# Any non-empty value other than 0/no/false enables strict mode.
|
||||
CLANG_FORMAT_BINARY ?=
|
||||
# Backward-compatibility baseline for the C API (signature-level) check. CI
|
||||
# overrides this with the PR's merge target; locally it falls back to main /
|
||||
# origin/main and is skipped if neither resolves.
|
||||
API_COMPAT_REF ?= main
|
||||
CHECK_C_API_GEN_STRICT ?=
|
||||
check-c-api-gen:
|
||||
# Link-completeness is a property of the checked-in c.h/c.cc and needs no
|
||||
# clang toolchain, so it always runs: every declared public C API function
|
||||
# must have exactly one definition (guards against dropped wrappers that
|
||||
# would break downstream language bindings at link time).
|
||||
$(PYTHON) tools/c_api_gen/check_api_completeness.py
|
||||
# Backward-compatibility: no public C function may be removed or have its
|
||||
# signature changed vs the baseline. Skipped if the baseline ref is not
|
||||
# resolvable locally (CI passes an explicit ref), unless CHECK_C_API_GEN_STRICT.
|
||||
@strict=""; case "$(CHECK_C_API_GEN_STRICT)" in ""|0|no|NO|false|FALSE) ;; *) strict=1 ;; esac; \
|
||||
ref=""; \
|
||||
if git rev-parse --verify --quiet "$(API_COMPAT_REF)^{commit}" >/dev/null; then ref="$(API_COMPAT_REF)"; \
|
||||
elif git rev-parse --verify --quiet "origin/$(API_COMPAT_REF)^{commit}" >/dev/null; then ref="origin/$(API_COMPAT_REF)"; fi; \
|
||||
if [ -n "$$ref" ]; then \
|
||||
$(PYTHON) tools/c_api_gen/check_api_compatibility.py --ref "$$ref"; \
|
||||
elif [ -n "$$strict" ]; then \
|
||||
echo "ERROR: C API compat baseline '$(API_COMPAT_REF)' not resolvable and CHECK_C_API_GEN_STRICT is set" >&2; exit 1; \
|
||||
else \
|
||||
echo "Skipping C API backward-compatibility check ($(API_COMPAT_REF) not found; set API_COMPAT_REF)"; \
|
||||
fi
|
||||
# Staleness: regenerate and confirm the checked-in output is current. Needs a
|
||||
# clang++ (the generator parses C++ ASTs); detect one the way the generator
|
||||
# does (a clang in $(CXX) -- which may be ccache-prefixed/versioned -- else a
|
||||
# bare/versioned clang++ on PATH) rather than testing $(CXX) verbatim.
|
||||
@strict=""; case "$(CHECK_C_API_GEN_STRICT)" in ""|0|no|NO|false|FALSE) ;; *) strict=1 ;; esac; \
|
||||
cf_arg=""; \
|
||||
if [ -n "$(CLANG_FORMAT_BINARY)" ]; then cf_arg="--clang-format $(CLANG_FORMAT_BINARY)"; fi; \
|
||||
have_clang=""; \
|
||||
for c in $$(printf '%s\n' $(CXX) | grep -i clang) clang++ clang++-21 clang++-20 clang++-19 clang++-18 clang++-17 clang++-16 clang++-15 clang++-14 clang++-13; do \
|
||||
if command -v "$$c" >/dev/null 2>&1; then have_clang=1; break; fi; \
|
||||
done; \
|
||||
if [ -n "$$have_clang" ]; then \
|
||||
$(PYTHON) tools/c_api_gen/verify_generated_up_to_date.py $$cf_arg; \
|
||||
elif [ -n "$$strict" ]; then \
|
||||
echo "ERROR: no clang++ found and CHECK_C_API_GEN_STRICT is set; cannot run the C API staleness check" >&2; exit 1; \
|
||||
else \
|
||||
echo "Skipping C API codegen staleness check (no clang++ found; install clang++ or set CXX to a clang to enable)"; \
|
||||
fi
|
||||
|
||||
# Quick local validation for C API generation plus the focused C API test.
|
||||
# This verifies the checked-in generated fragments as well as the inlined
|
||||
# include/rocksdb/c.h and db/c.cc outputs, then runs c_test in an isolated
|
||||
# TEST_TMPDIR to avoid stale-state failures.
|
||||
check-c-api-c_test:
|
||||
$(PYTHON) tools/c_api_gen/verify_generated_up_to_date.py
|
||||
$(MAKE) c_test
|
||||
@tmpdir=$$(mktemp -d); \
|
||||
trap 'rm -rf "$$tmpdir"' EXIT; \
|
||||
echo "===== Running c_test with TEST_TMPDIR=$$tmpdir"; \
|
||||
if command -v timeout >/dev/null 2>&1; then \
|
||||
TEST_TMPDIR="$$tmpdir" timeout 60 ./c_test; \
|
||||
elif command -v gtimeout >/dev/null 2>&1; then \
|
||||
TEST_TMPDIR="$$tmpdir" gtimeout 60 ./c_test; \
|
||||
else \
|
||||
TEST_TMPDIR="$$tmpdir" ./c_test; \
|
||||
fi
|
||||
|
||||
# TODO add ldb_tests
|
||||
check_some: $(ROCKSDBTESTS_SUBSET)
|
||||
for t in $(ROCKSDBTESTS_SUBSET); do echo "===== Running $$t (`date`)"; ./$$t || exit 1; done
|
||||
@@ -1359,13 +1278,8 @@ rocksdb.h rocksdb.cc: build_tools/amalgamate.py Makefile $(LIB_SOURCES) unity.cc
|
||||
build_tools/amalgamate.py -I. -i./include unity.cc -x include/rocksdb/c.h -H rocksdb.h -o rocksdb.cc
|
||||
|
||||
clean: clean-ext-libraries-all clean-rocks clean-rocksjava
|
||||
# Removed here rather than in clean-rocks (via CLEAN_FILES) so the build-parameter
|
||||
# auto-clean, which runs clean-rocks, doesn't delete the make_config.mk already
|
||||
# included by the in-progress make.
|
||||
@rm -f make_config.mk
|
||||
|
||||
clean-not-downloaded: clean-ext-libraries-bin clean-rocks clean-not-downloaded-rocksjava
|
||||
@rm -f make_config.mk
|
||||
|
||||
clean-rocks:
|
||||
# Not practical to exactly match all versions/variants in naming (e.g. debug or not)
|
||||
@@ -1380,11 +1294,6 @@ clean-rocks:
|
||||
else \
|
||||
$(FIND) . -name "*.[oda]" -exec rm -f {} \; ; \
|
||||
fi
|
||||
# Remove the build signature(s) LAST. Done after the object files are gone so
|
||||
# that a Ctrl+C partway through clean leaves the old signature in place: it
|
||||
# still matches the leftover objects, so a later build with different
|
||||
# parameters is still detected (signature usefulness is preserved).
|
||||
@rm -f $(BUILD_SIG_FILE) jl/.build_signature jls/.build_signature
|
||||
|
||||
clean-rocksjava: clean-rocks
|
||||
rm -rf jl jls
|
||||
@@ -2418,101 +2327,6 @@ ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
endif
|
||||
export SHA256_CMD
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Build parameter change detection
|
||||
# ----------------------------------------------------------------------------
|
||||
# RocksDB writes object files to the same $(OBJ_DIR) paths regardless of
|
||||
# DEBUG_LEVEL, sanitizers (ASAN/TSAN/UBSAN), ASSERT_STATUS_CHECKED, RTTI, LTO,
|
||||
# COERCE_CONTEXT_SWITCH, etc. Most of those are applied in this Makefile after
|
||||
# `include make_config.mk` and are therefore NOT reflected in make_config.mk,
|
||||
# so switching them and rebuilding silently mixes incompatible object files.
|
||||
# To guard against that, we hash the fully-resolved compile/link parameters
|
||||
# (which already embed make_config.mk via PLATFORM_*) and compare against the
|
||||
# value recorded from the previous build. On mismatch the build stops so the
|
||||
# user can `make clean`. Knobs:
|
||||
# AUTO_CLEAN=1
|
||||
# run `make clean-rocks` automatically on mismatch
|
||||
# ALLOW_BUILD_PARAMETER_CHANGE=1
|
||||
# skip the check entirely (e.g. intentionally mixing DEBUG_LEVEL=1 and
|
||||
# DEBUG_LEVEL=2)
|
||||
# The signature is stored per-$(OBJ_DIR), so Java (jl/jls) builds are tracked
|
||||
# independently from the default build.
|
||||
BUILD_SIG_FILE := $(OBJ_DIR)/.build_signature
|
||||
# NOTE: deliberately NOT added to CLEAN_FILES. The signature is removed as the
|
||||
# very last step of clean-rocks so that an interrupted clean keeps it (see
|
||||
# clean-rocks), preserving change detection across a Ctrl+C'd clean.
|
||||
|
||||
# Goals that do not compile object files into the tree (pure utilities,
|
||||
# informational, source/config checks): the change check is irrelevant for them.
|
||||
BUILD_SIG_NONBUILD_GOALS := \
|
||||
clean clean-rocks clean-not-downloaded clean-rocksjava \
|
||||
clean-not-downloaded-rocksjava clean-ext-libraries-all \
|
||||
clean-ext-libraries-bin \
|
||||
format format-auto check-format check-buck-targets check-headers \
|
||||
check-sources check-workflow-yaml check-progress clang-tidy \
|
||||
tags tags0 package jclean checkout_folly build_folly \
|
||||
watch-log dump-log suggest-slow-tests list_all_tests gen-pc \
|
||||
gen_parallel_tests check-c-api-gen \
|
||||
setup-hooks install-hooks uninstall-hooks uninstall db_crashtest_tests
|
||||
# Goals that clean before building (depend on or invoke `clean`): they manage
|
||||
# their own freshness, so the check must not block them (it would error before
|
||||
# their built-in clean runs).
|
||||
BUILD_SIG_NONBUILD_GOALS += \
|
||||
release coverage build_size analyze analyze_incremental \
|
||||
asan_check asan_crash_test asan_crash_test_with_atomic_flush \
|
||||
asan_crash_test_with_txn asan_crash_test_with_best_efforts_recovery \
|
||||
whitebox_asan_crash_test blackbox_asan_crash_test \
|
||||
ubsan_check ubsan_crash_test ubsan_crash_test_with_atomic_flush \
|
||||
ubsan_crash_test_with_txn ubsan_crash_test_with_best_efforts_recovery \
|
||||
whitebox_ubsan_crash_test blackbox_ubsan_crash_test
|
||||
|
||||
# Goals that explicitly clean; never trip the check when one is requested.
|
||||
BUILD_SIG_CLEAN_GOALS := \
|
||||
clean clean-rocks clean-not-downloaded clean-rocksjava \
|
||||
clean-not-downloaded-rocksjava clean-ext-libraries-all \
|
||||
clean-ext-libraries-bin
|
||||
|
||||
BUILD_SIG_GOALS := $(if $(MAKECMDGOALS),$(MAKECMDGOALS),all)
|
||||
BUILD_SIG_DO_BUILD := $(filter-out $(BUILD_SIG_NONBUILD_GOALS),$(BUILD_SIG_GOALS))
|
||||
BUILD_SIG_CLEANING := $(filter $(BUILD_SIG_CLEAN_GOALS),$(MAKECMDGOALS))
|
||||
# Dry runs (-n/--just-print) must not write the stamp, clean, or error; the
|
||||
# parse-time $(shell) below would otherwise run even under -n. Note that
|
||||
# actual options are canonicalized and shorted as possible such that all
|
||||
# short options are in the first word of MAKEFLAGS.
|
||||
BUILD_SIG_DRYRUN := $(findstring n,$(firstword -$(MAKEFLAGS)))
|
||||
|
||||
# Only enforce at the top level. Sub-makes (e.g. `check` invokes
|
||||
# $(MAKE) gen_parallel_tests / check-c-api-gen / check_0) inherit the parent's
|
||||
# build flags, so the top-level check already covers them; re-checking inside a
|
||||
# sub-make only causes spurious failures.
|
||||
ifneq ($(ALLOW_BUILD_PARAMETER_CHANGE),1)
|
||||
ifeq ($(MAKELEVEL),0)
|
||||
ifeq ($(BUILD_SIG_DRYRUN),)
|
||||
ifeq ($(BUILD_SIG_CLEANING),)
|
||||
ifneq ($(BUILD_SIG_DO_BUILD),)
|
||||
BUILD_SIG := $(shell printf '%s' '$(CC)|$(CXX)|$(CFLAGS)|$(CXXFLAGS)|$(LDFLAGS)|$(EXEC_LDFLAGS)' | $(SHA256_CMD) | cut -d ' ' -f 1)
|
||||
BUILD_SIG_OLD := $(shell cat $(BUILD_SIG_FILE) 2>/dev/null)
|
||||
ifneq ($(BUILD_SIG_OLD),)
|
||||
ifneq ($(BUILD_SIG_OLD),$(BUILD_SIG))
|
||||
ifeq ($(AUTO_CLEAN),1)
|
||||
$(info *** Build parameters changed since last build (OBJ_DIR=$(OBJ_DIR)); running 'make clean-rocks' because AUTO_CLEAN=1)
|
||||
BUILD_SIG_CLEAN_OUTPUT := $(shell $(MAKE) clean-rocks 1>&2)
|
||||
ifneq ($(.SHELLSTATUS),0)
|
||||
$(error AUTO_CLEAN: 'make clean-rocks' failed (exit $(.SHELLSTATUS)); not building against a partially-cleaned tree)
|
||||
endif
|
||||
else
|
||||
$(error Build parameters changed since the last build (OBJ_DIR=$(OBJ_DIR)). Existing object files are stale and must be removed. Run 'make clean', or set AUTO_CLEAN=1 to clean automatically, or ALLOW_BUILD_PARAMETER_CHANGE=1 to build anyway)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
# Record the current signature for the next build.
|
||||
BUILD_SIG_WRITE := $(shell mkdir -p $(OBJ_DIR) 2>/dev/null; printf '%s\n' '$(BUILD_SIG)' > $(BUILD_SIG_FILE))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
zlib-$(ZLIB_VER).tar.gz:
|
||||
curl --fail --output zlib-$(ZLIB_VER).tar.gz --location ${ZLIB_DOWNLOAD_BASE}/zlib-$(ZLIB_VER).tar.gz
|
||||
ZLIB_SHA256_ACTUAL=`$(SHA256_CMD) zlib-$(ZLIB_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
|
||||
@@ -360,7 +360,6 @@ def generate_buck(repo_path, deps_map):
|
||||
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
|
||||
)
|
||||
BUCK.export_file("tools/db_crashtest.py")
|
||||
BUCK.export_file("tools/fault_injection_log_parser.py")
|
||||
|
||||
print(ColorString.info("Generated BUCK Summary:"))
|
||||
print(ColorString.info("- %d libs" % BUCK.total_lib))
|
||||
|
||||
@@ -118,12 +118,6 @@ class TARGETSBuilder:
|
||||
self.total_bin = self.total_bin + 1
|
||||
|
||||
def add_c_test(self):
|
||||
# The actual c_test_bin target is defined by add_c_test_wrapper in the
|
||||
# internal //rocks/buckifier:defs.bzl (not in this OSS repo). db/c_test.c
|
||||
# #includes the generated c_api_gen/*.inc fragments, so under Buck's
|
||||
# hermetic sandbox that wrapper must expose them as headers, e.g.
|
||||
# headers = native.glob(["c_api_gen/**/*.inc"])
|
||||
# (Make/CMake resolve the include via -I. / PROJECT_SOURCE_DIR.)
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
b"""
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
# -DLZ4 if the LZ4 library is present
|
||||
# -DZSTD if the ZSTD library is present
|
||||
# -DNUMA if the NUMA library is present
|
||||
# -DTBB if the TBB library is present
|
||||
# -DMEMKIND if the memkind library is present
|
||||
#
|
||||
# Using gflags in rocksdb:
|
||||
@@ -423,6 +424,19 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_TBB; then
|
||||
# Test whether tbb is available
|
||||
$CXX $PLATFORM_CXXFLAGS $LDFLAGS -x c++ - -o test.o -ltbb 2>/dev/null <<EOF
|
||||
#include <tbb/tbb.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DTBB"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ltbb"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -ltbb"
|
||||
fi
|
||||
fi
|
||||
|
||||
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 \
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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).
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved.
|
||||
#
|
||||
# Check for some simple mistakes in public headers (on the command line)
|
||||
# that should prevent commit or push
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
# 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/e9cb2d4550b5a95024125084420342d256b349cd/11.x/centos9-native/3bed279
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/4e46e6967e15412702e51960c69790a78933856f/21/platform010/72a2ff8
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/63c5179edc90fec473e53c1b2f92269c65c39e07/11.x/platform010/5684a5a
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/b4a6248a76a163bcccb1e4751e247712c535846e/2.34/platform010/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/ef895164499fdfedb9d9b9c85520de10aa8ff9ca/1.1.8/platform010/76ebdda
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/643dbf0136f869562485cfcb2953f5cab60447ef/1.2.8/platform010/76ebdda
|
||||
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/823bf8e000654abef784311296ffc8166d9d3986/1.9.4/platform010/76ebdda
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/0cda78647b5712bec7ec6d9ba2f0dcdabfb6f44f/1.4.x/platform010/64091f4
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/a02e0de00945729ad553c187c64e86b57f45a33b/2.2.0/platform010/76ebdda
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/a1a50742bec7195bfafb594e174ea45dc197a420/master/platform010/779a252
|
||||
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/f3cce2e1a79ac8d64e102886603a6997d6e88640/1.8/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/0883cccda758e68b47e4c04e4fa01142e1f60f32/fb/platform010/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/1d5d19135a406a941d3a441eb2bf506bb9d19210/2.43/centos9-native/be7139e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/3e2b7ccc315c9a0f2b678ce6fd774f4970f9bf3b/3.22.0/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
|
||||
@@ -87,6 +87,15 @@ if test -z $PIC_BUILD; then
|
||||
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"
|
||||
|
||||
test "$USE_SSE" || USE_SSE=1
|
||||
export USE_SSE
|
||||
test "$PORTABLE" || PORTABLE=1
|
||||
@@ -95,7 +104,7 @@ 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"
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
@@ -141,17 +150,17 @@ 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"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB"
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-5-glibc-2.23/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/gcc-5-glibc-2.23/lib"
|
||||
# dynamic loading
|
||||
# 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"
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
|
||||
@@ -82,6 +82,11 @@ CFLAGS+=" -DNUMA"
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind${MAYBE_PIC}.a"
|
||||
|
||||
# location of TBB
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# location of LIBURING
|
||||
LIBURING_INCLUDE=" -isystem $LIBURING_BASE/include/"
|
||||
LIBURING_LIBS="$LIBURING_BASE/lib/liburing${MAYBE_PIC}.a"
|
||||
@@ -96,7 +101,7 @@ BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
AS="$BINUTILS/as"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $LIBURING_INCLUDE $BENCHMARK_INCLUDE"
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE $LIBURING_INCLUDE $BENCHMARK_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
@@ -152,18 +157,18 @@ 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"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $LIBURING_LIBS $BENCHMARK_LIBS"
|
||||
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+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform010/lib"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=$GCC_BASE/lib64"
|
||||
# dynamic loading
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
PLATFORM_LDFLAGS+=" -B$BINUTILS"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $LIBURING_LIBS $BENCHMARK_LIBS"
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS $LIBURING_LIBS $BENCHMARK_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2017 Google Inc. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import gtest_parallel
|
||||
import sys
|
||||
|
||||
sys.exit(gtest_parallel.main())
|
||||
@@ -1,959 +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 the Apache 2.0 License (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
# Copyright 2013 Google Inc. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import errno
|
||||
from functools import total_ordering
|
||||
import gzip
|
||||
import io
|
||||
import json
|
||||
import multiprocessing
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
|
||||
if sys.version_info.major >= 3:
|
||||
long = int
|
||||
import _pickle as cPickle
|
||||
import _thread as thread
|
||||
else:
|
||||
import cPickle
|
||||
import thread
|
||||
|
||||
from pickle import HIGHEST_PROTOCOL as PICKLE_HIGHEST_PROTOCOL
|
||||
|
||||
if sys.platform == 'win32':
|
||||
import msvcrt
|
||||
else:
|
||||
import fcntl
|
||||
|
||||
|
||||
# An object that catches SIGINT sent to the Python process and notices
|
||||
# if processes passed to wait() die by SIGINT (we need to look for
|
||||
# both of those cases, because pressing Ctrl+C can result in either
|
||||
# the main process or one of the subprocesses getting the signal).
|
||||
#
|
||||
# Before a SIGINT is seen, wait(p) will simply call p.wait() and
|
||||
# return the result. Once a SIGINT has been seen (in the main process
|
||||
# or a subprocess, including the one the current call is waiting for),
|
||||
# wait(p) will call p.terminate() and raise ProcessWasInterrupted.
|
||||
class SigintHandler(object):
|
||||
class ProcessWasInterrupted(Exception):
|
||||
pass
|
||||
|
||||
sigint_returncodes = {
|
||||
-signal.SIGINT, # Unix
|
||||
-1073741510, # Windows
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.__lock = threading.Lock()
|
||||
self.__processes = set()
|
||||
self.__got_sigint = False
|
||||
signal.signal(signal.SIGINT, lambda signal_num, frame: self.interrupt())
|
||||
|
||||
def __on_sigint(self):
|
||||
self.__got_sigint = True
|
||||
while self.__processes:
|
||||
try:
|
||||
self.__processes.pop().terminate()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def interrupt(self):
|
||||
with self.__lock:
|
||||
self.__on_sigint()
|
||||
|
||||
def got_sigint(self):
|
||||
with self.__lock:
|
||||
return self.__got_sigint
|
||||
|
||||
def wait(self, p, timeout_per_test):
|
||||
with self.__lock:
|
||||
if self.__got_sigint:
|
||||
p.terminate()
|
||||
self.__processes.add(p)
|
||||
try:
|
||||
code = p.wait(timeout_per_test)
|
||||
except subprocess.TimeoutExpired :
|
||||
p.terminate()
|
||||
self.__processes.remove(p)
|
||||
code = -errno.ETIME
|
||||
with self.__lock:
|
||||
self.__processes.discard(p)
|
||||
if code in self.sigint_returncodes:
|
||||
self.__on_sigint()
|
||||
if self.__got_sigint:
|
||||
raise self.ProcessWasInterrupted
|
||||
return code
|
||||
|
||||
|
||||
sigint_handler = SigintHandler()
|
||||
|
||||
|
||||
# Return the width of the terminal, or None if it couldn't be
|
||||
# determined (e.g. because we're not being run interactively).
|
||||
def term_width(out):
|
||||
if not out.isatty():
|
||||
return None
|
||||
try:
|
||||
p = subprocess.Popen(["stty", "size"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
(out, err) = p.communicate()
|
||||
if p.returncode != 0 or err:
|
||||
return None
|
||||
return int(out.split()[1])
|
||||
except (IndexError, OSError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
# Output transient and permanent lines of text. If several transient
|
||||
# lines are written in sequence, the new will overwrite the old. We
|
||||
# use this to ensure that lots of unimportant info (tests passing)
|
||||
# won't drown out important info (tests failing).
|
||||
class Outputter(object):
|
||||
def __init__(self, out_file):
|
||||
self.__out_file = out_file
|
||||
self.__previous_line_was_transient = False
|
||||
self.__width = term_width(out_file) # Line width, or None if not a tty.
|
||||
|
||||
def transient_line(self, msg):
|
||||
if self.__width is None:
|
||||
self.__out_file.write(msg + "\n")
|
||||
self.__out_file.flush()
|
||||
else:
|
||||
self.__out_file.write("\r" + msg[:self.__width].ljust(self.__width))
|
||||
self.__previous_line_was_transient = True
|
||||
|
||||
def flush_transient_output(self):
|
||||
if self.__previous_line_was_transient:
|
||||
self.__out_file.write("\n")
|
||||
self.__previous_line_was_transient = False
|
||||
|
||||
def permanent_line(self, msg):
|
||||
self.flush_transient_output()
|
||||
self.__out_file.write(msg + "\n")
|
||||
if self.__width is None:
|
||||
self.__out_file.flush()
|
||||
|
||||
|
||||
def get_save_file_path():
|
||||
"""Return path to file for saving transient data."""
|
||||
if sys.platform == 'win32':
|
||||
default_cache_path = os.path.join(os.path.expanduser('~'), 'AppData',
|
||||
'Local')
|
||||
cache_path = os.environ.get('LOCALAPPDATA', default_cache_path)
|
||||
else:
|
||||
# We don't use xdg module since it's not a standard.
|
||||
default_cache_path = os.path.join(os.path.expanduser('~'), '.cache')
|
||||
cache_path = os.environ.get('XDG_CACHE_HOME', default_cache_path)
|
||||
|
||||
if os.path.isdir(cache_path):
|
||||
return os.path.join(cache_path, 'gtest-parallel')
|
||||
else:
|
||||
sys.stderr.write('Directory {} does not exist'.format(cache_path))
|
||||
return os.path.join(os.path.expanduser('~'), '.gtest-parallel-times')
|
||||
|
||||
|
||||
@total_ordering
|
||||
class Task(object):
|
||||
"""Stores information about a task (single execution of a test).
|
||||
|
||||
This class stores information about the test to be executed (gtest binary and
|
||||
test name), and its result (log file, exit code and runtime).
|
||||
Each task is uniquely identified by the gtest binary, the test name and an
|
||||
execution number that increases each time the test is executed.
|
||||
Additionaly we store the last execution time, so that next time the test is
|
||||
executed, the slowest tests are run first.
|
||||
"""
|
||||
|
||||
def __init__(self, test_binary, test_name, test_command, execution_number,
|
||||
last_execution_time, output_dir):
|
||||
self.test_name = test_name
|
||||
self.output_dir = output_dir
|
||||
self.test_binary = test_binary
|
||||
self.test_command = test_command
|
||||
self.execution_number = execution_number
|
||||
self.last_execution_time = last_execution_time
|
||||
|
||||
self.exit_code = None
|
||||
self.runtime_ms = None
|
||||
|
||||
self.test_id = (test_binary, test_name)
|
||||
self.task_id = (test_binary, test_name, self.execution_number)
|
||||
|
||||
self.log_file = Task._logname(self.output_dir, self.test_binary, test_name,
|
||||
self.execution_number)
|
||||
|
||||
def __sorting_key(self):
|
||||
# Unseen or failing tests (both missing execution time) take precedence over
|
||||
# execution time. Tests are greater (seen as slower) when missing times so
|
||||
# that they are executed first.
|
||||
return (1 if self.last_execution_time is None else 0,
|
||||
self.last_execution_time)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__sorting_key() == other.__sorting_key()
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.__sorting_key() < other.__sorting_key()
|
||||
|
||||
@staticmethod
|
||||
def _normalize(string):
|
||||
return re.sub('[^A-Za-z0-9]', '_', string)
|
||||
|
||||
@staticmethod
|
||||
def _logname(output_dir, test_binary, test_name, execution_number):
|
||||
# Store logs to temporary files if there is no output_dir.
|
||||
if output_dir is None:
|
||||
(log_handle, log_name) = tempfile.mkstemp(prefix='gtest_parallel_',
|
||||
suffix=".log")
|
||||
os.close(log_handle)
|
||||
return log_name
|
||||
|
||||
log_name = '%s-%s-%d.log' % (Task._normalize(os.path.basename(test_binary)),
|
||||
Task._normalize(test_name), execution_number)
|
||||
|
||||
return os.path.join(output_dir, log_name)
|
||||
|
||||
def run(self, timeout_per_test):
|
||||
begin = time.time()
|
||||
with open(self.log_file, 'w') as log:
|
||||
task = subprocess.Popen(self.test_command, stdout=log, stderr=log)
|
||||
try:
|
||||
self.exit_code = sigint_handler.wait(task, timeout_per_test)
|
||||
except sigint_handler.ProcessWasInterrupted:
|
||||
thread.exit()
|
||||
self.runtime_ms = int(1000 * (time.time() - begin))
|
||||
self.last_execution_time = None if self.exit_code else self.runtime_ms
|
||||
|
||||
|
||||
class TaskManager(object):
|
||||
"""Executes the tasks and stores the passed, failed and interrupted tasks.
|
||||
|
||||
When a task is run, this class keeps track if it passed, failed or was
|
||||
interrupted. After a task finishes it calls the relevant functions of the
|
||||
Logger, TestResults and TestTimes classes, and in case of failure, retries the
|
||||
test as specified by the --retry_failed flag.
|
||||
"""
|
||||
|
||||
def __init__(self, times, logger, test_results, task_factory, times_to_retry,
|
||||
initial_execution_number):
|
||||
self.times = times
|
||||
self.logger = logger
|
||||
self.test_results = test_results
|
||||
self.task_factory = task_factory
|
||||
self.times_to_retry = times_to_retry
|
||||
self.initial_execution_number = initial_execution_number
|
||||
|
||||
self.global_exit_code = 0
|
||||
|
||||
self.passed = []
|
||||
self.failed = []
|
||||
self.started = {}
|
||||
self.timed_out = []
|
||||
self.execution_number = {}
|
||||
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def __get_next_execution_number(self, test_id):
|
||||
with self.lock:
|
||||
next_execution_number = self.execution_number.setdefault(
|
||||
test_id, self.initial_execution_number)
|
||||
self.execution_number[test_id] += 1
|
||||
return next_execution_number
|
||||
|
||||
def __register_start(self, task):
|
||||
with self.lock:
|
||||
self.started[task.task_id] = task
|
||||
|
||||
def register_exit(self, task):
|
||||
self.logger.log_exit(task)
|
||||
self.times.record_test_time(task.test_binary, task.test_name,
|
||||
task.last_execution_time)
|
||||
if self.test_results:
|
||||
self.test_results.log(task.test_name, task.runtime_ms / 1000.0,
|
||||
task.exit_code)
|
||||
|
||||
with self.lock:
|
||||
self.started.pop(task.task_id)
|
||||
if task.exit_code == 0:
|
||||
self.passed.append(task)
|
||||
elif task.exit_code == -errno.ETIME:
|
||||
self.timed_out.append(task)
|
||||
else:
|
||||
self.failed.append(task)
|
||||
|
||||
def run_task(self, task, timeout_per_test):
|
||||
for try_number in range(self.times_to_retry + 1):
|
||||
self.__register_start(task)
|
||||
task.run(timeout_per_test)
|
||||
self.register_exit(task)
|
||||
|
||||
if task.exit_code == 0:
|
||||
break
|
||||
|
||||
if try_number < self.times_to_retry:
|
||||
execution_number = self.__get_next_execution_number(task.test_id)
|
||||
# We need create a new Task instance. Each task represents a single test
|
||||
# execution, with its own runtime, exit code and log file.
|
||||
task = self.task_factory(task.test_binary, task.test_name,
|
||||
task.test_command, execution_number,
|
||||
task.last_execution_time, task.output_dir)
|
||||
|
||||
with self.lock:
|
||||
if task.exit_code != 0:
|
||||
self.global_exit_code = task.exit_code
|
||||
|
||||
|
||||
class FilterFormat(object):
|
||||
def __init__(self, output_dir):
|
||||
if sys.stdout.isatty():
|
||||
# stdout needs to be unbuffered since the output is interactive.
|
||||
if isinstance(sys.stdout, io.TextIOWrapper):
|
||||
# workaround for https://bugs.python.org/issue17404
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.detach(),
|
||||
line_buffering=True,
|
||||
write_through=True,
|
||||
newline='\n')
|
||||
else:
|
||||
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
|
||||
|
||||
self.output_dir = output_dir
|
||||
|
||||
self.total_tasks = 0
|
||||
self.finished_tasks = 0
|
||||
self.out = Outputter(sys.stdout)
|
||||
self.stdout_lock = threading.Lock()
|
||||
|
||||
def move_to(self, destination_dir, tasks):
|
||||
if self.output_dir is None:
|
||||
return
|
||||
|
||||
destination_dir = os.path.join(self.output_dir, destination_dir)
|
||||
os.makedirs(destination_dir)
|
||||
for task in tasks:
|
||||
shutil.move(task.log_file, destination_dir)
|
||||
|
||||
def print_tests(self, message, tasks, print_try_number, print_test_command):
|
||||
self.out.permanent_line("%s (%s/%s):" %
|
||||
(message, len(tasks), self.total_tasks))
|
||||
for task in sorted(tasks):
|
||||
runtime_ms = 'Interrupted'
|
||||
if task.runtime_ms is not None:
|
||||
runtime_ms = '%d ms' % task.runtime_ms
|
||||
if print_test_command:
|
||||
try:
|
||||
cmd_str = " ".join(task.test_command)
|
||||
except TypeError:
|
||||
cmd_str = task.test_command
|
||||
self.out.permanent_line(
|
||||
"%11s: %s%s" %
|
||||
(runtime_ms, cmd_str,
|
||||
(" (try #%d)" % task.execution_number) if print_try_number else ""))
|
||||
else:
|
||||
self.out.permanent_line(
|
||||
"%11s: %s %s%s" %
|
||||
(runtime_ms, task.test_binary, task.test_name,
|
||||
(" (try #%d)" % task.execution_number) if print_try_number else ""))
|
||||
|
||||
def log_exit(self, task):
|
||||
with self.stdout_lock:
|
||||
self.finished_tasks += 1
|
||||
self.out.transient_line("[%d/%d] %s (%d ms)" %
|
||||
(self.finished_tasks, self.total_tasks,
|
||||
task.test_name, task.runtime_ms))
|
||||
if task.exit_code != 0:
|
||||
signal_name = None
|
||||
if task.exit_code < 0:
|
||||
try:
|
||||
signal_name = signal.Signals(-task.exit_code).name
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
with open(task.log_file) as f:
|
||||
for line in f.readlines():
|
||||
self.out.permanent_line(line.rstrip())
|
||||
if task.exit_code is None:
|
||||
self.out.permanent_line("[%d/%d] %s aborted after %d ms" %
|
||||
(self.finished_tasks, self.total_tasks,
|
||||
task.test_name, task.runtime_ms))
|
||||
elif task.exit_code == -errno.ETIME:
|
||||
self.out.permanent_line(
|
||||
"\033[31m[ TIMEOUT ]\033[0m %s timed out after %d s"
|
||||
% (task.test_name, task.runtime_ms/1000))
|
||||
elif signal_name is not None:
|
||||
self.out.permanent_line(
|
||||
"[%d/%d] %s killed by signal %s (%d ms)" %
|
||||
(self.finished_tasks, self.total_tasks, task.test_name,
|
||||
signal_name, task.runtime_ms))
|
||||
else:
|
||||
self.out.permanent_line(
|
||||
"[%d/%d] %s returned with exit code %d (%d ms)" %
|
||||
(self.finished_tasks, self.total_tasks, task.test_name,
|
||||
task.exit_code, task.runtime_ms))
|
||||
|
||||
if self.output_dir is None:
|
||||
# Try to remove the file 100 times (sleeping for 0.1 second in between).
|
||||
# This is a workaround for a process handle seemingly holding on to the
|
||||
# file for too long inside os.subprocess. This workaround is in place
|
||||
# until we figure out a minimal repro to report upstream (or a better
|
||||
# suspect) to prevent os.remove exceptions.
|
||||
num_tries = 100
|
||||
for i in range(num_tries):
|
||||
try:
|
||||
os.remove(task.log_file)
|
||||
except OSError as e:
|
||||
if e.errno is not errno.ENOENT:
|
||||
if i is num_tries - 1:
|
||||
self.out.permanent_line('Could not remove temporary log file: ' +
|
||||
str(e))
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
break
|
||||
|
||||
def log_tasks(self, total_tasks):
|
||||
self.total_tasks += total_tasks
|
||||
self.out.transient_line("[0/%d] Running tests..." % self.total_tasks)
|
||||
|
||||
def summarize(self, passed_tasks, failed_tasks, interrupted_tasks):
|
||||
stats = {}
|
||||
|
||||
def add_stats(stats, task, idx):
|
||||
task_key = (task.test_binary, task.test_name)
|
||||
if not task_key in stats:
|
||||
# (passed, failed, interrupted) task_key is added as tie breaker to get
|
||||
# alphabetic sorting on equally-stable tests
|
||||
stats[task_key] = [0, 0, 0, task_key]
|
||||
stats[task_key][idx] += 1
|
||||
|
||||
for task in passed_tasks:
|
||||
add_stats(stats, task, 0)
|
||||
for task in failed_tasks:
|
||||
add_stats(stats, task, 1)
|
||||
for task in interrupted_tasks:
|
||||
add_stats(stats, task, 2)
|
||||
|
||||
self.out.permanent_line("SUMMARY:")
|
||||
for task_key in sorted(stats, key=stats.__getitem__):
|
||||
(num_passed, num_failed, num_interrupted, _) = stats[task_key]
|
||||
(test_binary, task_name) = task_key
|
||||
total_runs = num_passed + num_failed + num_interrupted
|
||||
if num_passed == total_runs:
|
||||
continue
|
||||
self.out.permanent_line(" %s %s passed %d / %d times%s." %
|
||||
(test_binary, task_name, num_passed, total_runs,
|
||||
"" if num_interrupted == 0 else
|
||||
(" (%d interrupted)" % num_interrupted)))
|
||||
|
||||
def flush(self):
|
||||
self.out.flush_transient_output()
|
||||
|
||||
|
||||
class CollectTestResults(object):
|
||||
def __init__(self, json_dump_filepath):
|
||||
self.test_results_lock = threading.Lock()
|
||||
self.json_dump_file = open(json_dump_filepath, 'w')
|
||||
self.test_results = {
|
||||
"interrupted": False,
|
||||
"path_delimiter": ".",
|
||||
# Third version of the file format. See the link in the flag description
|
||||
# for details.
|
||||
"version": 3,
|
||||
"seconds_since_epoch": int(time.time()),
|
||||
"num_failures_by_type": {
|
||||
"PASS": 0,
|
||||
"FAIL": 0,
|
||||
"TIMEOUT": 0,
|
||||
},
|
||||
"tests": {},
|
||||
}
|
||||
|
||||
def log(self, test, runtime_seconds, exit_code):
|
||||
if exit_code is None:
|
||||
actual_result = "TIMEOUT"
|
||||
elif exit_code == 0:
|
||||
actual_result = "PASS"
|
||||
else:
|
||||
actual_result = "FAIL"
|
||||
with self.test_results_lock:
|
||||
self.test_results['num_failures_by_type'][actual_result] += 1
|
||||
results = self.test_results['tests']
|
||||
for name in test.split('.'):
|
||||
results = results.setdefault(name, {})
|
||||
|
||||
if results:
|
||||
results['actual'] += ' ' + actual_result
|
||||
results['times'].append(runtime_seconds)
|
||||
else: # This is the first invocation of the test
|
||||
results['actual'] = actual_result
|
||||
results['times'] = [runtime_seconds]
|
||||
results['time'] = runtime_seconds
|
||||
results['expected'] = 'PASS'
|
||||
|
||||
def dump_to_file_and_close(self):
|
||||
json.dump(self.test_results, self.json_dump_file)
|
||||
self.json_dump_file.close()
|
||||
|
||||
|
||||
# Record of test runtimes. Has built-in locking.
|
||||
class TestTimes(object):
|
||||
class LockedFile(object):
|
||||
def __init__(self, filename, mode):
|
||||
self._filename = filename
|
||||
self._mode = mode
|
||||
self._fo = None
|
||||
|
||||
def __enter__(self):
|
||||
self._fo = open(self._filename, self._mode)
|
||||
|
||||
# Regardless of opening mode we always seek to the beginning of file.
|
||||
# This simplifies code working with LockedFile and also ensures that
|
||||
# we lock (and unlock below) always the same region in file on win32.
|
||||
self._fo.seek(0)
|
||||
|
||||
try:
|
||||
if sys.platform == 'win32':
|
||||
# We are locking here fixed location in file to use it as
|
||||
# an exclusive lock on entire file.
|
||||
msvcrt.locking(self._fo.fileno(), msvcrt.LK_LOCK, 1)
|
||||
else:
|
||||
fcntl.flock(self._fo.fileno(), fcntl.LOCK_EX)
|
||||
except IOError:
|
||||
self._fo.close()
|
||||
raise
|
||||
|
||||
return self._fo
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
# Flush any buffered data to disk. This is needed to prevent race
|
||||
# condition which happens from the moment of releasing file lock
|
||||
# till closing the file.
|
||||
self._fo.flush()
|
||||
|
||||
try:
|
||||
if sys.platform == 'win32':
|
||||
self._fo.seek(0)
|
||||
msvcrt.locking(self._fo.fileno(), msvcrt.LK_UNLCK, 1)
|
||||
else:
|
||||
fcntl.flock(self._fo.fileno(), fcntl.LOCK_UN)
|
||||
finally:
|
||||
self._fo.close()
|
||||
|
||||
return exc_value is None
|
||||
|
||||
def __init__(self, save_file):
|
||||
"Create new object seeded with saved test times from the given file."
|
||||
self.__times = {} # (test binary, test name) -> runtime in ms
|
||||
|
||||
# Protects calls to record_test_time(); other calls are not
|
||||
# expected to be made concurrently.
|
||||
self.__lock = threading.Lock()
|
||||
|
||||
try:
|
||||
with TestTimes.LockedFile(save_file, 'rb') as fd:
|
||||
times = TestTimes.__read_test_times_file(fd)
|
||||
except IOError:
|
||||
# We couldn't obtain the lock.
|
||||
return
|
||||
|
||||
# Discard saved times if the format isn't right.
|
||||
if type(times) is not dict:
|
||||
return
|
||||
for ((test_binary, test_name), runtime) in times.items():
|
||||
if (type(test_binary) is not str or type(test_name) is not str
|
||||
or type(runtime) not in {int, long, type(None)}):
|
||||
return
|
||||
|
||||
self.__times = times
|
||||
|
||||
def get_test_time(self, binary, testname):
|
||||
"""Return the last duration for the given test as an integer number of
|
||||
milliseconds, or None if the test failed or if there's no record for it."""
|
||||
return self.__times.get((binary, testname), None)
|
||||
|
||||
def record_test_time(self, binary, testname, runtime_ms):
|
||||
"""Record that the given test ran in the specified number of
|
||||
milliseconds. If the test failed, runtime_ms should be None."""
|
||||
with self.__lock:
|
||||
self.__times[(binary, testname)] = runtime_ms
|
||||
|
||||
def write_to_file(self, save_file):
|
||||
"Write all the times to file."
|
||||
try:
|
||||
with TestTimes.LockedFile(save_file, 'a+b') as fd:
|
||||
times = TestTimes.__read_test_times_file(fd)
|
||||
|
||||
if times is None:
|
||||
times = self.__times
|
||||
else:
|
||||
times.update(self.__times)
|
||||
|
||||
# We erase data from file while still holding a lock to it. This
|
||||
# way reading old test times and appending new ones are atomic
|
||||
# for external viewer.
|
||||
fd.seek(0)
|
||||
fd.truncate()
|
||||
with gzip.GzipFile(fileobj=fd, mode='wb') as gzf:
|
||||
cPickle.dump(times, gzf, PICKLE_HIGHEST_PROTOCOL)
|
||||
except IOError:
|
||||
pass # ignore errors---saving the times isn't that important
|
||||
|
||||
@staticmethod
|
||||
def __read_test_times_file(fd):
|
||||
try:
|
||||
with gzip.GzipFile(fileobj=fd, mode='rb') as gzf:
|
||||
times = cPickle.load(gzf)
|
||||
except Exception:
|
||||
# File doesn't exist, isn't readable, is malformed---whatever.
|
||||
# Just ignore it.
|
||||
return None
|
||||
else:
|
||||
return times
|
||||
|
||||
|
||||
def find_tests(binaries, additional_args, options, times):
|
||||
test_count = 0
|
||||
tasks = []
|
||||
for test_binary in binaries:
|
||||
command = [test_binary] + additional_args
|
||||
if options.gtest_also_run_disabled_tests:
|
||||
command += ['--gtest_also_run_disabled_tests']
|
||||
|
||||
list_command = command + ['--gtest_list_tests']
|
||||
if options.gtest_filter != '':
|
||||
list_command += ['--gtest_filter=' + options.gtest_filter]
|
||||
|
||||
try:
|
||||
test_list = subprocess.check_output(list_command,
|
||||
stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
sys.exit("%s: %s\n%s" % (test_binary, str(e), e.output))
|
||||
|
||||
try:
|
||||
test_list = test_list.split('\n')
|
||||
except TypeError:
|
||||
# subprocess.check_output() returns bytes in python3
|
||||
test_list = test_list.decode(sys.stdout.encoding).split('\n')
|
||||
|
||||
command += ['--gtest_color=' + options.gtest_color]
|
||||
|
||||
test_group = ''
|
||||
for line in test_list:
|
||||
if not line.strip():
|
||||
continue
|
||||
if line[0] != " ":
|
||||
# Remove comments for typed tests and strip whitespace.
|
||||
test_group = line.split('#')[0].strip()
|
||||
continue
|
||||
# Remove comments for parameterized tests and strip whitespace.
|
||||
line = line.split('#')[0].strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
test_name = test_group + line
|
||||
if not options.gtest_also_run_disabled_tests and 'DISABLED_' in test_name:
|
||||
continue
|
||||
|
||||
# Skip PRE_ tests which are used by Chromium.
|
||||
if '.PRE_' in test_name:
|
||||
continue
|
||||
|
||||
last_execution_time = times.get_test_time(test_binary, test_name)
|
||||
if options.failed and last_execution_time is not None:
|
||||
continue
|
||||
|
||||
test_command = command + ['--gtest_filter=' + test_name]
|
||||
if (test_count - options.shard_index) % options.shard_count == 0:
|
||||
for execution_number in range(options.repeat):
|
||||
tasks.append(
|
||||
Task(test_binary, test_name, test_command, execution_number + 1,
|
||||
last_execution_time, options.output_dir))
|
||||
|
||||
test_count += 1
|
||||
|
||||
# Sort the tasks to run the slowest tests first, so that faster ones can be
|
||||
# finished in parallel.
|
||||
return sorted(tasks, reverse=True)
|
||||
|
||||
|
||||
def execute_tasks(tasks, pool_size, task_manager, timeout_seconds,
|
||||
timeout_per_test, serialize_test_cases):
|
||||
class WorkerFn(object):
|
||||
def __init__(self, tasks, running_groups, timeout_per_test):
|
||||
self.tasks = tasks
|
||||
self.running_groups = running_groups
|
||||
self.timeout_per_test = timeout_per_test
|
||||
self.task_lock = threading.Lock()
|
||||
|
||||
def __call__(self):
|
||||
while True:
|
||||
with self.task_lock:
|
||||
for task_id in range(len(self.tasks)):
|
||||
task = self.tasks[task_id]
|
||||
|
||||
if self.running_groups is not None:
|
||||
test_group = task.test_name.split('.')[0]
|
||||
if test_group in self.running_groups:
|
||||
# Try to find other non-running test group.
|
||||
continue
|
||||
else:
|
||||
self.running_groups.add(test_group)
|
||||
|
||||
del self.tasks[task_id]
|
||||
break
|
||||
else:
|
||||
# Either there is no tasks left or number or remaining test
|
||||
# cases (groups) is less than number or running threads.
|
||||
return
|
||||
|
||||
task_manager.run_task(task, self.timeout_per_test)
|
||||
|
||||
if self.running_groups is not None:
|
||||
with self.task_lock:
|
||||
self.running_groups.remove(test_group)
|
||||
|
||||
def start_daemon(func):
|
||||
t = threading.Thread(target=func)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
return t
|
||||
|
||||
timeout = None
|
||||
try:
|
||||
if timeout_seconds:
|
||||
timeout = threading.Timer(timeout_seconds, sigint_handler.interrupt)
|
||||
timeout.start()
|
||||
running_groups = set() if serialize_test_cases else None
|
||||
worker_fn = WorkerFn(tasks, running_groups, timeout_per_test)
|
||||
workers = [start_daemon(worker_fn) for _ in range(pool_size)]
|
||||
for worker in workers:
|
||||
worker.join()
|
||||
finally:
|
||||
if timeout:
|
||||
timeout.cancel()
|
||||
for task in list(task_manager.started.values()):
|
||||
task.runtime_ms = timeout_seconds * 1000
|
||||
task_manager.register_exit(task)
|
||||
|
||||
|
||||
def default_options_parser():
|
||||
parser = optparse.OptionParser(
|
||||
usage='usage: %prog [options] binary [binary ...] -- [additional args]')
|
||||
|
||||
parser.add_option('-d',
|
||||
'--output_dir',
|
||||
type='string',
|
||||
default=None,
|
||||
help='Output directory for test logs. Logs will be '
|
||||
'available under gtest-parallel-logs/, so '
|
||||
'--output_dir=/tmp will results in all logs being '
|
||||
'available under /tmp/gtest-parallel-logs/.')
|
||||
parser.add_option('-r',
|
||||
'--repeat',
|
||||
type='int',
|
||||
default=1,
|
||||
help='Number of times to execute all the tests.')
|
||||
parser.add_option('--retry_failed',
|
||||
type='int',
|
||||
default=0,
|
||||
help='Number of times to repeat failed tests.')
|
||||
parser.add_option('--failed',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='run only failed and new tests')
|
||||
parser.add_option('-w',
|
||||
'--workers',
|
||||
type='int',
|
||||
default=multiprocessing.cpu_count(),
|
||||
help='number of workers to spawn')
|
||||
parser.add_option('--gtest_color',
|
||||
type='string',
|
||||
default='yes',
|
||||
help='color output')
|
||||
parser.add_option('--gtest_filter',
|
||||
type='string',
|
||||
default='',
|
||||
help='test filter')
|
||||
parser.add_option('--gtest_also_run_disabled_tests',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='run disabled tests too')
|
||||
parser.add_option(
|
||||
'--print_test_times',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='list the run time of each test at the end of execution')
|
||||
parser.add_option(
|
||||
'--print_test_command',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Print full test command instead of name')
|
||||
parser.add_option('--shard_count',
|
||||
type='int',
|
||||
default=1,
|
||||
help='total number of shards (for sharding test execution '
|
||||
'between multiple machines)')
|
||||
parser.add_option('--shard_index',
|
||||
type='int',
|
||||
default=0,
|
||||
help='zero-indexed number identifying this shard (for '
|
||||
'sharding test execution between multiple machines)')
|
||||
parser.add_option(
|
||||
'--dump_json_test_results',
|
||||
type='string',
|
||||
default=None,
|
||||
help='Saves the results of the tests as a JSON machine-'
|
||||
'readable file. The format of the file is specified at '
|
||||
'https://www.chromium.org/developers/the-json-test-results-format')
|
||||
parser.add_option('--timeout',
|
||||
type='int',
|
||||
default=None,
|
||||
help='Interrupt all remaining processes after the given '
|
||||
'time (in seconds).')
|
||||
parser.add_option('--timeout_per_test',
|
||||
type='int',
|
||||
default=None,
|
||||
help='Interrupt single processes after the given '
|
||||
'time (in seconds).')
|
||||
parser.add_option('--serialize_test_cases',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Do not run tests from the same test '
|
||||
'case in parallel.')
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
# Remove additional arguments (anything after --).
|
||||
additional_args = []
|
||||
|
||||
for i in range(len(sys.argv)):
|
||||
if sys.argv[i] == '--':
|
||||
additional_args = sys.argv[i + 1:]
|
||||
sys.argv = sys.argv[:i]
|
||||
break
|
||||
|
||||
parser = default_options_parser()
|
||||
(options, binaries) = parser.parse_args()
|
||||
|
||||
if (options.output_dir is not None and not os.path.isdir(options.output_dir)):
|
||||
parser.error('--output_dir value must be an existing directory, '
|
||||
'current value is "%s"' % options.output_dir)
|
||||
|
||||
# Append gtest-parallel-logs to log output, this is to avoid deleting user
|
||||
# data if an user passes a directory where files are already present. If a
|
||||
# user specifies --output_dir=Docs/, we'll create Docs/gtest-parallel-logs
|
||||
# and clean that directory out on startup, instead of nuking Docs/.
|
||||
if options.output_dir:
|
||||
options.output_dir = os.path.join(options.output_dir, 'gtest-parallel-logs')
|
||||
|
||||
if binaries == []:
|
||||
parser.print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
if options.shard_count < 1:
|
||||
parser.error("Invalid number of shards: %d. Must be at least 1." %
|
||||
options.shard_count)
|
||||
if not (0 <= options.shard_index < options.shard_count):
|
||||
parser.error("Invalid shard index: %d. Must be between 0 and %d "
|
||||
"(less than the number of shards)." %
|
||||
(options.shard_index, options.shard_count - 1))
|
||||
|
||||
# Check that all test binaries have an unique basename. That way we can ensure
|
||||
# the logs are saved to unique files even when two different binaries have
|
||||
# common tests.
|
||||
unique_binaries = set(os.path.basename(binary) for binary in binaries)
|
||||
assert len(unique_binaries) == len(binaries), (
|
||||
"All test binaries must have an unique basename.")
|
||||
|
||||
if options.output_dir:
|
||||
# Remove files from old test runs.
|
||||
if os.path.isdir(options.output_dir):
|
||||
shutil.rmtree(options.output_dir)
|
||||
# Create directory for test log output.
|
||||
try:
|
||||
os.makedirs(options.output_dir)
|
||||
except OSError as e:
|
||||
# Ignore errors if this directory already exists.
|
||||
if e.errno != errno.EEXIST or not os.path.isdir(options.output_dir):
|
||||
raise e
|
||||
|
||||
test_results = None
|
||||
if options.dump_json_test_results is not None:
|
||||
test_results = CollectTestResults(options.dump_json_test_results)
|
||||
|
||||
save_file = get_save_file_path()
|
||||
|
||||
times = TestTimes(save_file)
|
||||
logger = FilterFormat(options.output_dir)
|
||||
|
||||
task_manager = TaskManager(times, logger, test_results, Task,
|
||||
options.retry_failed, options.repeat + 1)
|
||||
|
||||
tasks = find_tests(binaries, additional_args, options, times)
|
||||
logger.log_tasks(len(tasks))
|
||||
execute_tasks(tasks, options.workers, task_manager, options.timeout,
|
||||
options.timeout_per_test, options.serialize_test_cases)
|
||||
|
||||
print_try_number = options.retry_failed > 0 or options.repeat > 1
|
||||
if task_manager.passed:
|
||||
logger.move_to('passed', task_manager.passed)
|
||||
if options.print_test_times:
|
||||
logger.print_tests('PASSED TESTS', task_manager.passed, print_try_number, options.print_test_command)
|
||||
|
||||
if task_manager.failed:
|
||||
logger.print_tests('FAILED TESTS', task_manager.failed, print_try_number, options.print_test_command)
|
||||
logger.move_to('failed', task_manager.failed)
|
||||
|
||||
if task_manager.timed_out:
|
||||
logger.print_tests('TIMED OUT TESTS', task_manager.timed_out, print_try_number, options.print_test_command)
|
||||
logger.move_to('timed_out', task_manager.timed_out)
|
||||
|
||||
if task_manager.started:
|
||||
logger.print_tests('INTERRUPTED TESTS', task_manager.started.values(),
|
||||
print_try_number, options.print_test_command)
|
||||
logger.move_to('interrupted', task_manager.started.values())
|
||||
|
||||
if options.repeat > 1 and (task_manager.failed or task_manager.started):
|
||||
logger.summarize(task_manager.passed, task_manager.failed,
|
||||
task_manager.started.values())
|
||||
|
||||
logger.flush()
|
||||
times.write_to_file(save_file)
|
||||
if test_results:
|
||||
test_results.dump_to_file_and_close()
|
||||
|
||||
if sigint_handler.got_sigint():
|
||||
return -signal.SIGINT
|
||||
|
||||
return task_manager.global_exit_code
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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).
|
||||
#
|
||||
# Build RocksDB unit test binaries and run them under gtest-parallel,
|
||||
# which shards the test cases across CPUs for faster execution.
|
||||
#
|
||||
# Hardened version of a simple `make <bin> && gtest-parallel ./<bin>` helper:
|
||||
# * AUTO_CLEAN=1 so the Makefile automatically runs a clean when the build
|
||||
# parameters (DEBUG_LEVEL, sanitizers, ASSERT_STATUS_CHECKED, RTTI, etc.)
|
||||
# have changed since the last build, preventing stale/mixed object files.
|
||||
# * Parallel build with -j<NCORES>, computed the same way the Makefile does.
|
||||
# * Uses the gtest-parallel checked in alongside this script (build_tools/),
|
||||
# so it works regardless of PATH.
|
||||
# * `set -euo pipefail` so any failure stops the script.
|
||||
#
|
||||
# Run from the repository root.
|
||||
#
|
||||
# Accepts one or more test binaries (gtest-parallel pools all their test cases
|
||||
# into one shared worker queue). The leading non-option arguments are treated as
|
||||
# binaries; everything from the first option onward is forwarded to
|
||||
# gtest-parallel / the test binaries.
|
||||
#
|
||||
# Example usage:
|
||||
# build_tools/rocksptest.sh db_test
|
||||
# build_tools/rocksptest.sh db_test -r1000 --gtest_filter=*MixedSlowdown*
|
||||
# build_tools/rocksptest.sh db_test env_test db_basic_test
|
||||
# build_tools/rocksptest.sh db_test env_test --gtest_filter=*Foo*
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "usage: $0 <test_binary> [more_test_binaries...] [gtest-parallel/test args...]" >&2
|
||||
echo "example: $0 db_test env_test -r1000 --gtest_filter=*MixedSlowdown*" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# First argument must be a test binary, not an option (catches a forgotten name).
|
||||
if [ "${1#-}" != "$1" ]; then
|
||||
echo "$0: first argument must be a test binary name, not an option ('$1')" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Collect the leading non-option arguments as test binaries; everything from the
|
||||
# first option onward is forwarded to gtest-parallel / the test binaries.
|
||||
BINS=()
|
||||
while [ "$#" -gt 0 ] && [ "${1#-}" = "$1" ]; do
|
||||
BINS+=("$1")
|
||||
shift
|
||||
done
|
||||
|
||||
# Paths as gtest-parallel expects them (relative to the current directory).
|
||||
BIN_PATHS=()
|
||||
for b in "${BINS[@]}"; do
|
||||
BIN_PATHS+=("./$b")
|
||||
done
|
||||
|
||||
# Directory of this script, so we use the gtest-parallel checked in next to it.
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
# Compute parallelism the same way the Makefile computes NCORES.
|
||||
NCORES=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
|
||||
|
||||
make AUTO_CLEAN=1 -j"$NCORES" "${BINS[@]}"
|
||||
"$SCRIPT_DIR/gtest-parallel" "${BIN_PATHS[@]}" "$@"
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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).
|
||||
#
|
||||
# Build a single RocksDB unit test binary and run it directly (serially).
|
||||
# Only recommended with --gtest_filter=... because of speed. See also
|
||||
# build_tools/rocksptest.sh
|
||||
#
|
||||
# Hardened version of a simple `make <bin> && ./<bin>` helper:
|
||||
# * AUTO_CLEAN=1 so the Makefile automatically runs a clean when the build
|
||||
# parameters (DEBUG_LEVEL, sanitizers, ASSERT_STATUS_CHECKED, RTTI, etc.)
|
||||
# have changed since the last build, preventing stale/mixed object files.
|
||||
# * Parallel build with -j<NCORES>, computed the same way the Makefile does.
|
||||
# * `set -euo pipefail` so any failure stops the script.
|
||||
#
|
||||
# Run from the repository root.
|
||||
#
|
||||
# Example usage:
|
||||
# build_tools/rockstest.sh db_test --gtest_filter=*MixedSlowdown*
|
||||
# build_tools/rockstest.sh env_test # Slow to run many tests serially
|
||||
#
|
||||
# Install mode:
|
||||
# build_tools/rockstest.sh install
|
||||
# Creates ~/bin/rockstest and ~/bin/rocksptest shims that defer to
|
||||
# build_tools/rockstest.sh / rocksptest.sh in whatever directory you run
|
||||
# them from, so you can just type `rockstest db_test` from any rocksdb
|
||||
# source root.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Install mode: write thin ~/bin shims that defer to the build_tools scripts in
|
||||
# the current directory.
|
||||
if [ "${1:-}" = "install" ]; then
|
||||
mkdir -p "$HOME/bin"
|
||||
for name in rockstest rocksptest; do
|
||||
dest="$HOME/bin/$name"
|
||||
cat > "$dest" <<EOF
|
||||
#!/usr/bin/env bash
|
||||
# Auto-generated by 'build_tools/rockstest.sh install'. Defers to
|
||||
# build_tools/$name.sh in the current rocksdb source root.
|
||||
if [ -x build_tools/$name.sh ]; then
|
||||
exec build_tools/$name.sh "\$@"
|
||||
else
|
||||
echo "build_tools/$name.sh not found (Not in a rocksdb source root directory?)" >&2
|
||||
exit 1
|
||||
fi
|
||||
EOF
|
||||
chmod +x "$dest"
|
||||
echo "Installed $dest"
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "usage: $0 <test_binary> [test args...]" >&2
|
||||
echo " $0 install # create ~/bin/rockstest and ~/bin/rocksptest shims" >&2
|
||||
echo "example: $0 db_test --gtest_filter=*MixedSlowdown*" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# First argument must be a test binary, not an option (catches a forgotten name).
|
||||
if [ "${1#-}" != "$1" ]; then
|
||||
echo "$0: first argument must be a test binary name, not an option ('$1')" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BIN=$1
|
||||
shift
|
||||
|
||||
# Compute parallelism the same way the Makefile computes NCORES.
|
||||
NCORES=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
|
||||
|
||||
make AUTO_CLEAN=1 -j"$NCORES" "$BIN"
|
||||
./"$BIN" "$@"
|
||||
@@ -32,7 +32,7 @@ function get_lib_base()
|
||||
local lib_platform=$3
|
||||
|
||||
local result="$TP2_LATEST/$lib_name/"
|
||||
|
||||
|
||||
# Lib Version
|
||||
if [ -z "$lib_version" ] || [ "$lib_version" = "LATEST" ]; then
|
||||
# version is not provided, use latest
|
||||
@@ -40,7 +40,7 @@ function get_lib_base()
|
||||
else
|
||||
result="$result/$lib_version/"
|
||||
fi
|
||||
|
||||
|
||||
# Lib Platform
|
||||
if [ -z "$lib_platform" ]; then
|
||||
# platform is not provided, use latest gcc
|
||||
@@ -49,17 +49,17 @@ function get_lib_base()
|
||||
echo $lib_platform
|
||||
result="$result/$lib_platform/"
|
||||
fi
|
||||
|
||||
|
||||
result=`ls -1d $result/*/ | head -n1`
|
||||
|
||||
echo Finding link $result
|
||||
|
||||
|
||||
# lib_name => LIB_NAME_BASE
|
||||
local __res_var=${lib_name^^}"_BASE"
|
||||
__res_var=`echo $__res_var | tr - _`
|
||||
# LIB_NAME_BASE=$result
|
||||
eval $__res_var=`readlink -f $result`
|
||||
|
||||
|
||||
log_variable $__res_var
|
||||
}
|
||||
|
||||
@@ -75,33 +75,17 @@ touch "$OUTPUT"
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
# GCC is pinned to 11.x because the only newer GCC in third-party2 (13.x) is
|
||||
# built for centos9/glibc>=2.35 -- its libgcc_s.so.1 has a hard reference
|
||||
# to _dl_find_object@GLIBC_2.35, but platform010 ships glibc 2.34. Bumping
|
||||
# GCC requires a platform with glibc >= 2.35.
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos9-native/*/`
|
||||
# Clang is pinned to the latest tested major (21). Bump deliberately.
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/21/platform010/*/`
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos8-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/15/platform010/*/`
|
||||
|
||||
log_header
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
# Libraries locations
|
||||
# libgcc is pinned to 11.x to match the GCC 11 compiler above (libstdc++/
|
||||
# libgcc runtime, ABI, and C++ headers). Bump in lockstep with GCC_BASE.
|
||||
get_lib_base libgcc 11.x platform010
|
||||
# glibc 2.34 is the platform010 ABI baseline (ld.so + libc); it is also the
|
||||
# only version available in third-party2, and defines the platform -- do
|
||||
# not bump independently.
|
||||
get_lib_base glibc 2.34 platform010
|
||||
get_lib_base snappy LATEST platform010
|
||||
# zlib is pinned to 1.2.8: it is the latest version with an x86_64
|
||||
# platform010 build (1.2.13 / 1.3.1 are centos9 / aarch64 only). At time of
|
||||
# writing, LATEST here doesn't work: get_lib_base picks the newest version
|
||||
# dir first and only then appends the platform, with no fallback -- so
|
||||
# LATEST would resolve to 1.3.1, find no platform010 build, and emit an
|
||||
# empty ZLIB_BASE.
|
||||
get_lib_base zlib 1.2.8 platform010
|
||||
get_lib_base bzip2 LATEST platform010
|
||||
get_lib_base lz4 LATEST platform010
|
||||
@@ -110,11 +94,12 @@ 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 kernel-headers fb platform010
|
||||
get_lib_base binutils LATEST centos9-native
|
||||
get_lib_base binutils LATEST centos8-native
|
||||
get_lib_base valgrind LATEST platform010
|
||||
|
||||
git diff $OUTPUT
|
||||
@@ -1,39 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'BlockBasedOptions simple'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_block_based_options_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_block_based_options_subset.cc.inc
|
||||
|
||||
/* BlockBasedOptions simple */
|
||||
|
||||
void rocksdb_block_based_options_set_data_block_hash_ratio(
|
||||
rocksdb_block_based_table_options_t* options, double v) {
|
||||
options->rep.data_block_hash_table_util_ratio = v;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_top_level_index_pinning_tier(
|
||||
rocksdb_block_based_table_options_t* options, int v) {
|
||||
options->rep.metadata_cache_options.top_level_index_pinning =
|
||||
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_partition_pinning_tier(
|
||||
rocksdb_block_based_table_options_t* options, int v) {
|
||||
options->rep.metadata_cache_options.partition_pinning =
|
||||
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_unpartitioned_pinning_tier(
|
||||
rocksdb_block_based_table_options_t* options, int v) {
|
||||
options->rep.metadata_cache_options.unpartitioned_pinning =
|
||||
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'BlockBasedOptions simple'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_block_based_options_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_block_based_options_subset.cc.inc
|
||||
|
||||
/* BlockBasedOptions simple */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_block_based_options_set_data_block_hash_ratio(
|
||||
rocksdb_block_based_table_options_t* options, double v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_block_based_options_set_top_level_index_pinning_tier(
|
||||
rocksdb_block_based_table_options_t* options, int v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_block_based_options_set_partition_pinning_tier(
|
||||
rocksdb_block_based_table_options_t* options, int v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_block_based_options_set_unpartitioned_pinning_tier(
|
||||
rocksdb_block_based_table_options_t* options, int v);
|
||||
@@ -1,21 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'CuckooOptions simple'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_cuckoo_options_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_cuckoo_options_subset.cc.inc
|
||||
|
||||
/* CuckooOptions simple */
|
||||
|
||||
void rocksdb_cuckoo_options_set_hash_ratio(
|
||||
rocksdb_cuckoo_table_options_t* options, double v) {
|
||||
options->rep.hash_table_ratio = v;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'CuckooOptions simple'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_cuckoo_options_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_cuckoo_options_subset.cc.inc
|
||||
|
||||
/* CuckooOptions simple */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_cuckoo_options_set_hash_ratio(
|
||||
rocksdb_cuckoo_table_options_t* options, double v);
|
||||
@@ -1,189 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'DB data operations'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_db_simple_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_db_simple_subset.cc.inc
|
||||
|
||||
/* DB data operations */
|
||||
|
||||
void rocksdb_put(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
SaveError(errptr,
|
||||
db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_put_cf(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen, const char* val,
|
||||
size_t vallen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Put(options->rep, column_family->rep,
|
||||
Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_delete(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen)));
|
||||
}
|
||||
|
||||
void rocksdb_delete_cf(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Delete(options->rep, column_family->rep,
|
||||
Slice(key, keylen)));
|
||||
}
|
||||
|
||||
void rocksdb_merge(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, const char* val,
|
||||
size_t vallen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Merge(options->rep, Slice(key, keylen),
|
||||
Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_merge_cf(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen, const char* val,
|
||||
size_t vallen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Merge(options->rep, column_family->rep,
|
||||
Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_write(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_writebatch_t* batch, char** errptr) {
|
||||
SaveError(errptr, db->rep->Write(options->rep, &batch->rep));
|
||||
}
|
||||
|
||||
void rocksdb_put_with_ts(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, const char* ts,
|
||||
size_t tslen, const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->Put(options->rep, Slice(key, keylen),
|
||||
Slice(ts, tslen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_put_cf_with_ts(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen, const char* ts,
|
||||
size_t tslen, const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
SaveError(errptr,
|
||||
db->rep->Put(options->rep, column_family->rep, Slice(key, keylen),
|
||||
Slice(ts, tslen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_delete_with_ts(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, const char* ts,
|
||||
size_t tslen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen),
|
||||
Slice(ts, tslen)));
|
||||
}
|
||||
|
||||
void rocksdb_delete_cf_with_ts(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen, const char* ts,
|
||||
size_t tslen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Delete(options->rep, column_family->rep,
|
||||
Slice(key, keylen), Slice(ts, tslen)));
|
||||
}
|
||||
|
||||
void rocksdb_singledelete(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, char** errptr) {
|
||||
SaveError(errptr, db->rep->SingleDelete(options->rep, Slice(key, keylen)));
|
||||
}
|
||||
|
||||
void rocksdb_singledelete_cf(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen, char** errptr) {
|
||||
SaveError(errptr, db->rep->SingleDelete(options->rep, column_family->rep,
|
||||
Slice(key, keylen)));
|
||||
}
|
||||
|
||||
void rocksdb_singledelete_with_ts(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen,
|
||||
const char* ts, size_t tslen, char** errptr) {
|
||||
SaveError(errptr, db->rep->SingleDelete(options->rep, Slice(key, keylen),
|
||||
Slice(ts, tslen)));
|
||||
}
|
||||
|
||||
void rocksdb_singledelete_cf_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, char** errptr) {
|
||||
SaveError(errptr,
|
||||
db->rep->SingleDelete(options->rep, column_family->rep,
|
||||
Slice(key, keylen), Slice(ts, tslen)));
|
||||
}
|
||||
|
||||
void rocksdb_delete_range_cf(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* start_key, size_t start_key_len,
|
||||
const char* end_key, size_t end_key_len,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->DeleteRange(options->rep, column_family->rep,
|
||||
Slice(start_key, start_key_len),
|
||||
Slice(end_key, end_key_len)));
|
||||
}
|
||||
|
||||
void rocksdb_flush(rocksdb_t* db, const rocksdb_flushoptions_t* options,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->Flush(options->rep));
|
||||
}
|
||||
|
||||
void rocksdb_flush_cf(rocksdb_t* db, const rocksdb_flushoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->Flush(options->rep, column_family->rep));
|
||||
}
|
||||
|
||||
void rocksdb_flush_wal(rocksdb_t* db, unsigned char sync, char** errptr) {
|
||||
SaveError(errptr, db->rep->FlushWAL(sync));
|
||||
}
|
||||
|
||||
void rocksdb_pause_background_work(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->PauseBackgroundWork());
|
||||
}
|
||||
|
||||
void rocksdb_continue_background_work(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->ContinueBackgroundWork());
|
||||
}
|
||||
|
||||
void rocksdb_disable_file_deletions(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->DisableFileDeletions());
|
||||
}
|
||||
|
||||
void rocksdb_enable_file_deletions(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->EnableFileDeletions());
|
||||
}
|
||||
|
||||
void rocksdb_verify_checksum(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->VerifyChecksum());
|
||||
}
|
||||
|
||||
void rocksdb_verify_file_checksums(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->VerifyFileChecksums(ReadOptions()));
|
||||
}
|
||||
|
||||
void rocksdb_destroy_db(const rocksdb_options_t* options, const char* name,
|
||||
char** errptr) {
|
||||
SaveError(errptr, DestroyDB(name, options->rep));
|
||||
}
|
||||
|
||||
void rocksdb_repair_db(const rocksdb_options_t* options, const char* name,
|
||||
char** errptr) {
|
||||
SaveError(errptr, RepairDB(name, options->rep));
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'DB data operations'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_db_simple_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_db_simple_subset.cc.inc
|
||||
|
||||
/* DB data operations */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_put(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, const char* val, size_t vallen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_put_cf(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* val, size_t vallen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_delete(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_delete_cf(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_merge(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, const char* val, size_t vallen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_merge_cf(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* val, size_t vallen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_write(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_writebatch_t* batch, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_put_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, const char* val, size_t vallen,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_put_cf_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, const char* val, size_t vallen,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_delete_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_delete_cf_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete_cf(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete_cf_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_delete_range_cf(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* start_key,
|
||||
size_t start_key_len, const char* end_key, size_t end_key_len,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_flush(
|
||||
rocksdb_t* db, const rocksdb_flushoptions_t* options, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_flush_cf(
|
||||
rocksdb_t* db, const rocksdb_flushoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_flush_wal(rocksdb_t* db,
|
||||
unsigned char sync,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_pause_background_work(rocksdb_t* db,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_continue_background_work(rocksdb_t* db,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_disable_file_deletions(rocksdb_t* db,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_enable_file_deletions(rocksdb_t* db,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_verify_checksum(rocksdb_t* db,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_verify_file_checksums(rocksdb_t* db,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_destroy_db(
|
||||
const rocksdb_options_t* options, const char* name, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_repair_db(
|
||||
const rocksdb_options_t* options, const char* name, char** errptr);
|
||||
@@ -1,255 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/auto_simple_bindings.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Sources:
|
||||
// - include/rocksdb/listener.h
|
||||
// - tools/c_api_gen/auto_simple_bindings_blocklist.json
|
||||
// - include/rocksdb/c.h
|
||||
// - tools/c_api_gen/c_base.cc
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/auto_simple_bindings.py
|
||||
|
||||
// Output group: Auto-discovered JobInfo metadata simple
|
||||
|
||||
/* FlushJobInfo */
|
||||
|
||||
uint32_t rocksdb_flushjobinfo_cf_id(const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.cf_id;
|
||||
}
|
||||
|
||||
const char* rocksdb_flushjobinfo_cf_name(const rocksdb_flushjobinfo_t* info,
|
||||
size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_flushjobinfo_file_path(const rocksdb_flushjobinfo_t* info,
|
||||
size_t* size) {
|
||||
*size = info->rep.file_path.size();
|
||||
return info->rep.file_path.data();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_flushjobinfo_file_number(const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.file_number;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_flushjobinfo_oldest_blob_file_number(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.oldest_blob_file_number;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_flushjobinfo_thread_id(const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.thread_id;
|
||||
}
|
||||
|
||||
int rocksdb_flushjobinfo_job_id(const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.job_id;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_flushjobinfo_triggered_writes_slowdown(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.triggered_writes_slowdown;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_flushjobinfo_triggered_writes_stop(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.triggered_writes_stop;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_flushjobinfo_smallest_seqno(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.smallest_seqno;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_flushjobinfo_largest_seqno(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.largest_seqno;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_flushjobinfo_flush_reason(const rocksdb_flushjobinfo_t* info) {
|
||||
return static_cast<uint32_t>(info->rep.flush_reason);
|
||||
}
|
||||
|
||||
uint32_t rocksdb_flushjobinfo_blob_compression_type(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return static_cast<uint32_t>(info->rep.blob_compression_type);
|
||||
}
|
||||
|
||||
/* CompactionJobInfo */
|
||||
|
||||
uint32_t rocksdb_compactionjobinfo_cf_id(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.cf_id;
|
||||
}
|
||||
|
||||
const char* rocksdb_compactionjobinfo_cf_name(
|
||||
const rocksdb_compactionjobinfo_t* info, size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
void rocksdb_compactionjobinfo_status(const rocksdb_compactionjobinfo_t* info,
|
||||
char** errptr) {
|
||||
SaveError(errptr, info->rep.status);
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_thread_id(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.thread_id;
|
||||
}
|
||||
|
||||
int rocksdb_compactionjobinfo_job_id(const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.job_id;
|
||||
}
|
||||
|
||||
int rocksdb_compactionjobinfo_num_l0_files(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.num_l0_files;
|
||||
}
|
||||
|
||||
int rocksdb_compactionjobinfo_base_input_level(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.base_input_level;
|
||||
}
|
||||
|
||||
int rocksdb_compactionjobinfo_output_level(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.output_level;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_compactionjobinfo_compaction_reason(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return static_cast<uint32_t>(info->rep.compaction_reason);
|
||||
}
|
||||
|
||||
uint32_t rocksdb_compactionjobinfo_compression(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return static_cast<uint32_t>(info->rep.compression);
|
||||
}
|
||||
|
||||
uint32_t rocksdb_compactionjobinfo_blob_compression_type(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return static_cast<uint32_t>(info->rep.blob_compression_type);
|
||||
}
|
||||
|
||||
unsigned char rocksdb_compactionjobinfo_aborted(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.aborted;
|
||||
}
|
||||
|
||||
/* SubcompactionJobInfo */
|
||||
|
||||
uint32_t rocksdb_subcompactionjobinfo_cf_id(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return info->rep.cf_id;
|
||||
}
|
||||
|
||||
const char* rocksdb_subcompactionjobinfo_cf_name(
|
||||
const rocksdb_subcompactionjobinfo_t* info, size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
void rocksdb_subcompactionjobinfo_status(
|
||||
const rocksdb_subcompactionjobinfo_t* info, char** errptr) {
|
||||
SaveError(errptr, info->rep.status);
|
||||
}
|
||||
|
||||
uint64_t rocksdb_subcompactionjobinfo_thread_id(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return info->rep.thread_id;
|
||||
}
|
||||
|
||||
int rocksdb_subcompactionjobinfo_job_id(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return info->rep.job_id;
|
||||
}
|
||||
|
||||
int rocksdb_subcompactionjobinfo_subcompaction_job_id(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return info->rep.subcompaction_job_id;
|
||||
}
|
||||
|
||||
int rocksdb_subcompactionjobinfo_base_input_level(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return info->rep.base_input_level;
|
||||
}
|
||||
|
||||
int rocksdb_subcompactionjobinfo_output_level(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return info->rep.output_level;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_subcompactionjobinfo_compaction_reason(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return static_cast<uint32_t>(info->rep.compaction_reason);
|
||||
}
|
||||
|
||||
uint32_t rocksdb_subcompactionjobinfo_compression(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return static_cast<uint32_t>(info->rep.compression);
|
||||
}
|
||||
|
||||
uint32_t rocksdb_subcompactionjobinfo_blob_compression_type(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return static_cast<uint32_t>(info->rep.blob_compression_type);
|
||||
}
|
||||
|
||||
/* ExternalFileIngestionInfo */
|
||||
|
||||
const char* rocksdb_externalfileingestioninfo_cf_name(
|
||||
const rocksdb_externalfileingestioninfo_t* info, size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_externalfileingestioninfo_external_file_path(
|
||||
const rocksdb_externalfileingestioninfo_t* info, size_t* size) {
|
||||
*size = info->rep.external_file_path.size();
|
||||
return info->rep.external_file_path.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_externalfileingestioninfo_internal_file_path(
|
||||
const rocksdb_externalfileingestioninfo_t* info, size_t* size) {
|
||||
*size = info->rep.internal_file_path.size();
|
||||
return info->rep.internal_file_path.data();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_externalfileingestioninfo_global_seqno(
|
||||
const rocksdb_externalfileingestioninfo_t* info) {
|
||||
return info->rep.global_seqno;
|
||||
}
|
||||
|
||||
/* MemTableInfo */
|
||||
|
||||
const char* rocksdb_memtableinfo_cf_name(const rocksdb_memtableinfo_t* info,
|
||||
size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_memtableinfo_first_seqno(const rocksdb_memtableinfo_t* info) {
|
||||
return info->rep.first_seqno;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_memtableinfo_earliest_seqno(
|
||||
const rocksdb_memtableinfo_t* info) {
|
||||
return info->rep.earliest_seqno;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_memtableinfo_num_entries(const rocksdb_memtableinfo_t* info) {
|
||||
return info->rep.num_entries;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_memtableinfo_num_deletes(const rocksdb_memtableinfo_t* info) {
|
||||
return info->rep.num_deletes;
|
||||
}
|
||||
|
||||
const char* rocksdb_memtableinfo_newest_udt(const rocksdb_memtableinfo_t* info,
|
||||
size_t* size) {
|
||||
*size = info->rep.newest_udt.size();
|
||||
return info->rep.newest_udt.data();
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/auto_simple_bindings.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Sources:
|
||||
// - include/rocksdb/listener.h
|
||||
// - tools/c_api_gen/auto_simple_bindings_blocklist.json
|
||||
// - include/rocksdb/c.h
|
||||
// - tools/c_api_gen/c_base.cc
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/auto_simple_bindings.py
|
||||
|
||||
// Output group: Auto-discovered JobInfo metadata simple
|
||||
|
||||
/* FlushJobInfo */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint32_t
|
||||
rocksdb_flushjobinfo_cf_id(const rocksdb_flushjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_flushjobinfo_cf_name(
|
||||
const rocksdb_flushjobinfo_t* info, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_flushjobinfo_file_path(
|
||||
const rocksdb_flushjobinfo_t* info, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_flushjobinfo_file_number(const rocksdb_flushjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_flushjobinfo_oldest_blob_file_number(
|
||||
const rocksdb_flushjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_flushjobinfo_thread_id(const rocksdb_flushjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_flushjobinfo_job_id(
|
||||
const rocksdb_flushjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_flushjobinfo_triggered_writes_slowdown(
|
||||
const rocksdb_flushjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_flushjobinfo_triggered_writes_stop(const rocksdb_flushjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_flushjobinfo_smallest_seqno(const rocksdb_flushjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_flushjobinfo_largest_seqno(const rocksdb_flushjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint32_t
|
||||
rocksdb_flushjobinfo_flush_reason(const rocksdb_flushjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint32_t
|
||||
rocksdb_flushjobinfo_blob_compression_type(const rocksdb_flushjobinfo_t* info);
|
||||
|
||||
/* CompactionJobInfo */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint32_t
|
||||
rocksdb_compactionjobinfo_cf_id(const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_compactionjobinfo_cf_name(
|
||||
const rocksdb_compactionjobinfo_t* info, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_compactionjobinfo_status(
|
||||
const rocksdb_compactionjobinfo_t* info, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compactionjobinfo_thread_id(const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_compactionjobinfo_job_id(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_compactionjobinfo_num_l0_files(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_compactionjobinfo_base_input_level(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_compactionjobinfo_output_level(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint32_t rocksdb_compactionjobinfo_compaction_reason(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint32_t
|
||||
rocksdb_compactionjobinfo_compression(const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint32_t
|
||||
rocksdb_compactionjobinfo_blob_compression_type(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_compactionjobinfo_aborted(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
/* SubcompactionJobInfo */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint32_t
|
||||
rocksdb_subcompactionjobinfo_cf_id(const rocksdb_subcompactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_subcompactionjobinfo_cf_name(
|
||||
const rocksdb_subcompactionjobinfo_t* info, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_subcompactionjobinfo_status(
|
||||
const rocksdb_subcompactionjobinfo_t* info, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_subcompactionjobinfo_thread_id(
|
||||
const rocksdb_subcompactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_subcompactionjobinfo_job_id(
|
||||
const rocksdb_subcompactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int
|
||||
rocksdb_subcompactionjobinfo_subcompaction_job_id(
|
||||
const rocksdb_subcompactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_subcompactionjobinfo_base_input_level(
|
||||
const rocksdb_subcompactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_subcompactionjobinfo_output_level(
|
||||
const rocksdb_subcompactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint32_t
|
||||
rocksdb_subcompactionjobinfo_compaction_reason(
|
||||
const rocksdb_subcompactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint32_t rocksdb_subcompactionjobinfo_compression(
|
||||
const rocksdb_subcompactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint32_t
|
||||
rocksdb_subcompactionjobinfo_blob_compression_type(
|
||||
const rocksdb_subcompactionjobinfo_t* info);
|
||||
|
||||
/* ExternalFileIngestionInfo */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char*
|
||||
rocksdb_externalfileingestioninfo_cf_name(
|
||||
const rocksdb_externalfileingestioninfo_t* info, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char*
|
||||
rocksdb_externalfileingestioninfo_external_file_path(
|
||||
const rocksdb_externalfileingestioninfo_t* info, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char*
|
||||
rocksdb_externalfileingestioninfo_internal_file_path(
|
||||
const rocksdb_externalfileingestioninfo_t* info, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_externalfileingestioninfo_global_seqno(
|
||||
const rocksdb_externalfileingestioninfo_t* info);
|
||||
|
||||
/* MemTableInfo */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_memtableinfo_cf_name(
|
||||
const rocksdb_memtableinfo_t* info, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_memtableinfo_first_seqno(const rocksdb_memtableinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_memtableinfo_earliest_seqno(const rocksdb_memtableinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_memtableinfo_num_entries(const rocksdb_memtableinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_memtableinfo_num_deletes(const rocksdb_memtableinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_memtableinfo_newest_udt(
|
||||
const rocksdb_memtableinfo_t* info, size_t* size);
|
||||
@@ -1,84 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'JobInfo metadata simple'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_jobinfo_metadata_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_jobinfo_metadata_subset.cc.inc
|
||||
|
||||
/* JobInfo metadata simple */
|
||||
|
||||
size_t rocksdb_compactionjobinfo_input_files_count(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.input_files.size();
|
||||
}
|
||||
|
||||
size_t rocksdb_compactionjobinfo_output_files_count(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.output_files.size();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_elapsed_micros(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.elapsed_micros;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_num_corrupt_keys(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_corrupt_keys;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_input_records(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_input_records;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_output_records(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_output_records;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_total_input_bytes(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.total_input_bytes;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_total_output_bytes(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.total_output_bytes;
|
||||
}
|
||||
|
||||
size_t rocksdb_compactionjobinfo_num_input_files(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_input_files;
|
||||
}
|
||||
|
||||
size_t rocksdb_compactionjobinfo_num_input_files_at_output_level(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_input_files_at_output_level;
|
||||
}
|
||||
|
||||
const char* rocksdb_writestallinfo_cf_name(const rocksdb_writestallinfo_t* info,
|
||||
size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
const rocksdb_writestallcondition_t* rocksdb_writestallinfo_cur(
|
||||
const rocksdb_writestallinfo_t* info) {
|
||||
return reinterpret_cast<const rocksdb_writestallcondition_t*>(
|
||||
&info->rep.condition.cur);
|
||||
}
|
||||
|
||||
const rocksdb_writestallcondition_t* rocksdb_writestallinfo_prev(
|
||||
const rocksdb_writestallinfo_t* info) {
|
||||
return reinterpret_cast<const rocksdb_writestallcondition_t*>(
|
||||
&info->rep.condition.prev);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'JobInfo metadata simple'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_jobinfo_metadata_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_jobinfo_metadata_subset.cc.inc
|
||||
|
||||
/* JobInfo metadata simple */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API size_t rocksdb_compactionjobinfo_input_files_count(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API size_t rocksdb_compactionjobinfo_output_files_count(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compactionjobinfo_elapsed_micros(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compactionjobinfo_num_corrupt_keys(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compactionjobinfo_input_records(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compactionjobinfo_output_records(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compactionjobinfo_total_input_bytes(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compactionjobinfo_total_output_bytes(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API size_t rocksdb_compactionjobinfo_num_input_files(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API size_t
|
||||
rocksdb_compactionjobinfo_num_input_files_at_output_level(
|
||||
const rocksdb_compactionjobinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_writestallinfo_cf_name(
|
||||
const rocksdb_writestallinfo_t* info, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const rocksdb_writestallcondition_t*
|
||||
rocksdb_writestallinfo_cur(const rocksdb_writestallinfo_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const rocksdb_writestallcondition_t*
|
||||
rocksdb_writestallinfo_prev(const rocksdb_writestallinfo_t* info);
|
||||
@@ -1,501 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/auto_simple_bindings.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Sources:
|
||||
// - include/rocksdb/compaction_job_stats.h
|
||||
// - include/rocksdb/listener.h
|
||||
// - include/rocksdb/table_properties.h
|
||||
// - tools/c_api_gen/auto_simple_bindings_blocklist.json
|
||||
// - include/rocksdb/c.h
|
||||
// - tools/c_api_gen/c_base.cc
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/auto_simple_bindings.py
|
||||
|
||||
// Output group: Auto-discovered metadata view structs simple
|
||||
|
||||
/* TableProperties */
|
||||
|
||||
uint64_t rocksdb_table_properties_orig_file_number(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.orig_file_number;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_data_size(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.data_size;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_uncompressed_data_size(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.uncompressed_data_size;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_index_size(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.index_size;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_index_partitions(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.index_partitions;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_top_level_index_size(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.top_level_index_size;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_index_key_is_user_key(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.index_key_is_user_key;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_index_value_is_delta_encoded(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.index_value_is_delta_encoded;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_udi_is_primary_index(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.udi_is_primary_index;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_filter_size(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.filter_size;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_raw_key_size(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.raw_key_size;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_raw_value_size(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.raw_value_size;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_num_data_blocks(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.num_data_blocks;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_num_data_blocks_compression_rejected(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.num_data_blocks_compression_rejected;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_num_data_blocks_compression_bypassed(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.num_data_blocks_compression_bypassed;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_num_uniform_blocks(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.num_uniform_blocks;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_num_entries(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.num_entries;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_num_filter_entries(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.num_filter_entries;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_num_deletions(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.num_deletions;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_num_merge_operands(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.num_merge_operands;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_num_range_deletions(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.num_range_deletions;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_format_version(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.format_version;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_fixed_key_len(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.fixed_key_len;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_column_family_id(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.column_family_id;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_creation_time(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.creation_time;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_oldest_key_time(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.oldest_key_time;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_newest_key_time(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.newest_key_time;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_file_creation_time(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.file_creation_time;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_slow_compression_estimated_data_size(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.slow_compression_estimated_data_size;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_fast_compression_estimated_data_size(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.fast_compression_estimated_data_size;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_external_sst_file_global_seqno_offset(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.external_sst_file_global_seqno_offset;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_tail_start_offset(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.tail_start_offset;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_user_defined_timestamps_persisted(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.user_defined_timestamps_persisted;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_key_largest_seqno(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.key_largest_seqno;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_key_smallest_seqno(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.key_smallest_seqno;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_data_block_restart_interval(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.data_block_restart_interval;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_index_block_restart_interval(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.index_block_restart_interval;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_separate_key_value_in_data_block(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.separate_key_value_in_data_block;
|
||||
}
|
||||
|
||||
const char* rocksdb_table_properties_db_id(
|
||||
const rocksdb_table_properties_t* props, size_t* size) {
|
||||
*size = props->rep.db_id.size();
|
||||
return props->rep.db_id.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_table_properties_db_session_id(
|
||||
const rocksdb_table_properties_t* props, size_t* size) {
|
||||
*size = props->rep.db_session_id.size();
|
||||
return props->rep.db_session_id.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_table_properties_db_host_id(
|
||||
const rocksdb_table_properties_t* props, size_t* size) {
|
||||
*size = props->rep.db_host_id.size();
|
||||
return props->rep.db_host_id.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_table_properties_column_family_name(
|
||||
const rocksdb_table_properties_t* props, size_t* size) {
|
||||
*size = props->rep.column_family_name.size();
|
||||
return props->rep.column_family_name.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_table_properties_filter_policy_name(
|
||||
const rocksdb_table_properties_t* props, size_t* size) {
|
||||
*size = props->rep.filter_policy_name.size();
|
||||
return props->rep.filter_policy_name.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_table_properties_comparator_name(
|
||||
const rocksdb_table_properties_t* props, size_t* size) {
|
||||
*size = props->rep.comparator_name.size();
|
||||
return props->rep.comparator_name.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_table_properties_merge_operator_name(
|
||||
const rocksdb_table_properties_t* props, size_t* size) {
|
||||
*size = props->rep.merge_operator_name.size();
|
||||
return props->rep.merge_operator_name.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_table_properties_prefix_extractor_name(
|
||||
const rocksdb_table_properties_t* props, size_t* size) {
|
||||
*size = props->rep.prefix_extractor_name.size();
|
||||
return props->rep.prefix_extractor_name.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_table_properties_property_collectors_names(
|
||||
const rocksdb_table_properties_t* props, size_t* size) {
|
||||
*size = props->rep.property_collectors_names.size();
|
||||
return props->rep.property_collectors_names.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_table_properties_compression_name(
|
||||
const rocksdb_table_properties_t* props, size_t* size) {
|
||||
*size = props->rep.compression_name.size();
|
||||
return props->rep.compression_name.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_table_properties_compression_options(
|
||||
const rocksdb_table_properties_t* props, size_t* size) {
|
||||
*size = props->rep.compression_options.size();
|
||||
return props->rep.compression_options.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_table_properties_seqno_to_time_mapping(
|
||||
const rocksdb_table_properties_t* props, size_t* size) {
|
||||
*size = props->rep.seqno_to_time_mapping.size();
|
||||
return props->rep.seqno_to_time_mapping.data();
|
||||
}
|
||||
|
||||
/* CompactionJobStats */
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_elapsed_micros(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.elapsed_micros;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_cpu_micros(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.cpu_micros;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_compaction_job_stats_has_accurate_num_input_records(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.has_accurate_num_input_records;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_num_input_records(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_input_records;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_num_blobs_read(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_blobs_read;
|
||||
}
|
||||
|
||||
size_t rocksdb_compaction_job_stats_num_input_files(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_input_files;
|
||||
}
|
||||
|
||||
size_t rocksdb_compaction_job_stats_num_input_files_trivially_moved(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_input_files_trivially_moved;
|
||||
}
|
||||
|
||||
size_t rocksdb_compaction_job_stats_num_input_files_at_output_level(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_input_files_at_output_level;
|
||||
}
|
||||
|
||||
size_t rocksdb_compaction_job_stats_num_filtered_input_files(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_filtered_input_files;
|
||||
}
|
||||
|
||||
size_t rocksdb_compaction_job_stats_num_filtered_input_files_at_output_level(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_filtered_input_files_at_output_level;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_num_output_records(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_output_records;
|
||||
}
|
||||
|
||||
size_t rocksdb_compaction_job_stats_num_output_files(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_output_files;
|
||||
}
|
||||
|
||||
size_t rocksdb_compaction_job_stats_num_output_files_blob(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_output_files_blob;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_compaction_job_stats_is_full_compaction(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.is_full_compaction;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_compaction_job_stats_is_manual_compaction(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.is_manual_compaction;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_compaction_job_stats_is_remote_compaction(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.is_remote_compaction;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_total_input_bytes(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.total_input_bytes;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_total_blob_bytes_read(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.total_blob_bytes_read;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_total_output_bytes(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.total_output_bytes;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_total_output_bytes_blob(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.total_output_bytes_blob;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_total_skipped_input_bytes(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.total_skipped_input_bytes;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_num_records_replaced(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_records_replaced;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_total_input_raw_key_bytes(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.total_input_raw_key_bytes;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_total_input_raw_value_bytes(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.total_input_raw_value_bytes;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_num_input_deletion_records(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_input_deletion_records;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_num_expired_deletion_records(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_expired_deletion_records;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_num_corrupt_keys(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_corrupt_keys;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_file_write_nanos(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.file_write_nanos;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_file_range_sync_nanos(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.file_range_sync_nanos;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_file_fsync_nanos(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.file_fsync_nanos;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_file_prepare_write_nanos(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.file_prepare_write_nanos;
|
||||
}
|
||||
|
||||
const char* rocksdb_compaction_job_stats_smallest_output_key_prefix(
|
||||
const rocksdb_compaction_job_stats_t* stats, size_t* size) {
|
||||
*size = stats->rep.smallest_output_key_prefix.size();
|
||||
return stats->rep.smallest_output_key_prefix.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_compaction_job_stats_largest_output_key_prefix(
|
||||
const rocksdb_compaction_job_stats_t* stats, size_t* size) {
|
||||
*size = stats->rep.largest_output_key_prefix.size();
|
||||
return stats->rep.largest_output_key_prefix.data();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_num_single_del_fallthru(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_single_del_fallthru;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_job_stats_num_single_del_mismatch(
|
||||
const rocksdb_compaction_job_stats_t* stats) {
|
||||
return stats->rep.num_single_del_mismatch;
|
||||
}
|
||||
|
||||
/* CompactionFileInfo */
|
||||
|
||||
int rocksdb_compaction_file_info_level(
|
||||
const rocksdb_compaction_file_info_t* info) {
|
||||
return info->rep.level;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_file_info_file_number(
|
||||
const rocksdb_compaction_file_info_t* info) {
|
||||
return info->rep.file_number;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compaction_file_info_oldest_blob_file_number(
|
||||
const rocksdb_compaction_file_info_t* info) {
|
||||
return info->rep.oldest_blob_file_number;
|
||||
}
|
||||
|
||||
/* BlobFileAdditionInfo */
|
||||
|
||||
uint64_t rocksdb_blob_file_addition_info_total_blob_count(
|
||||
const rocksdb_blob_file_addition_info_t* info) {
|
||||
return info->rep.total_blob_count;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_blob_file_addition_info_total_blob_bytes(
|
||||
const rocksdb_blob_file_addition_info_t* info) {
|
||||
return info->rep.total_blob_bytes;
|
||||
}
|
||||
|
||||
/* BlobFileGarbageInfo */
|
||||
|
||||
uint64_t rocksdb_blob_file_garbage_info_garbage_blob_count(
|
||||
const rocksdb_blob_file_garbage_info_t* info) {
|
||||
return info->rep.garbage_blob_count;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_blob_file_garbage_info_garbage_blob_bytes(
|
||||
const rocksdb_blob_file_garbage_info_t* info) {
|
||||
return info->rep.garbage_blob_bytes;
|
||||
}
|
||||
@@ -1,361 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/auto_simple_bindings.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Sources:
|
||||
// - include/rocksdb/compaction_job_stats.h
|
||||
// - include/rocksdb/listener.h
|
||||
// - include/rocksdb/table_properties.h
|
||||
// - tools/c_api_gen/auto_simple_bindings_blocklist.json
|
||||
// - include/rocksdb/c.h
|
||||
// - tools/c_api_gen/c_base.cc
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/auto_simple_bindings.py
|
||||
|
||||
// Output group: Auto-discovered metadata view structs simple
|
||||
|
||||
/* TableProperties */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_orig_file_number(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_data_size(const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_uncompressed_data_size(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_index_size(const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_index_partitions(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_top_level_index_size(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_index_key_is_user_key(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_index_value_is_delta_encoded(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_udi_is_primary_index(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_filter_size(const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_raw_key_size(const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_raw_value_size(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_num_data_blocks(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_num_data_blocks_compression_rejected(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_num_data_blocks_compression_bypassed(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_num_uniform_blocks(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_num_entries(const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_num_filter_entries(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_num_deletions(const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_num_merge_operands(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_num_range_deletions(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_format_version(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_fixed_key_len(const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_column_family_id(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_creation_time(const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_oldest_key_time(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_newest_key_time(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_file_creation_time(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_slow_compression_estimated_data_size(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_fast_compression_estimated_data_size(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_external_sst_file_global_seqno_offset(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_tail_start_offset(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_user_defined_timestamps_persisted(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_key_largest_seqno(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_key_smallest_seqno(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_data_block_restart_interval(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_index_block_restart_interval(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_separate_key_value_in_data_block(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_table_properties_db_id(
|
||||
const rocksdb_table_properties_t* props, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_table_properties_db_session_id(
|
||||
const rocksdb_table_properties_t* props, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_table_properties_db_host_id(
|
||||
const rocksdb_table_properties_t* props, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char*
|
||||
rocksdb_table_properties_column_family_name(
|
||||
const rocksdb_table_properties_t* props, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char*
|
||||
rocksdb_table_properties_filter_policy_name(
|
||||
const rocksdb_table_properties_t* props, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_table_properties_comparator_name(
|
||||
const rocksdb_table_properties_t* props, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char*
|
||||
rocksdb_table_properties_merge_operator_name(
|
||||
const rocksdb_table_properties_t* props, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char*
|
||||
rocksdb_table_properties_prefix_extractor_name(
|
||||
const rocksdb_table_properties_t* props, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char*
|
||||
rocksdb_table_properties_property_collectors_names(
|
||||
const rocksdb_table_properties_t* props, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char*
|
||||
rocksdb_table_properties_compression_name(
|
||||
const rocksdb_table_properties_t* props, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char*
|
||||
rocksdb_table_properties_compression_options(
|
||||
const rocksdb_table_properties_t* props, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char*
|
||||
rocksdb_table_properties_seqno_to_time_mapping(
|
||||
const rocksdb_table_properties_t* props, size_t* size);
|
||||
|
||||
/* CompactionJobStats */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compaction_job_stats_elapsed_micros(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compaction_job_stats_cpu_micros(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_compaction_job_stats_has_accurate_num_input_records(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_num_input_records(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compaction_job_stats_num_blobs_read(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API size_t rocksdb_compaction_job_stats_num_input_files(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API size_t
|
||||
rocksdb_compaction_job_stats_num_input_files_trivially_moved(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API size_t
|
||||
rocksdb_compaction_job_stats_num_input_files_at_output_level(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API size_t
|
||||
rocksdb_compaction_job_stats_num_filtered_input_files(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API size_t
|
||||
rocksdb_compaction_job_stats_num_filtered_input_files_at_output_level(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_num_output_records(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API size_t rocksdb_compaction_job_stats_num_output_files(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API size_t
|
||||
rocksdb_compaction_job_stats_num_output_files_blob(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_compaction_job_stats_is_full_compaction(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_compaction_job_stats_is_manual_compaction(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_compaction_job_stats_is_remote_compaction(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_total_input_bytes(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_total_blob_bytes_read(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_total_output_bytes(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_total_output_bytes_blob(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_total_skipped_input_bytes(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_num_records_replaced(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_total_input_raw_key_bytes(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_total_input_raw_value_bytes(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_num_input_deletion_records(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_num_expired_deletion_records(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_num_corrupt_keys(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_file_write_nanos(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_file_range_sync_nanos(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_file_fsync_nanos(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_file_prepare_write_nanos(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char*
|
||||
rocksdb_compaction_job_stats_smallest_output_key_prefix(
|
||||
const rocksdb_compaction_job_stats_t* stats, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API const char*
|
||||
rocksdb_compaction_job_stats_largest_output_key_prefix(
|
||||
const rocksdb_compaction_job_stats_t* stats, size_t* size);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_num_single_del_fallthru(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_job_stats_num_single_del_mismatch(
|
||||
const rocksdb_compaction_job_stats_t* stats);
|
||||
|
||||
/* CompactionFileInfo */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_compaction_file_info_level(
|
||||
const rocksdb_compaction_file_info_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compaction_file_info_file_number(
|
||||
const rocksdb_compaction_file_info_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_compaction_file_info_oldest_blob_file_number(
|
||||
const rocksdb_compaction_file_info_t* info);
|
||||
|
||||
/* BlobFileAdditionInfo */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_blob_file_addition_info_total_blob_count(
|
||||
const rocksdb_blob_file_addition_info_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_blob_file_addition_info_total_blob_bytes(
|
||||
const rocksdb_blob_file_addition_info_t* info);
|
||||
|
||||
/* BlobFileGarbageInfo */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_blob_file_garbage_info_garbage_blob_count(
|
||||
const rocksdb_blob_file_garbage_info_t* info);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_blob_file_garbage_info_garbage_blob_bytes(
|
||||
const rocksdb_blob_file_garbage_info_t* info);
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,84 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'Options simple'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_options_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_options_subset.cc.inc
|
||||
|
||||
/* Options simple */
|
||||
|
||||
void rocksdb_options_set_compression_options_zstd_max_train_bytes(
|
||||
rocksdb_options_t* opt, int zstd_max_train_bytes) {
|
||||
opt->rep.compression_opts.zstd_max_train_bytes = zstd_max_train_bytes;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_compression_options_zstd_max_train_bytes(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.compression_opts.zstd_max_train_bytes;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_options_use_zstd_dict_trainer(
|
||||
rocksdb_options_t* opt, unsigned char use_zstd_dict_trainer) {
|
||||
opt->rep.compression_opts.use_zstd_dict_trainer = use_zstd_dict_trainer;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_compression_options_use_zstd_dict_trainer(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.compression_opts.use_zstd_dict_trainer;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_options_parallel_threads(
|
||||
rocksdb_options_t* opt, int value) {
|
||||
opt->rep.compression_opts.parallel_threads = value;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_compression_options_parallel_threads(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.compression_opts.parallel_threads;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_options_max_dict_buffer_bytes(
|
||||
rocksdb_options_t* opt, uint64_t max_dict_buffer_bytes) {
|
||||
opt->rep.compression_opts.max_dict_buffer_bytes = max_dict_buffer_bytes;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_compression_options_max_dict_buffer_bytes(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.compression_opts.max_dict_buffer_bytes;
|
||||
}
|
||||
|
||||
unsigned char
|
||||
rocksdb_options_get_bottommost_compression_options_use_zstd_dict_trainer(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.bottommost_compression_opts.use_zstd_dict_trainer;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_use_fsync(rocksdb_options_t* opt, int use_fsync) {
|
||||
opt->rep.use_fsync = use_fsync;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_use_fsync(rocksdb_options_t* opt) {
|
||||
return opt->rep.use_fsync;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_disable_auto_compactions(rocksdb_options_t* opt,
|
||||
int disable) {
|
||||
opt->rep.disable_auto_compactions = disable;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_optimize_filters_for_hits(rocksdb_options_t* opt,
|
||||
int v) {
|
||||
opt->rep.optimize_filters_for_hits = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_report_bg_io_stats(rocksdb_options_t* opt, int v) {
|
||||
opt->rep.report_bg_io_stats = v;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'Options simple'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_options_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_options_subset.cc.inc
|
||||
|
||||
/* Options simple */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_compression_options_zstd_max_train_bytes(
|
||||
rocksdb_options_t* opt, int zstd_max_train_bytes);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int
|
||||
rocksdb_options_get_compression_options_zstd_max_train_bytes(
|
||||
rocksdb_options_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_compression_options_use_zstd_dict_trainer(
|
||||
rocksdb_options_t* opt, unsigned char use_zstd_dict_trainer);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_options_get_compression_options_use_zstd_dict_trainer(
|
||||
rocksdb_options_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_compression_options_parallel_threads(rocksdb_options_t* opt,
|
||||
int value);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int
|
||||
rocksdb_options_get_compression_options_parallel_threads(
|
||||
rocksdb_options_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_compression_options_max_dict_buffer_bytes(
|
||||
rocksdb_options_t* opt, uint64_t max_dict_buffer_bytes);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_options_get_compression_options_max_dict_buffer_bytes(
|
||||
rocksdb_options_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_options_get_bottommost_compression_options_use_zstd_dict_trainer(
|
||||
rocksdb_options_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_use_fsync(
|
||||
rocksdb_options_t* opt, int use_fsync);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_options_get_use_fsync(
|
||||
rocksdb_options_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_disable_auto_compactions(
|
||||
rocksdb_options_t* opt, int disable);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_optimize_filters_for_hits(
|
||||
rocksdb_options_t* opt, int v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_report_bg_io_stats(
|
||||
rocksdb_options_t* opt, int v);
|
||||
@@ -1,236 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/auto_simple_bindings.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Sources:
|
||||
// - include/rocksdb/options.h
|
||||
// - tools/c_api_gen/auto_simple_bindings_blocklist.json
|
||||
// - include/rocksdb/c.h
|
||||
// - tools/c_api_gen/c_base.cc
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/auto_simple_bindings.py
|
||||
|
||||
// Output group: Auto-discovered ReadOptions simple
|
||||
|
||||
/* ReadOptions */
|
||||
|
||||
void rocksdb_readoptions_set_deadline(rocksdb_readoptions_t* opt,
|
||||
uint64_t microseconds) {
|
||||
opt->rep.deadline = std::chrono::microseconds(microseconds);
|
||||
}
|
||||
|
||||
uint64_t rocksdb_readoptions_get_deadline(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.deadline.count();
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_io_timeout(rocksdb_readoptions_t* opt,
|
||||
uint64_t microseconds) {
|
||||
opt->rep.io_timeout = std::chrono::microseconds(microseconds);
|
||||
}
|
||||
|
||||
uint64_t rocksdb_readoptions_get_io_timeout(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.io_timeout.count();
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_read_tier(rocksdb_readoptions_t* opt, int v) {
|
||||
opt->rep.read_tier = static_cast<decltype(opt->rep.read_tier)>(v);
|
||||
}
|
||||
|
||||
int rocksdb_readoptions_get_read_tier(rocksdb_readoptions_t* opt) {
|
||||
return static_cast<int>(opt->rep.read_tier);
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_rate_limiter_priority(rocksdb_readoptions_t* opt,
|
||||
int v) {
|
||||
opt->rep.rate_limiter_priority =
|
||||
static_cast<decltype(opt->rep.rate_limiter_priority)>(v);
|
||||
}
|
||||
|
||||
int rocksdb_readoptions_get_rate_limiter_priority(rocksdb_readoptions_t* opt) {
|
||||
return static_cast<int>(opt->rep.rate_limiter_priority);
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_value_size_soft_limit(rocksdb_readoptions_t* opt,
|
||||
uint64_t v) {
|
||||
opt->rep.value_size_soft_limit = v;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_readoptions_get_value_size_soft_limit(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.value_size_soft_limit;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_verify_checksums(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.verify_checksums = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_verify_checksums(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.verify_checksums;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_fill_cache(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.fill_cache = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_fill_cache(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.fill_cache;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_ignore_range_deletions(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.ignore_range_deletions = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_ignore_range_deletions(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.ignore_range_deletions;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_async_io(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.async_io = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_async_io(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.async_io;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_optimize_multiget_for_io(
|
||||
rocksdb_readoptions_t* opt, unsigned char v) {
|
||||
opt->rep.optimize_multiget_for_io = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_optimize_multiget_for_io(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.optimize_multiget_for_io;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_readahead_size(rocksdb_readoptions_t* opt,
|
||||
size_t v) {
|
||||
opt->rep.readahead_size = v;
|
||||
}
|
||||
|
||||
size_t rocksdb_readoptions_get_readahead_size(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.readahead_size;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_max_skippable_internal_keys(
|
||||
rocksdb_readoptions_t* opt, uint64_t v) {
|
||||
opt->rep.max_skippable_internal_keys = v;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_readoptions_get_max_skippable_internal_keys(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.max_skippable_internal_keys;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_tailing(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.tailing = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_tailing(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.tailing;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_total_order_seek(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.total_order_seek = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_total_order_seek(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.total_order_seek;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_auto_prefix_mode(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.auto_prefix_mode = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_auto_prefix_mode(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.auto_prefix_mode;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_prefix_same_as_start(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.prefix_same_as_start = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_prefix_same_as_start(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.prefix_same_as_start;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_pin_data(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.pin_data = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_pin_data(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.pin_data;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_adaptive_readahead(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.adaptive_readahead = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_adaptive_readahead(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.adaptive_readahead;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_background_purge_on_iterator_cleanup(
|
||||
rocksdb_readoptions_t* opt, unsigned char v) {
|
||||
opt->rep.background_purge_on_iterator_cleanup = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_background_purge_on_iterator_cleanup(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.background_purge_on_iterator_cleanup;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_auto_readahead_size(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.auto_readahead_size = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_auto_readahead_size(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.auto_readahead_size;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_allow_unprepared_value(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.allow_unprepared_value = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_allow_unprepared_value(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.allow_unprepared_value;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_auto_refresh_iterator_with_snapshot(
|
||||
rocksdb_readoptions_t* opt, unsigned char v) {
|
||||
opt->rep.auto_refresh_iterator_with_snapshot = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_auto_refresh_iterator_with_snapshot(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.auto_refresh_iterator_with_snapshot;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_io_activity(rocksdb_readoptions_t* opt, int v) {
|
||||
opt->rep.io_activity = static_cast<decltype(opt->rep.io_activity)>(v);
|
||||
}
|
||||
|
||||
int rocksdb_readoptions_get_io_activity(rocksdb_readoptions_t* opt) {
|
||||
return static_cast<int>(opt->rep.io_activity);
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/auto_simple_bindings.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Sources:
|
||||
// - include/rocksdb/options.h
|
||||
// - tools/c_api_gen/auto_simple_bindings_blocklist.json
|
||||
// - include/rocksdb/c.h
|
||||
// - tools/c_api_gen/c_base.cc
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/auto_simple_bindings.py
|
||||
|
||||
// Output group: Auto-discovered ReadOptions simple
|
||||
|
||||
/* ReadOptions */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_deadline(
|
||||
rocksdb_readoptions_t* opt, uint64_t microseconds);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_readoptions_get_deadline(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_io_timeout(
|
||||
rocksdb_readoptions_t* opt, uint64_t microseconds);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_readoptions_get_io_timeout(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_read_tier(
|
||||
rocksdb_readoptions_t* opt, int v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_readoptions_get_read_tier(
|
||||
rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_rate_limiter_priority(
|
||||
rocksdb_readoptions_t* opt, int v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_readoptions_get_rate_limiter_priority(
|
||||
rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_value_size_soft_limit(
|
||||
rocksdb_readoptions_t* opt, uint64_t v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_readoptions_get_value_size_soft_limit(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_verify_checksums(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_readoptions_get_verify_checksums(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_fill_cache(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_readoptions_get_fill_cache(
|
||||
rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_ignore_range_deletions(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_readoptions_get_ignore_range_deletions(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_async_io(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_readoptions_get_async_io(
|
||||
rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_readoptions_set_optimize_multiget_for_io(rocksdb_readoptions_t* opt,
|
||||
unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_readoptions_get_optimize_multiget_for_io(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_readahead_size(
|
||||
rocksdb_readoptions_t* opt, size_t v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API size_t
|
||||
rocksdb_readoptions_get_readahead_size(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_readoptions_set_max_skippable_internal_keys(rocksdb_readoptions_t* opt,
|
||||
uint64_t v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_readoptions_get_max_skippable_internal_keys(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_tailing(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_readoptions_get_tailing(
|
||||
rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_total_order_seek(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_readoptions_get_total_order_seek(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_auto_prefix_mode(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_readoptions_get_auto_prefix_mode(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_prefix_same_as_start(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_readoptions_get_prefix_same_as_start(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_pin_data(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_readoptions_get_pin_data(
|
||||
rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_adaptive_readahead(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_readoptions_get_adaptive_readahead(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_readoptions_set_background_purge_on_iterator_cleanup(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_readoptions_get_background_purge_on_iterator_cleanup(
|
||||
rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_auto_readahead_size(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_readoptions_get_auto_readahead_size(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_allow_unprepared_value(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_readoptions_get_allow_unprepared_value(rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_readoptions_set_auto_refresh_iterator_with_snapshot(
|
||||
rocksdb_readoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_readoptions_get_auto_refresh_iterator_with_snapshot(
|
||||
rocksdb_readoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_io_activity(
|
||||
rocksdb_readoptions_t* opt, int v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_readoptions_get_io_activity(
|
||||
rocksdb_readoptions_t* opt);
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,371 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'DB data operations'
|
||||
// --section 'WriteBatch' --section 'Transaction'
|
||||
// --section 'TransactionDB simple'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_subset.cc.inc
|
||||
|
||||
/* DB data operations */
|
||||
|
||||
void rocksdb_put(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
SaveError(errptr,
|
||||
db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_put_cf(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen, const char* val,
|
||||
size_t vallen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Put(options->rep, column_family->rep,
|
||||
Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_delete(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen)));
|
||||
}
|
||||
|
||||
void rocksdb_delete_cf(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Delete(options->rep, column_family->rep,
|
||||
Slice(key, keylen)));
|
||||
}
|
||||
|
||||
void rocksdb_merge(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, const char* val,
|
||||
size_t vallen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Merge(options->rep, Slice(key, keylen),
|
||||
Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_merge_cf(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen, const char* val,
|
||||
size_t vallen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Merge(options->rep, column_family->rep,
|
||||
Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_write(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_writebatch_t* batch, char** errptr) {
|
||||
SaveError(errptr, db->rep->Write(options->rep, &batch->rep));
|
||||
}
|
||||
|
||||
void rocksdb_put_with_ts(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, const char* ts,
|
||||
size_t tslen, const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->Put(options->rep, Slice(key, keylen),
|
||||
Slice(ts, tslen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_put_cf_with_ts(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen, const char* ts,
|
||||
size_t tslen, const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
SaveError(errptr,
|
||||
db->rep->Put(options->rep, column_family->rep, Slice(key, keylen),
|
||||
Slice(ts, tslen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_delete_with_ts(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, const char* ts,
|
||||
size_t tslen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen),
|
||||
Slice(ts, tslen)));
|
||||
}
|
||||
|
||||
void rocksdb_delete_cf_with_ts(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen, const char* ts,
|
||||
size_t tslen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Delete(options->rep, column_family->rep,
|
||||
Slice(key, keylen), Slice(ts, tslen)));
|
||||
}
|
||||
|
||||
void rocksdb_singledelete(rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, char** errptr) {
|
||||
SaveError(errptr, db->rep->SingleDelete(options->rep, Slice(key, keylen)));
|
||||
}
|
||||
|
||||
void rocksdb_singledelete_cf(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen, char** errptr) {
|
||||
SaveError(errptr, db->rep->SingleDelete(options->rep, column_family->rep,
|
||||
Slice(key, keylen)));
|
||||
}
|
||||
|
||||
void rocksdb_singledelete_with_ts(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen,
|
||||
const char* ts, size_t tslen, char** errptr) {
|
||||
SaveError(errptr, db->rep->SingleDelete(options->rep, Slice(key, keylen),
|
||||
Slice(ts, tslen)));
|
||||
}
|
||||
|
||||
void rocksdb_singledelete_cf_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, char** errptr) {
|
||||
SaveError(errptr,
|
||||
db->rep->SingleDelete(options->rep, column_family->rep,
|
||||
Slice(key, keylen), Slice(ts, tslen)));
|
||||
}
|
||||
|
||||
void rocksdb_delete_range_cf(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* start_key, size_t start_key_len,
|
||||
const char* end_key, size_t end_key_len,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->DeleteRange(options->rep, column_family->rep,
|
||||
Slice(start_key, start_key_len),
|
||||
Slice(end_key, end_key_len)));
|
||||
}
|
||||
|
||||
void rocksdb_flush(rocksdb_t* db, const rocksdb_flushoptions_t* options,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->Flush(options->rep));
|
||||
}
|
||||
|
||||
void rocksdb_flush_cf(rocksdb_t* db, const rocksdb_flushoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->Flush(options->rep, column_family->rep));
|
||||
}
|
||||
|
||||
void rocksdb_flush_wal(rocksdb_t* db, unsigned char sync, char** errptr) {
|
||||
SaveError(errptr, db->rep->FlushWAL(sync));
|
||||
}
|
||||
|
||||
void rocksdb_pause_background_work(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->PauseBackgroundWork());
|
||||
}
|
||||
|
||||
void rocksdb_continue_background_work(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->ContinueBackgroundWork());
|
||||
}
|
||||
|
||||
void rocksdb_disable_file_deletions(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->DisableFileDeletions());
|
||||
}
|
||||
|
||||
void rocksdb_enable_file_deletions(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->EnableFileDeletions());
|
||||
}
|
||||
|
||||
void rocksdb_verify_checksum(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->VerifyChecksum());
|
||||
}
|
||||
|
||||
void rocksdb_verify_file_checksums(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->VerifyFileChecksums(ReadOptions()));
|
||||
}
|
||||
|
||||
void rocksdb_destroy_db(const rocksdb_options_t* options, const char* name,
|
||||
char** errptr) {
|
||||
SaveError(errptr, DestroyDB(name, options->rep));
|
||||
}
|
||||
|
||||
void rocksdb_repair_db(const rocksdb_options_t* options, const char* name,
|
||||
char** errptr) {
|
||||
SaveError(errptr, RepairDB(name, options->rep));
|
||||
}
|
||||
|
||||
/* WriteBatch */
|
||||
|
||||
void rocksdb_writebatch_clear(rocksdb_writebatch_t* b) { b->rep.Clear(); }
|
||||
|
||||
void rocksdb_writebatch_put(rocksdb_writebatch_t* b, const char* key,
|
||||
size_t klen, const char* val, size_t vlen) {
|
||||
b->rep.Put(Slice(key, klen), Slice(val, vlen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_put_cf(rocksdb_writebatch_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen) {
|
||||
b->rep.Put(column_family->rep, Slice(key, klen), Slice(val, vlen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_delete(rocksdb_writebatch_t* b, const char* key,
|
||||
size_t klen) {
|
||||
b->rep.Delete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_put_log_data(rocksdb_writebatch_t* b, const char* blob,
|
||||
size_t len) {
|
||||
b->rep.PutLogData(Slice(blob, len));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_set_save_point(rocksdb_writebatch_t* b) {
|
||||
b->rep.SetSavePoint();
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_rollback_to_save_point(rocksdb_writebatch_t* b,
|
||||
char** errptr) {
|
||||
SaveError(errptr, b->rep.RollbackToSavePoint());
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_pop_save_point(rocksdb_writebatch_t* b, char** errptr) {
|
||||
SaveError(errptr, b->rep.PopSavePoint());
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_verify_checksum(rocksdb_writebatch_t* b,
|
||||
char** errptr) {
|
||||
SaveError(errptr, b->rep.VerifyChecksum());
|
||||
}
|
||||
|
||||
/* Transaction */
|
||||
|
||||
void rocksdb_transaction_put(rocksdb_transaction_t* txn, const char* key,
|
||||
size_t klen, const char* val, size_t vlen,
|
||||
char** errptr) {
|
||||
SaveError(errptr, txn->rep->Put(Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_put_cf(rocksdb_transaction_t* txn,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Put(column_family->rep, Slice(key, klen),
|
||||
Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_merge(rocksdb_transaction_t* txn, const char* key,
|
||||
size_t klen, const char* val, size_t vlen,
|
||||
char** errptr) {
|
||||
SaveError(errptr, txn->rep->Merge(Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_merge_cf(rocksdb_transaction_t* txn,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Merge(column_family->rep, Slice(key, klen),
|
||||
Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_delete(rocksdb_transaction_t* txn, const char* key,
|
||||
size_t klen, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Delete(Slice(key, klen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_delete_cf(
|
||||
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Delete(column_family->rep, Slice(key, klen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_commit(rocksdb_transaction_t* txn, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Commit());
|
||||
}
|
||||
|
||||
void rocksdb_transaction_rollback(rocksdb_transaction_t* txn, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Rollback());
|
||||
}
|
||||
|
||||
void rocksdb_transaction_put_log_data(rocksdb_transaction_t* txn,
|
||||
const char* blob, size_t len) {
|
||||
txn->rep->PutLogData(Slice(blob, len));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_set_savepoint(rocksdb_transaction_t* txn) {
|
||||
txn->rep->SetSavePoint();
|
||||
}
|
||||
|
||||
void rocksdb_transaction_rollback_to_savepoint(rocksdb_transaction_t* txn,
|
||||
char** errptr) {
|
||||
SaveError(errptr, txn->rep->RollbackToSavePoint());
|
||||
}
|
||||
|
||||
/* TransactionDB simple */
|
||||
|
||||
void rocksdb_transactiondb_put(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr) {
|
||||
SaveError(errptr,
|
||||
txn_db->rep->Put(options->rep, Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_put_cf(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen,
|
||||
const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Put(options->rep, column_family->rep,
|
||||
Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_write(rocksdb_transactiondb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_writebatch_t* batch, char** errptr) {
|
||||
SaveError(errptr, db->rep->Write(options->rep, &batch->rep));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_merge(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Merge(options->rep, Slice(key, klen),
|
||||
Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_merge_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key, size_t klen,
|
||||
const char* val, size_t vlen, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Merge(options->rep, column_family->rep,
|
||||
Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_delete(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Delete(options->rep, Slice(key, klen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_delete_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Delete(options->rep, column_family->rep,
|
||||
Slice(key, keylen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_flush_wal(rocksdb_transactiondb_t* txn_db,
|
||||
unsigned char sync, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->FlushWAL(sync));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_flush(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_flushoptions_t* options,
|
||||
char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Flush(options->rep));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_flush_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_flushoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Flush(options->rep, column_family->rep));
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'DB data operations'
|
||||
// --section 'WriteBatch' --section 'Transaction'
|
||||
// --section 'TransactionDB simple'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_subset.cc.inc
|
||||
|
||||
/* DB data operations */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_put(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, const char* val, size_t vallen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_put_cf(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* val, size_t vallen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_delete(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_delete_cf(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_merge(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, const char* val, size_t vallen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_merge_cf(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* val, size_t vallen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_write(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_writebatch_t* batch, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_put_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, const char* val, size_t vallen,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_put_cf_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, const char* val, size_t vallen,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_delete_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_delete_cf_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete_cf(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete_cf_with_ts(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* ts, size_t tslen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_delete_range_cf(
|
||||
rocksdb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* start_key,
|
||||
size_t start_key_len, const char* end_key, size_t end_key_len,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_flush(
|
||||
rocksdb_t* db, const rocksdb_flushoptions_t* options, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_flush_cf(
|
||||
rocksdb_t* db, const rocksdb_flushoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_flush_wal(rocksdb_t* db,
|
||||
unsigned char sync,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_pause_background_work(rocksdb_t* db,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_continue_background_work(rocksdb_t* db,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_disable_file_deletions(rocksdb_t* db,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_enable_file_deletions(rocksdb_t* db,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_verify_checksum(rocksdb_t* db,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_verify_file_checksums(rocksdb_t* db,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_destroy_db(
|
||||
const rocksdb_options_t* options, const char* name, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_repair_db(
|
||||
const rocksdb_options_t* options, const char* name, char** errptr);
|
||||
|
||||
/* WriteBatch */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_clear(
|
||||
rocksdb_writebatch_t* b);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_put(rocksdb_writebatch_t* b,
|
||||
const char* key,
|
||||
size_t klen,
|
||||
const char* val,
|
||||
size_t vlen);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_put_cf(
|
||||
rocksdb_writebatch_t* b, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val, size_t vlen);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_delete(
|
||||
rocksdb_writebatch_t* b, const char* key, size_t klen);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_put_log_data(
|
||||
rocksdb_writebatch_t* b, const char* blob, size_t len);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_set_save_point(
|
||||
rocksdb_writebatch_t* b);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_rollback_to_save_point(
|
||||
rocksdb_writebatch_t* b, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_pop_save_point(
|
||||
rocksdb_writebatch_t* b, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_verify_checksum(
|
||||
rocksdb_writebatch_t* b, char** errptr);
|
||||
|
||||
/* Transaction */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_put(
|
||||
rocksdb_transaction_t* txn, const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_put_cf(
|
||||
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_merge(
|
||||
rocksdb_transaction_t* txn, const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_merge_cf(
|
||||
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_delete(
|
||||
rocksdb_transaction_t* txn, const char* key, size_t klen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_delete_cf(
|
||||
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_commit(
|
||||
rocksdb_transaction_t* txn, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_rollback(
|
||||
rocksdb_transaction_t* txn, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_put_log_data(
|
||||
rocksdb_transaction_t* txn, const char* blob, size_t len);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_set_savepoint(
|
||||
rocksdb_transaction_t* txn);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_rollback_to_savepoint(
|
||||
rocksdb_transaction_t* txn, char** errptr);
|
||||
|
||||
/* TransactionDB simple */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_put(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_put_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* val, size_t vallen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_write(
|
||||
rocksdb_transactiondb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_writebatch_t* batch, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_merge(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_merge_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key, size_t klen,
|
||||
const char* val, size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_delete(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_delete_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_flush_wal(
|
||||
rocksdb_transactiondb_t* txn_db, unsigned char sync, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_flush(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_flushoptions_t* options,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_flush_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_flushoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, char** errptr);
|
||||
@@ -1,77 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'Transaction'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_transaction_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_transaction_subset.cc.inc
|
||||
|
||||
/* Transaction */
|
||||
|
||||
void rocksdb_transaction_put(rocksdb_transaction_t* txn, const char* key,
|
||||
size_t klen, const char* val, size_t vlen,
|
||||
char** errptr) {
|
||||
SaveError(errptr, txn->rep->Put(Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_put_cf(rocksdb_transaction_t* txn,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Put(column_family->rep, Slice(key, klen),
|
||||
Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_merge(rocksdb_transaction_t* txn, const char* key,
|
||||
size_t klen, const char* val, size_t vlen,
|
||||
char** errptr) {
|
||||
SaveError(errptr, txn->rep->Merge(Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_merge_cf(rocksdb_transaction_t* txn,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Merge(column_family->rep, Slice(key, klen),
|
||||
Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_delete(rocksdb_transaction_t* txn, const char* key,
|
||||
size_t klen, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Delete(Slice(key, klen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_delete_cf(
|
||||
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Delete(column_family->rep, Slice(key, klen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_commit(rocksdb_transaction_t* txn, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Commit());
|
||||
}
|
||||
|
||||
void rocksdb_transaction_rollback(rocksdb_transaction_t* txn, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Rollback());
|
||||
}
|
||||
|
||||
void rocksdb_transaction_put_log_data(rocksdb_transaction_t* txn,
|
||||
const char* blob, size_t len) {
|
||||
txn->rep->PutLogData(Slice(blob, len));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_set_savepoint(rocksdb_transaction_t* txn) {
|
||||
txn->rep->SetSavePoint();
|
||||
}
|
||||
|
||||
void rocksdb_transaction_rollback_to_savepoint(rocksdb_transaction_t* txn,
|
||||
char** errptr) {
|
||||
SaveError(errptr, txn->rep->RollbackToSavePoint());
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'Transaction'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_transaction_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_transaction_subset.cc.inc
|
||||
|
||||
/* Transaction */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_put(
|
||||
rocksdb_transaction_t* txn, const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_put_cf(
|
||||
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_merge(
|
||||
rocksdb_transaction_t* txn, const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_merge_cf(
|
||||
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_delete(
|
||||
rocksdb_transaction_t* txn, const char* key, size_t klen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_delete_cf(
|
||||
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_commit(
|
||||
rocksdb_transaction_t* txn, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_rollback(
|
||||
rocksdb_transaction_t* txn, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_put_log_data(
|
||||
rocksdb_transaction_t* txn, const char* blob, size_t len);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_set_savepoint(
|
||||
rocksdb_transaction_t* txn);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_rollback_to_savepoint(
|
||||
rocksdb_transaction_t* txn, char** errptr);
|
||||
@@ -1,87 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'TransactionDB simple'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_transactiondb_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_transactiondb_subset.cc.inc
|
||||
|
||||
/* TransactionDB simple */
|
||||
|
||||
void rocksdb_transactiondb_put(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr) {
|
||||
SaveError(errptr,
|
||||
txn_db->rep->Put(options->rep, Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_put_cf(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen,
|
||||
const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Put(options->rep, column_family->rep,
|
||||
Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_write(rocksdb_transactiondb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_writebatch_t* batch, char** errptr) {
|
||||
SaveError(errptr, db->rep->Write(options->rep, &batch->rep));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_merge(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Merge(options->rep, Slice(key, klen),
|
||||
Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_merge_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key, size_t klen,
|
||||
const char* val, size_t vlen, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Merge(options->rep, column_family->rep,
|
||||
Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_delete(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Delete(options->rep, Slice(key, klen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_delete_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Delete(options->rep, column_family->rep,
|
||||
Slice(key, keylen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_flush_wal(rocksdb_transactiondb_t* txn_db,
|
||||
unsigned char sync, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->FlushWAL(sync));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_flush(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_flushoptions_t* options,
|
||||
char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Flush(options->rep));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_flush_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_flushoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Flush(options->rep, column_family->rep));
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'TransactionDB simple'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_transactiondb_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_transactiondb_subset.cc.inc
|
||||
|
||||
/* TransactionDB simple */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_put(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_put_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, const char* val, size_t vallen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_write(
|
||||
rocksdb_transactiondb_t* db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_writebatch_t* batch, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_merge(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_merge_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key, size_t klen,
|
||||
const char* val, size_t vlen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_delete(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_delete_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_flush_wal(
|
||||
rocksdb_transactiondb_t* txn_db, unsigned char sync, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_flush(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_flushoptions_t* options,
|
||||
char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_flush_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_flushoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, char** errptr);
|
||||
@@ -1,58 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'WriteBatch'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_writebatch_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_writebatch_subset.cc.inc
|
||||
|
||||
/* WriteBatch */
|
||||
|
||||
void rocksdb_writebatch_clear(rocksdb_writebatch_t* b) { b->rep.Clear(); }
|
||||
|
||||
void rocksdb_writebatch_put(rocksdb_writebatch_t* b, const char* key,
|
||||
size_t klen, const char* val, size_t vlen) {
|
||||
b->rep.Put(Slice(key, klen), Slice(val, vlen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_put_cf(rocksdb_writebatch_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen) {
|
||||
b->rep.Put(column_family->rep, Slice(key, klen), Slice(val, vlen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_delete(rocksdb_writebatch_t* b, const char* key,
|
||||
size_t klen) {
|
||||
b->rep.Delete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_put_log_data(rocksdb_writebatch_t* b, const char* blob,
|
||||
size_t len) {
|
||||
b->rep.PutLogData(Slice(blob, len));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_set_save_point(rocksdb_writebatch_t* b) {
|
||||
b->rep.SetSavePoint();
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_rollback_to_save_point(rocksdb_writebatch_t* b,
|
||||
char** errptr) {
|
||||
SaveError(errptr, b->rep.RollbackToSavePoint());
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_pop_save_point(rocksdb_writebatch_t* b, char** errptr) {
|
||||
SaveError(errptr, b->rep.PopSavePoint());
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_verify_checksum(rocksdb_writebatch_t* b,
|
||||
char** errptr) {
|
||||
SaveError(errptr, b->rep.VerifyChecksum());
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// @generated
|
||||
// -----------------------------------------------------------------------------
|
||||
// Auto-generated by tools/c_api_gen/generate_c_api.py.
|
||||
// DO NOT EDIT THIS FILE DIRECTLY.
|
||||
// Source: tools/c_api_gen/spec.json
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// To regenerate this file:
|
||||
// python3 tools/c_api_gen/generate_c_api.py
|
||||
// --spec tools/c_api_gen/spec.json --section 'WriteBatch'
|
||||
// --header-out
|
||||
// c_api_gen/c_generated_writebatch_subset.h.inc
|
||||
// --source-out
|
||||
// c_api_gen/c_generated_writebatch_subset.cc.inc
|
||||
|
||||
/* WriteBatch */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_clear(
|
||||
rocksdb_writebatch_t* b);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_put(rocksdb_writebatch_t* b,
|
||||
const char* key,
|
||||
size_t klen,
|
||||
const char* val,
|
||||
size_t vlen);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_put_cf(
|
||||
rocksdb_writebatch_t* b, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val, size_t vlen);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_delete(
|
||||
rocksdb_writebatch_t* b, const char* key, size_t klen);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_put_log_data(
|
||||
rocksdb_writebatch_t* b, const char* blob, size_t len);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_set_save_point(
|
||||
rocksdb_writebatch_t* b);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_rollback_to_save_point(
|
||||
rocksdb_writebatch_t* b, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_pop_save_point(
|
||||
rocksdb_writebatch_t* b, char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_verify_checksum(
|
||||
rocksdb_writebatch_t* b, char** errptr);
|
||||
Vendored
-18
@@ -127,24 +127,6 @@ class OffsetableCacheKey : private CacheKey {
|
||||
return CacheKey(file_num_etc64_, offset_etc64_ ^ offset);
|
||||
}
|
||||
|
||||
// Construct a CacheKey for a record located at byte `offset` within a file in
|
||||
// which every distinct cached region is at least 5 bytes long. The low two
|
||||
// offset bits are dropped, which is collision-free under that >= 5-byte (>= 4
|
||||
// would suffice) guarantee and lets different kinds of records in the same
|
||||
// file share a single keyspace without colliding.
|
||||
//
|
||||
// This is the canonical cache-key scheme for byte-offset records that carry
|
||||
// the block-based "min 5 byte" guarantee: block-based SST blocks (see
|
||||
// BlockBasedTable::GetCacheKey) and SimpleGen2Blob records (which always
|
||||
// include a >= 5-byte trailer; see db/blob/blob_gen2_format.h). Because both
|
||||
// use this same function, an SST's data blocks and its embedded blob records
|
||||
// never collide even when the block cache and blob cache are the same cache.
|
||||
// Keeping a single implementation here avoids a divergence bug that would
|
||||
// silently alias the two keyspaces.
|
||||
inline CacheKey WithOffsetForMinSizeRecord(uint64_t offset) const {
|
||||
return WithOffset(offset >> 2);
|
||||
}
|
||||
|
||||
// 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.
|
||||
static constexpr size_t kCommonPrefixSize = 8;
|
||||
|
||||
Vendored
+1
-1
@@ -3108,7 +3108,7 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::Lookup(
|
||||
HandleImpl* const arr = array_.Get();
|
||||
NextWithShift next_with_shift = arr[home].head_next_with_shift.LoadRelaxed();
|
||||
for (size_t i = 0; !next_with_shift.IsEnd() && i < 10; ++i) {
|
||||
HandleImpl* h = &arr[next_with_shift.GetNext()];
|
||||
HandleImpl* h = &arr[next_with_shift.IsEnd()];
|
||||
// Attempt cheap key match without acquiring a read ref. This could give a
|
||||
// false positive, which is re-checked after acquiring read ref, or false
|
||||
// negative, which is re-checked in the full Lookup. Also, this is a
|
||||
|
||||
+1
-24
@@ -421,30 +421,6 @@ Example reference: commit `429b36c22d76403d275dd0e6877b08d4cea2bc90` (block_alig
|
||||
|
||||
If an option already exists but needs C API support:
|
||||
|
||||
**First decide whether the option is auto-managed.**
|
||||
|
||||
Many simple option fields are no longer maintained by hand in
|
||||
`include/rocksdb/c.h` and `db/c.cc`. Before adding a manual wrapper:
|
||||
|
||||
- Check whether the option belongs to a family handled by
|
||||
`tools/c_api_gen/auto_simple_bindings.py`.
|
||||
- If it is a simple scalar/enum/string field in an auto-managed family, run
|
||||
`python3 tools/c_api_gen/regen_all.py` and let the generator add the C API.
|
||||
- After regenerating, run
|
||||
`python3 tools/c_api_gen/verify_generated_up_to_date.py` to confirm the
|
||||
checked-in generated fragments stay stable.
|
||||
- Do not edit generated `.inc` files under `c_api_gen/` by hand.
|
||||
- If the new field is not ready for C API support yet, add an entry to
|
||||
`tools/c_api_gen/auto_simple_bindings_blocklist.json` with
|
||||
`"policy": "deferred"` and a concrete reason so the build stays intentional
|
||||
rather than silently drifting.
|
||||
- If the field needs custom ownership, callback, vector, or nested-struct
|
||||
marshalling, keep it manual or move it into `tools/c_api_gen/spec.json`
|
||||
depending on the API shape.
|
||||
|
||||
If the option is not auto-managed, or the C API really is a custom/manual case,
|
||||
the traditional hand-written pattern still applies:
|
||||
|
||||
**File: `db/c.cc`**
|
||||
|
||||
```cpp
|
||||
@@ -533,3 +509,4 @@ Common option types used in serialization:
|
||||
- [ ] unreleased_history markdown file
|
||||
- [ ] Java API (for BlockBasedTableOptions)
|
||||
- [ ] C API (if needed)
|
||||
|
||||
@@ -168,18 +168,9 @@ Status YourNewAPI(const YourAPIOptions& /*options*/,
|
||||
|
||||
### Step 5: Add C API Bindings
|
||||
|
||||
> **Important:** `include/rocksdb/c.h` and `db/c.cc` are `@generated` and must
|
||||
> NOT be edited by hand — your changes would be overwritten on the next
|
||||
> regeneration. Choose the right path first (see "Before writing C API code by
|
||||
> hand" below). For hand-written (manual) wrappers, edit the source templates
|
||||
> `tools/c_api_gen/c_base.h` (declarations) and `tools/c_api_gen/c_base.cc`
|
||||
> (implementations), then run `python3 tools/c_api_gen/regen_all.py` to produce
|
||||
> `c.h` / `c.cc`. The snippets below show the *declaration* and *implementation*
|
||||
> you would add to those templates.
|
||||
**Header:** `include/rocksdb/c.h`
|
||||
|
||||
**Header (declaration → `tools/c_api_gen/c_base.h`):**
|
||||
|
||||
```c
|
||||
\`\`\`c
|
||||
// Basic version
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_your_new_api(
|
||||
rocksdb_t* db,
|
||||
@@ -200,7 +191,7 @@ extern ROCKSDB_LIBRARY_API void rocksdb_your_new_api_opt(
|
||||
char** errptr);
|
||||
\`\`\`
|
||||
|
||||
**Implementation (→ `tools/c_api_gen/c_base.cc`):**
|
||||
**Implementation:** `db/c.cc`
|
||||
|
||||
\`\`\`cpp
|
||||
void rocksdb_your_new_api(rocksdb_t* db, const char* start_key,
|
||||
@@ -226,38 +217,6 @@ void rocksdb_your_new_api_cf(rocksdb_t* db,
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
**Before writing C API code by hand, choose the right path:**
|
||||
|
||||
- **Auto-managed simple struct fields:** If you added a new field to a managed
|
||||
public struct such as `ReadOptions`, selected option structs, or managed
|
||||
metadata structs, first check `tools/c_api_gen/auto_simple_bindings.py`.
|
||||
Simple scalar/enum/string fields should be regenerated with
|
||||
`python3 tools/c_api_gen/regen_all.py` rather than hand-editing
|
||||
`include/rocksdb/c.h` / `db/c.cc`.
|
||||
After regenerating, run
|
||||
`python3 tools/c_api_gen/verify_generated_up_to_date.py` to confirm the
|
||||
checked-in generated fragments are stable.
|
||||
- **Spec-driven wrappers:** If the API is still mechanically generated but needs
|
||||
an explicit C shape, naming, or `Status`/`char** errptr` policy, add it to
|
||||
`tools/c_api_gen/spec.json`.
|
||||
- **Fully manual wrappers:** If the API uses callbacks, ownership transfer,
|
||||
vectors/maps, open flows, or otherwise irregular marshalling, keep the C API
|
||||
hand-written.
|
||||
|
||||
**Temporary deferral for auto-managed families:**
|
||||
|
||||
If a new field lands in an auto-managed family but the C API shape is not ready
|
||||
yet, add a checked-in entry to
|
||||
`tools/c_api_gen/auto_simple_bindings_blocklist.json` with:
|
||||
|
||||
- `policy: "manual"` when the field is intentionally outside simple auto-gen
|
||||
- `policy: "deferred"` when the field should be revisited later
|
||||
- a concrete `reason`
|
||||
- `tracking_issue` when available
|
||||
|
||||
If a field in an auto-managed family is not supported by the generator and is
|
||||
not covered by the blocklist, regeneration should fail. That is intentional.
|
||||
|
||||
**If you have options, also add:**
|
||||
|
||||
\`\`\`cpp
|
||||
@@ -523,8 +482,8 @@ public class YourAPIOptionsTest {
|
||||
| StackableDB | `include/rocksdb/utilities/stackable_db.h` | ✓ |
|
||||
| Secondary DB | `db/db_impl/db_impl_secondary.h` | If not supported |
|
||||
| Compacted DB | `db/db_impl/compacted_db_impl.h` | If not supported |
|
||||
| C API Header (manual wrappers) | `tools/c_api_gen/c_base.h` → regen → `include/rocksdb/c.h` (`@generated`) | ✓ |
|
||||
| C API Implementation (manual wrappers) | `tools/c_api_gen/c_base.cc` → regen → `db/c.cc` (`@generated`) | ✓ |
|
||||
| C API Header | `include/rocksdb/c.h` | ✓ |
|
||||
| C API Implementation | `db/c.cc` | ✓ |
|
||||
| Java API | `java/src/main/java/org/rocksdb/RocksDB.java` | ✓ |
|
||||
| Java Options | `java/src/main/java/org/rocksdb/YourAPIOptions.java` | If needed |
|
||||
| JNI Implementation | `java/rocksjni/rocksjni.cc` | ✓ |
|
||||
|
||||
@@ -44,6 +44,10 @@ if(@WITH_NUMA@)
|
||||
find_dependency(NUMA)
|
||||
endif()
|
||||
|
||||
if(@WITH_TBB@)
|
||||
find_dependency(TBB)
|
||||
endif()
|
||||
|
||||
find_dependency(Threads)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/RocksDBTargets.cmake")
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# - Find TBB
|
||||
# Find the Thread Building Blocks library and includes
|
||||
#
|
||||
# TBB_INCLUDE_DIRS - where to find tbb.h, etc.
|
||||
# TBB_LIBRARIES - List of libraries when using TBB.
|
||||
# TBB_FOUND - True if TBB found.
|
||||
|
||||
if(NOT DEFINED TBB_ROOT_DIR)
|
||||
set(TBB_ROOT_DIR "$ENV{TBBROOT}")
|
||||
endif()
|
||||
|
||||
find_path(TBB_INCLUDE_DIRS
|
||||
NAMES tbb/tbb.h
|
||||
HINTS ${TBB_ROOT_DIR}/include)
|
||||
|
||||
find_library(TBB_LIBRARIES
|
||||
NAMES tbb
|
||||
HINTS ${TBB_ROOT_DIR}/lib ENV LIBRARY_PATH)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(TBB DEFAULT_MSG TBB_LIBRARIES TBB_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
TBB_LIBRARIES
|
||||
TBB_INCLUDE_DIRS)
|
||||
|
||||
if(TBB_FOUND AND NOT (TARGET TBB::TBB))
|
||||
add_library (TBB::TBB UNKNOWN IMPORTED)
|
||||
set_target_properties(TBB::TBB
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${TBB_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS})
|
||||
endif()
|
||||
@@ -11,24 +11,6 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// WARNING: This value is == kCurrentFileBlobIndexFileNumber.
|
||||
// Use this name only where file number zero means "no valid blob file" or
|
||||
// "current file" is not understood/supported.
|
||||
constexpr uint64_t kInvalidBlobFileNumber = 0;
|
||||
|
||||
// WARNING: This value is == kInvalidBlobFileNumber.
|
||||
// Use this name only for BlobIndex references to the same physical file as what
|
||||
// is currently being read; generic blob-file metadata must treat zero as
|
||||
// invalid. (Using a distinct value like 1 was found to be more problematic,
|
||||
// e.g. because of legacy "stackable" blob implementation.)
|
||||
//
|
||||
// This "zero is invalid unless you are the embedded reader/writer" contract is
|
||||
// enforced by integrity checks that reject file number zero on generic paths;
|
||||
// see FileMetaData::UpdateBoundaries (write/output path) and Version::GetBlob /
|
||||
// Version::MultiGetBlob (read path). Same-file references must be resolved (by
|
||||
// EmbeddedBlobResolvingIterator) before they reach those paths. Do not weaken
|
||||
// those checks -- they catch leaks/corruption closer to the root cause.
|
||||
constexpr uint64_t kCurrentFileBlobIndexFileNumber = kInvalidBlobFileNumber;
|
||||
static_assert(kCurrentFileBlobIndexFileNumber == kInvalidBlobFileNumber);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -32,9 +32,6 @@ Status BlobGarbageMeter::GetBlobReferenceDetails(const ParsedInternalKey& ikey,
|
||||
if (blob_index.IsInlined() || blob_index.HasTTL()) {
|
||||
return Status::Corruption("Unexpected TTL/inlined blob index");
|
||||
}
|
||||
if (blob_index.IsSameFile()) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
*blob_file_number = blob_index.file_number();
|
||||
*bytes =
|
||||
@@ -128,9 +125,7 @@ Status BlobGarbageMeter::ProcessEntityBlobReferences(
|
||||
!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (file_number != kInvalidBlobFileNumber) {
|
||||
AddFlow(file_number, blob_bytes, is_inflow);
|
||||
}
|
||||
AddFlow(file_number, blob_bytes, is_inflow);
|
||||
return Status::OK();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -151,24 +151,6 @@ TEST(BlobGarbageMeterTest, PlainValue) {
|
||||
ASSERT_TRUE(blob_garbage_meter.flows().empty());
|
||||
}
|
||||
|
||||
TEST(BlobGarbageMeterTest, SameFileBlobIndex) {
|
||||
constexpr char user_key[] = "user_key";
|
||||
constexpr SequenceNumber seq = 123;
|
||||
|
||||
const InternalKey key(user_key, seq, kTypeBlobIndex);
|
||||
const Slice key_slice = key.Encode();
|
||||
|
||||
std::string value;
|
||||
BlobIndex::EncodeBlob(&value, kCurrentFileBlobIndexFileNumber,
|
||||
/*offset=*/123, /*size=*/456, kNoCompression);
|
||||
|
||||
BlobGarbageMeter blob_garbage_meter;
|
||||
|
||||
ASSERT_OK(blob_garbage_meter.ProcessInFlow(key_slice, value));
|
||||
ASSERT_OK(blob_garbage_meter.ProcessOutFlow(key_slice, value));
|
||||
ASSERT_TRUE(blob_garbage_meter.flows().empty());
|
||||
}
|
||||
|
||||
TEST(BlobGarbageMeterTest, CorruptInternalKey) {
|
||||
constexpr char corrupt_key[] = "i_am_corrupt";
|
||||
const Slice key_slice(corrupt_key);
|
||||
|
||||
@@ -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 "db/blob/blob_gen2_format.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "file/random_access_file_reader.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "table/format.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
Status ReadAndVerifySimpleGen2BlobRecord(
|
||||
const ReadOptions& read_options, RandomAccessFileReader* file,
|
||||
uint64_t record_offset, size_t payload_size, size_t record_size,
|
||||
ChecksumType checksum_type, uint32_t base_context_checksum,
|
||||
CompressionType expected_compression, char* buf) {
|
||||
assert(file != nullptr);
|
||||
assert(buf != nullptr);
|
||||
assert(record_size == payload_size + kSimpleGen2BlobTrailerSize);
|
||||
|
||||
Slice result;
|
||||
IOOptions opts;
|
||||
IODebugContext dbg;
|
||||
Status s = file->PrepareIOOptions(read_options, opts, &dbg);
|
||||
if (s.ok()) {
|
||||
s = file->Read(opts, record_offset, record_size, &result, buf, nullptr,
|
||||
&dbg);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (result.size() != record_size) {
|
||||
return Status::Corruption("Could not read complete blob record");
|
||||
}
|
||||
// With mmap reads the data lands outside `buf`; copy it in so the caller can
|
||||
// rely on `buf` owning the bytes (this is the only copy on the mmap path).
|
||||
// TODO: fix this extra memcpy in the mmap case
|
||||
if (result.data() != buf) {
|
||||
memcpy(buf, result.data(), record_size);
|
||||
}
|
||||
|
||||
const char* record = buf;
|
||||
const CompressionType compression =
|
||||
static_cast<CompressionType>(record[payload_size]);
|
||||
if (compression != expected_compression) {
|
||||
return Status::Corruption(
|
||||
"Blob record compression does not match blob index");
|
||||
}
|
||||
|
||||
if (read_options.verify_checksums) {
|
||||
uint32_t stored = DecodeFixed32(record + payload_size + 1);
|
||||
stored -= ChecksumModifierForContext(base_context_checksum, record_offset);
|
||||
const uint32_t computed = ComputeBuiltinChecksumWithLastByte(
|
||||
checksum_type, record, payload_size, record[payload_size]);
|
||||
if (stored != computed) {
|
||||
return Status::Corruption("Blob record checksum mismatch in " +
|
||||
file->file_name() + " offset " +
|
||||
std::to_string(record_offset) + " size " +
|
||||
std::to_string(payload_size));
|
||||
}
|
||||
}
|
||||
|
||||
if (compression != kNoCompression) {
|
||||
return Status::Corruption("Blob record compression is not supported");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
IOStatus WriteSimpleGen2BlobRecord(WritableFileWriter* file,
|
||||
const WriteOptions& write_options,
|
||||
ChecksumType checksum_type,
|
||||
uint32_t base_context_checksum,
|
||||
uint64_t record_offset, const Slice& payload,
|
||||
CompressionType compression) {
|
||||
assert(file != nullptr);
|
||||
// Placeholder for future embedded blob compression support; only
|
||||
// uncompressed payloads are currently written.
|
||||
assert(compression == kNoCompression);
|
||||
|
||||
std::array<char, kSimpleGen2BlobTrailerSize> trailer;
|
||||
trailer[0] = lossless_cast<char>(compression);
|
||||
uint32_t checksum = ComputeBuiltinChecksumWithLastByte(
|
||||
checksum_type, payload.data(), payload.size(), /*last_byte=*/trailer[0]);
|
||||
checksum += ChecksumModifierForContext(base_context_checksum, record_offset);
|
||||
EncodeFixed32(trailer.data() + 1, checksum);
|
||||
|
||||
IOOptions opts;
|
||||
IOStatus io_s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
if (!payload.empty()) {
|
||||
io_s = file->Append(opts, payload);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
}
|
||||
return file->Append(opts, Slice(trailer.data(), trailer.size()));
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1,92 +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 <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "cache/cache_key.h"
|
||||
#include "rocksdb/compression_type.h"
|
||||
#include "rocksdb/io_status.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
struct ReadOptions;
|
||||
struct WriteOptions;
|
||||
class RandomAccessFileReader;
|
||||
class WritableFileWriter;
|
||||
class Slice;
|
||||
enum ChecksumType : char;
|
||||
|
||||
// "SimpleGen2Blob" is the second-generation on-disk format for a blob payload
|
||||
// referenced by a same-file / context-relative blob index. A record is just the
|
||||
// payload bytes followed by a small block-style trailer:
|
||||
//
|
||||
// <payload bytes> <1-byte compression marker> <4-byte checksum>
|
||||
//
|
||||
// The checksum is a builtin block checksum over (payload + compression marker),
|
||||
// context-modified by the record's absolute file offset (see
|
||||
// ChecksumModifierForContext / ComputeBuiltinChecksumWithLastByte in
|
||||
// table/format.h) so that an identical payload at a different offset gets a
|
||||
// different stored checksum. Payloads are currently uncompressed only.
|
||||
//
|
||||
// This format is deliberately decoupled from any particular file type: the same
|
||||
// record shape is read out of SST files (embedded blobs) and is intended to be
|
||||
// reused by other blob-bearing files. The only file-type-specific inputs are
|
||||
// the byte source (a RandomAccessFileReader) and the checksum context (checksum
|
||||
// type + base context checksum), both passed in by the caller.
|
||||
inline constexpr size_t kSimpleGen2BlobTrailerSize = 5;
|
||||
|
||||
// Cache key for the SimpleGen2Blob record at byte `record_offset` within a file
|
||||
// whose base cache key is `base_cache_key`. Delegates to the shared
|
||||
// OffsetableCacheKey::WithOffsetForMinSizeRecord scheme -- the exact same
|
||||
// scheme block-based SST data blocks use via BlockBasedTable::GetCacheKey --
|
||||
// which is valid because a SimpleGen2Blob record always carries a >= 5-byte
|
||||
// trailer. As a result the cache key is collision-free with the file's data
|
||||
// blocks even when the blob cache and block cache are the same cache, with no
|
||||
// separately maintained algorithm to drift out of sync.
|
||||
inline CacheKey GetSimpleGen2BlobCacheKey(
|
||||
const OffsetableCacheKey& base_cache_key, uint64_t record_offset) {
|
||||
return base_cache_key.WithOffsetForMinSizeRecord(record_offset);
|
||||
}
|
||||
|
||||
// Reads a SimpleGen2Blob record of `record_size` bytes (which must equal
|
||||
// `payload_size + kSimpleGen2BlobTrailerSize`) from `file` at `record_offset`
|
||||
// into `buf` (capacity >= record_size) and verifies its compression marker and,
|
||||
// when read_options.verify_checksums is set, its context-modified checksum.
|
||||
//
|
||||
// `checksum_type` and `base_context_checksum` are the file's checksum context
|
||||
// (e.g. from the SST footer). `expected_compression` is the compression type
|
||||
// the caller expects the record to carry (from the blob index); it must match
|
||||
// the record's marker, and currently must be kNoCompression.
|
||||
//
|
||||
// On success, buf[0, payload_size) holds the verified, uncompressed payload
|
||||
// (the trailer remains at the tail of `buf` and can be ignored).
|
||||
Status ReadAndVerifySimpleGen2BlobRecord(
|
||||
const ReadOptions& read_options, RandomAccessFileReader* file,
|
||||
uint64_t record_offset, size_t payload_size, size_t record_size,
|
||||
ChecksumType checksum_type, uint32_t base_context_checksum,
|
||||
CompressionType expected_compression, char* buf);
|
||||
|
||||
// Writes a SimpleGen2Blob record for `payload` at the current end of `file`,
|
||||
// which the caller asserts is byte offset `record_offset`. Appends the payload
|
||||
// bytes followed by the 5-byte trailer (compression marker + context-modified
|
||||
// builtin checksum), mirroring ReadAndVerifySimpleGen2BlobRecord so the on-disk
|
||||
// record format lives in one module.
|
||||
//
|
||||
// `checksum_type` and `base_context_checksum` are the file's checksum context
|
||||
// (e.g. from the SST footer). `compression` is the payload's compression type,
|
||||
// which currently must be kNoCompression.
|
||||
IOStatus WriteSimpleGen2BlobRecord(WritableFileWriter* file,
|
||||
const WriteOptions& write_options,
|
||||
ChecksumType checksum_type,
|
||||
uint32_t base_context_checksum,
|
||||
uint64_t record_offset, const Slice& payload,
|
||||
CompressionType compression);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+2
-16
@@ -7,7 +7,6 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_constants.h"
|
||||
#include "rocksdb/compression_type.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/compression.h"
|
||||
@@ -58,14 +57,6 @@ class BlobIndex {
|
||||
|
||||
bool IsInlined() const { return type_ == Type::kInlinedTTL; }
|
||||
|
||||
// True for a blob record stored in the same physical file as the table entry.
|
||||
// Only embedded-blob SST reader/writer paths should interpret file number
|
||||
// zero this way; generic blob metadata treats it as invalid.
|
||||
bool IsSameFile() const {
|
||||
return (type_ == Type::kBlob || type_ == Type::kBlobTTL) &&
|
||||
file_number_ == kCurrentFileBlobIndexFileNumber;
|
||||
}
|
||||
|
||||
bool HasTTL() const {
|
||||
return type_ == Type::kInlinedTTL || type_ == Type::kBlobTTL;
|
||||
}
|
||||
@@ -134,13 +125,8 @@ class BlobIndex {
|
||||
if (IsInlined()) {
|
||||
oss << "[inlined blob] value:" << value_.ToString(output_hex);
|
||||
} else {
|
||||
oss << "[blob ref] file:";
|
||||
if (IsSameFile()) {
|
||||
oss << "same";
|
||||
} else {
|
||||
oss << file_number_;
|
||||
}
|
||||
oss << " offset:" << offset_ << " size:" << size_
|
||||
oss << "[blob ref] file:" << file_number_ << " offset:" << offset_
|
||||
<< " size:" << size_
|
||||
<< " compression: " << CompressionTypeToString(compression_);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,7 @@
|
||||
#include "cache/charged_cache.h"
|
||||
#include "db/blob/blob_contents.h"
|
||||
#include "db/blob/blob_file_reader.h"
|
||||
#include "db/blob/blob_gen2_format.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "file/random_access_file_reader.h"
|
||||
#include "memory/memory_allocator_impl.h"
|
||||
#include "monitoring/statistics_impl.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "table/get_context.h"
|
||||
@@ -308,96 +305,6 @@ Status BlobSource::GetBlob(const ReadOptions& read_options,
|
||||
return s;
|
||||
}
|
||||
|
||||
Status BlobSource::GetSimpleGen2Blob(
|
||||
const ReadOptions& read_options, const OffsetableCacheKey& base_cache_key,
|
||||
RandomAccessFileReader* file, uint64_t record_offset, uint64_t payload_size,
|
||||
ChecksumType checksum_type, uint32_t base_context_checksum,
|
||||
CompressionType expected_compression, PinnableSlice* value,
|
||||
uint64_t* bytes_read) {
|
||||
assert(value);
|
||||
assert(file);
|
||||
|
||||
const uint64_t record_size = payload_size + kSimpleGen2BlobTrailerSize;
|
||||
|
||||
// The cache key is derived from the SimpleGen2Blob format (shared scheme with
|
||||
// block-based SST blocks); see GetSimpleGen2BlobCacheKey.
|
||||
const CacheKey cache_key =
|
||||
GetSimpleGen2BlobCacheKey(base_cache_key, record_offset);
|
||||
|
||||
Status s;
|
||||
|
||||
CacheHandleGuard<BlobContents> blob_handle;
|
||||
|
||||
// First, try to get the blob from the cache.
|
||||
if (blob_cache_) {
|
||||
Slice key = cache_key.AsSlice();
|
||||
s = GetBlobFromCache(key, &blob_handle);
|
||||
if (s.ok()) {
|
||||
PinCachedBlob(&blob_handle, value);
|
||||
|
||||
// For consistency, the on-disk record size is assigned to bytes_read on
|
||||
// both cache hits and misses.
|
||||
if (bytes_read) {
|
||||
*bytes_read = record_size;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
assert(blob_handle.IsEmpty());
|
||||
|
||||
const bool no_io = read_options.read_tier == kBlockCacheTier;
|
||||
if (no_io) {
|
||||
return Status::Incomplete("Cannot read blob(s): no disk I/O allowed");
|
||||
}
|
||||
|
||||
// Cache miss (or no cache configured). Read the record into a buffer
|
||||
// allocated from the blob cache's memory allocator when we intend to insert
|
||||
// it, exposing the uncompressed payload as BlobContents (the trailer just
|
||||
// sits unused at the tail of the buffer).
|
||||
MemoryAllocator* const allocator = (blob_cache_ && read_options.fill_cache)
|
||||
? blob_cache_.get()->memory_allocator()
|
||||
: nullptr;
|
||||
|
||||
CacheAllocationPtr buf =
|
||||
AllocateBlock(static_cast<size_t>(record_size), allocator);
|
||||
s = ReadAndVerifySimpleGen2BlobRecord(
|
||||
read_options, file, record_offset, static_cast<size_t>(payload_size),
|
||||
static_cast<size_t>(record_size), checksum_type, base_context_checksum,
|
||||
expected_compression, buf.get());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
std::unique_ptr<BlobContents> blob_contents(
|
||||
new BlobContents(std::move(buf), static_cast<size_t>(payload_size)));
|
||||
|
||||
// Record the per-read statistics (mirrors BlobFileReader::GetBlob).
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_BYTES_READ, record_size);
|
||||
PERF_COUNTER_ADD(blob_read_count, 1);
|
||||
PERF_COUNTER_ADD(blob_read_byte, record_size);
|
||||
if (bytes_read) {
|
||||
*bytes_read = record_size;
|
||||
}
|
||||
|
||||
if (blob_cache_ && read_options.fill_cache) {
|
||||
// If filling cache is allowed and a cache is configured, try to put the
|
||||
// blob into the cache.
|
||||
Slice key = cache_key.AsSlice();
|
||||
s = PutBlobIntoCache(key, &blob_contents, &blob_handle);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
PinCachedBlob(&blob_handle, value);
|
||||
} else {
|
||||
PinOwnedBlob(&blob_contents, value);
|
||||
}
|
||||
|
||||
assert(s.ok());
|
||||
return s;
|
||||
}
|
||||
|
||||
void BlobSource::MultiGetBlob(const ReadOptions& read_options,
|
||||
autovector<BlobFileReadRequests>& blob_reqs,
|
||||
uint64_t* bytes_read) {
|
||||
|
||||
@@ -25,8 +25,6 @@ struct MutableCFOptions;
|
||||
class Status;
|
||||
class FilePrefetchBuffer;
|
||||
class Slice;
|
||||
class RandomAccessFileReader;
|
||||
enum ChecksumType : char;
|
||||
|
||||
// BlobSource is a class that provides universal access to blobs, regardless of
|
||||
// whether they are in the blob cache, secondary cache, or (remote) storage.
|
||||
@@ -61,41 +59,6 @@ class BlobSource {
|
||||
FilePrefetchBuffer* prefetch_buffer, PinnableSlice* value,
|
||||
uint64_t* bytes_read);
|
||||
|
||||
// Reads a SimpleGen2Blob payload (see db/blob/blob_gen2_format.h) through the
|
||||
// blob value cache and BLOB_DB_* statistics. This is the counterpart to
|
||||
// GetBlob() for the second-generation blob record format, which is read
|
||||
// directly from a RandomAccessFileReader rather than from a traditional blob
|
||||
// file.
|
||||
//
|
||||
// The cache key is derived from the SimpleGen2Blob format itself, not chosen
|
||||
// by the caller: it is GetSimpleGen2BlobCacheKey(base_cache_key,
|
||||
// record_offset), the same offset scheme block-based SST blocks use. The
|
||||
// caller supplies only its file's `base_cache_key` (db_id / db_session_id /
|
||||
// file_number). This keeps blob records collision-free with the file's data
|
||||
// blocks even when the blob cache and block cache are the same cache.
|
||||
//
|
||||
// `file`, `record_offset`, `payload_size`, `checksum_type`,
|
||||
// `base_context_checksum`, and `expected_compression` are the inputs to the
|
||||
// SimpleGen2Blob reader used on a cache miss (see
|
||||
// ReadAndVerifySimpleGen2BlobRecord). The on-disk record size (payload +
|
||||
// trailer) is reported via `*bytes_read` (when non-null) and the
|
||||
// BLOB_DB_BLOB_FILE_BYTES_READ / blob_read_byte counters, consistently on
|
||||
// both cache hits and misses.
|
||||
//
|
||||
// On a cache hit, pins the cached value into `*value` (no copy). On a miss,
|
||||
// reads + verifies the record into a cache-allocator buffer, records the
|
||||
// per-read stats, inserts it into the cache (when configured and fill_cache
|
||||
// is set), and pins it into `*value`. If blob_cache_ is not configured, the
|
||||
// record is still read and read stats recorded, just without a cache
|
||||
// lookup/insert.
|
||||
Status GetSimpleGen2Blob(const ReadOptions& read_options,
|
||||
const OffsetableCacheKey& base_cache_key,
|
||||
RandomAccessFileReader* file, uint64_t record_offset,
|
||||
uint64_t payload_size, ChecksumType checksum_type,
|
||||
uint32_t base_context_checksum,
|
||||
CompressionType expected_compression,
|
||||
PinnableSlice* value, uint64_t* bytes_read);
|
||||
|
||||
// Read multiple blobs from the underlying cache or blob file(s).
|
||||
//
|
||||
// If successful, returns ok and sets "result" in the elements of "blob_reqs"
|
||||
|
||||
@@ -25,9 +25,6 @@
|
||||
#include "file/filename.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "util/string_util.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
@@ -44,18 +41,6 @@ void CorruptPinnedBlobIndexOnCleanup(void* arg1, void* /*arg2*/) {
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(BlobIndexTest, SameFileBlobIndex) {
|
||||
std::string encoded;
|
||||
BlobIndex::EncodeBlob(&encoded, kCurrentFileBlobIndexFileNumber,
|
||||
/*offset=*/123, /*size=*/456, kNoCompression);
|
||||
|
||||
BlobIndex blob_index;
|
||||
ASSERT_OK(blob_index.DecodeFrom(encoded));
|
||||
EXPECT_TRUE(blob_index.IsSameFile());
|
||||
EXPECT_EQ(blob_index.file_number(), kCurrentFileBlobIndexFileNumber);
|
||||
EXPECT_NE(blob_index.DebugString(false).find("file:same"), std::string::npos);
|
||||
}
|
||||
|
||||
// kTypeBlobIndex is a value type used by BlobDB only. The base rocksdb
|
||||
// should accept the value type on write, and report not supported value
|
||||
// for reads, unless caller request for it explicitly. The base rocksdb
|
||||
@@ -345,436 +330,6 @@ TEST_F(DBBlobIndexTest, ReadOnlyGetImplReturnsBlobIndexWhenRequested) {
|
||||
ASSERT_TRUE(is_blob_index);
|
||||
}
|
||||
|
||||
// Regression test for a same-file (embedded) BlobIndex being exposed
|
||||
// unresolved through an iterator. With index_type=kBinarySearchWithFirstKey
|
||||
// and allow_unprepared_value (the default for DB iterators), the block-based
|
||||
// table iterator can sit in the is_at_first_key_from_index_ state and return
|
||||
// the raw kTypeBlobIndex internal key from the index, while value() resolves
|
||||
// the same-file blob to its plain payload. DBIter then reads the stale
|
||||
// kTypeBlobIndex type and routes the already-resolved plain value through the
|
||||
// blob-index path, corrupting/misreading it.
|
||||
//
|
||||
// Forcing one entry per data block makes every embedded blob the first key of
|
||||
// a block, reliably triggering the deferred-first-key state on both SeekToFirst
|
||||
// and forward block transitions.
|
||||
TEST_F(DBBlobIndexTest, EmbeddedBlobIteratorWithFirstKeyIndex) {
|
||||
Options options = GetTestOptions();
|
||||
options.create_if_missing = true;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.index_type =
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey;
|
||||
// Soft limit of 1 byte starts a new data block after every entry.
|
||||
bbto.block_size = 1;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Build an embedded-blob SST whose large values are stored as same-file blob
|
||||
// records (table entries become same-file BlobIndex references).
|
||||
const std::string sst_path = dbname_ + "/embedded_first_key.sst";
|
||||
SstFileWriterEmbeddedBlobOptions embedded_blob_options;
|
||||
embedded_blob_options.min_blob_size = 8;
|
||||
|
||||
const std::string big1(1024, 'x');
|
||||
const std::string big2(1024, 'y');
|
||||
const std::string big3(1024, 'z');
|
||||
|
||||
SstFileWriter writer(EnvOptions(), options);
|
||||
ASSERT_OK(writer.OpenWithEmbeddedBlobs(sst_path, embedded_blob_options));
|
||||
ASSERT_OK(writer.Put("k1", big1));
|
||||
ASSERT_OK(writer.Put("k2", big2));
|
||||
ASSERT_OK(writer.Put("k3", big3));
|
||||
ASSERT_OK(writer.Finish());
|
||||
|
||||
ASSERT_OK(db_->IngestExternalFile({sst_path}, IngestExternalFileOptions()));
|
||||
|
||||
// Point lookups resolve same-file blobs before exposing them, so Get works.
|
||||
ASSERT_EQ(Get("k1"), big1);
|
||||
ASSERT_EQ(Get("k2"), big2);
|
||||
ASSERT_EQ(Get("k3"), big3);
|
||||
|
||||
// Iterator path: the bug manifests here.
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_EQ(iter->key(), "k1");
|
||||
EXPECT_EQ(iter->value(), big1);
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_EQ(iter->key(), "k2");
|
||||
EXPECT_EQ(iter->value(), big2);
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_EQ(iter->key(), "k3");
|
||||
EXPECT_EQ(iter->value(), big3);
|
||||
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
// Seek directly to a block whose first key is an embedded blob.
|
||||
iter->Seek("k2");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_EQ(iter->key(), "k2");
|
||||
EXPECT_EQ(iter->value(), big2);
|
||||
}
|
||||
|
||||
// Backward iteration (Prev / SeekForPrev) over an embedded-blob SST must work,
|
||||
// including for wide-column entities whose same-file blob columns are resolved
|
||||
// into an iterator-owned buffer. DBIter requires the underlying iterator's
|
||||
// value to be pinned for backward iteration; EmbeddedBlobResolvingIterator
|
||||
// therefore pins resolved values -- both whole-value blob payloads and rebuilt
|
||||
// wide-column values -- and hands their cleanup to the PinnedIteratorsManager
|
||||
// across repositioning. Before that, backward iteration over the wide-column
|
||||
// entity returned NotSupported (or tripped the IsValuePinned() assertion).
|
||||
TEST_F(DBBlobIndexTest, EmbeddedBlobBackwardIteration) {
|
||||
Options options = GetTestOptions();
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const std::string sst_path = dbname_ + "/embedded_backward.sst";
|
||||
SstFileWriterEmbeddedBlobOptions embedded_blob_options;
|
||||
embedded_blob_options.min_blob_size = 8;
|
||||
|
||||
// Whole-value same-file blobs.
|
||||
const std::string big1(1024, 'x');
|
||||
const std::string big3(1024, 'z');
|
||||
// Wide-column entity: a small (inline) default column plus a large
|
||||
// (embedded, same-file blob) non-default column -- the mixed wide-column
|
||||
// path whose resolved value lands in an iterator-owned buffer.
|
||||
const std::string k2_default(2, 'd');
|
||||
const std::string k2_big_col(1024, 'c');
|
||||
const WideColumns k2_columns{{kDefaultWideColumnName, k2_default},
|
||||
{"big", k2_big_col}};
|
||||
|
||||
SstFileWriter writer(EnvOptions(), options);
|
||||
ASSERT_OK(writer.OpenWithEmbeddedBlobs(sst_path, embedded_blob_options));
|
||||
ASSERT_OK(writer.Put("k1", big1));
|
||||
ASSERT_OK(writer.PutEntity("k2", k2_columns));
|
||||
ASSERT_OK(writer.Put("k3", big3));
|
||||
ASSERT_OK(writer.Finish());
|
||||
|
||||
ASSERT_OK(db_->IngestExternalFile({sst_path}, IngestExternalFileOptions()));
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
|
||||
// Backward scan via SeekToLast + Prev.
|
||||
iter->SeekToLast();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_EQ(iter->key(), "k3");
|
||||
EXPECT_EQ(iter->value(), big3);
|
||||
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_EQ(iter->key(), "k2");
|
||||
EXPECT_EQ(iter->value(), k2_default);
|
||||
EXPECT_EQ(iter->columns(), k2_columns);
|
||||
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_EQ(iter->key(), "k1");
|
||||
EXPECT_EQ(iter->value(), big1);
|
||||
|
||||
iter->Prev();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
// SeekForPrev directly onto the wide-column entity, then step back.
|
||||
iter->SeekForPrev("k2");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_EQ(iter->key(), "k2");
|
||||
EXPECT_EQ(iter->value(), k2_default);
|
||||
EXPECT_EQ(iter->columns(), k2_columns);
|
||||
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_EQ(iter->key(), "k1");
|
||||
EXPECT_EQ(iter->value(), big1);
|
||||
}
|
||||
|
||||
// Regression test for T277566778. If resolving a same-file ("embedded") blob
|
||||
// fails during compaction (e.g. a blob-region read fault), the error must be
|
||||
// surfaced as-is. It must NOT be masked by feeding the raw, unresolved
|
||||
// same-file BlobIndex downstream, where FileMetaData::UpdateBoundaries would
|
||||
// (correctly) reject file number 0 and report a misleading "Invalid blob file
|
||||
// number" corruption. The EmbeddedBlobResolvingIterator must not expose an
|
||||
// unresolved same-file value on error: for eager (compaction) callers it
|
||||
// resolves during positioning so the error is visible via status()/Valid()
|
||||
// before value().
|
||||
TEST_F(DBBlobIndexTest, EmbeddedBlobResolveErrorDuringCompactionNotMasked) {
|
||||
Options options = GetTestOptions();
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const std::string sst_path = dbname_ + "/embedded_resolve_err.sst";
|
||||
SstFileWriterEmbeddedBlobOptions embedded_blob_options;
|
||||
embedded_blob_options.min_blob_size = 8;
|
||||
|
||||
// Wide-column entity with a large (embedded, same-file blob) column: the
|
||||
// mixed path whose resolution reads the blob region during compaction.
|
||||
const std::string big_col(1024, 'c');
|
||||
const WideColumns columns{{kDefaultWideColumnName, "d"}, {"big", big_col}};
|
||||
|
||||
SstFileWriter writer(EnvOptions(), options);
|
||||
ASSERT_OK(writer.OpenWithEmbeddedBlobs(sst_path, embedded_blob_options));
|
||||
ASSERT_OK(writer.PutEntity("k2", columns));
|
||||
ASSERT_OK(writer.Finish());
|
||||
ASSERT_OK(db_->IngestExternalFile({sst_path}, IngestExternalFileOptions()));
|
||||
|
||||
// Add keys straddling the entity so a real compaction (not a trivial move)
|
||||
// reads and resolves the embedded entity.
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Put("k3", "v3"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Inject a resolution failure only during the compaction below, simulating a
|
||||
// blob-region read fault.
|
||||
const Status kInjected = Status::IOError("injected embedded blob read error");
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTable::MaybeResolveEmbeddedValue:InjectError",
|
||||
[&](void* arg) { *static_cast<Status*>(arg) = kInjected; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
const Status s = db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
// The compaction must fail with the injected read error, NOT a masked
|
||||
// "Invalid blob file number" corruption.
|
||||
ASSERT_NOK(s);
|
||||
EXPECT_TRUE(s.IsIOError()) << s.ToString();
|
||||
EXPECT_EQ(s.ToString().find("Invalid blob file number"), std::string::npos)
|
||||
<< s.ToString();
|
||||
}
|
||||
|
||||
// Regression test for T277310719, the whole-value (kTypeBlobIndex) sibling of
|
||||
// the wide-column-entity case above (T277566778). Both share one root cause: on
|
||||
// a blob-region resolution error during compaction, the
|
||||
// EmbeddedBlobResolvingIterator must not fall back to exposing the raw,
|
||||
// unresolved same-file value.
|
||||
//
|
||||
// The whole-value variant is more dangerous than the entity variant because it
|
||||
// escapes the FileMetaData::UpdateBoundaries tripwire: ResolveKeyType()
|
||||
// rewrites the key type kTypeBlobIndex -> kTypeValue (no I/O, always succeeds),
|
||||
// so on a masked resolution error the emitted entry is {kTypeValue, raw
|
||||
// BlobIndex bytes}. UpdateBoundaries only scans kTypeBlobIndex/entity types, so
|
||||
// unlike the entity variant it does NOT reject it -- the masked error slips
|
||||
// through, a corrupt {kTypeValue, BlobIndex} record is written to the
|
||||
// compaction output, and a later point lookup reads those raw BlobIndex bytes
|
||||
// back as the value (in db_stress this surfaced as the
|
||||
// ExpectedValue::IsValueBaseValid assertion, not "Invalid blob file number").
|
||||
// The eager (compaction) resolver must surface the error via status()/Valid()
|
||||
// before value() is consumed.
|
||||
TEST_F(DBBlobIndexTest,
|
||||
EmbeddedBlobResolveErrorWholeValueDuringCompactionNotMasked) {
|
||||
Options options = GetTestOptions();
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const std::string sst_path = dbname_ + "/embedded_resolve_err_wholevalue.sst";
|
||||
SstFileWriterEmbeddedBlobOptions embedded_blob_options;
|
||||
embedded_blob_options.min_blob_size = 8;
|
||||
|
||||
// A large whole value is stored as a same-file blob, so the table entry
|
||||
// becomes a same-file (file number 0) BlobIndex (kTypeBlobIndex) -- the
|
||||
// whole-value path whose resolution reads the blob region during compaction.
|
||||
const std::string big_val(1024, 'b');
|
||||
|
||||
SstFileWriter writer(EnvOptions(), options);
|
||||
ASSERT_OK(writer.OpenWithEmbeddedBlobs(sst_path, embedded_blob_options));
|
||||
ASSERT_OK(writer.Put("k2", big_val));
|
||||
ASSERT_OK(writer.Finish());
|
||||
ASSERT_OK(db_->IngestExternalFile({sst_path}, IngestExternalFileOptions()));
|
||||
|
||||
// Add keys straddling the embedded blob so a real compaction (not a trivial
|
||||
// move) reads and resolves it.
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Put("k3", "v3"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Inject a resolution failure only during the compaction below, simulating a
|
||||
// blob-region read fault.
|
||||
const Status kInjected = Status::IOError("injected embedded blob read error");
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTable::MaybeResolveEmbeddedValue:InjectError",
|
||||
[&](void* arg) { *static_cast<Status*>(arg) = kInjected; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
const Status s = db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
// The compaction must fail with the injected read error. Before the fix it
|
||||
// instead succeeded (masking the error) and persisted a {kTypeValue, raw
|
||||
// BlobIndex} record; it must not do that, and it must not report a misleading
|
||||
// "Invalid blob file number" corruption either.
|
||||
ASSERT_NOK(s);
|
||||
EXPECT_TRUE(s.IsIOError()) << s.ToString();
|
||||
EXPECT_EQ(s.ToString().find("Invalid blob file number"), std::string::npos)
|
||||
<< s.ToString();
|
||||
}
|
||||
|
||||
// When a blob cache is configured, same-file ("embedded") blob reads should go
|
||||
// through BlobSource: the first read misses + inserts + does a disk read, and
|
||||
// the second read of the same blob is served from the blob cache. This holds on
|
||||
// both the Get() and the iterator paths.
|
||||
TEST_F(DBBlobIndexTest, EmbeddedBlobBlobCacheHitMiss) {
|
||||
Options options = GetTestOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
LRUCacheOptions co;
|
||||
co.capacity = 8 << 20;
|
||||
options.blob_cache = NewLRUCache(co);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const std::string sst_path = dbname_ + "/embedded_cache.sst";
|
||||
SstFileWriterEmbeddedBlobOptions embedded_blob_options;
|
||||
embedded_blob_options.min_blob_size = 8;
|
||||
|
||||
const std::string big1(1024, 'x');
|
||||
const std::string big2(1024, 'y');
|
||||
|
||||
SstFileWriter writer(EnvOptions(), options);
|
||||
ASSERT_OK(writer.OpenWithEmbeddedBlobs(sst_path, embedded_blob_options));
|
||||
ASSERT_OK(writer.Put("k1", big1));
|
||||
ASSERT_OK(writer.Put("k2", big2));
|
||||
ASSERT_OK(writer.Finish());
|
||||
|
||||
ASSERT_OK(db_->IngestExternalFile({sst_path}, IngestExternalFileOptions()));
|
||||
|
||||
Statistics* const stats = options.statistics.get();
|
||||
|
||||
// First Get: blob cache miss -> disk read -> cache insert.
|
||||
ASSERT_OK(stats->Reset());
|
||||
get_perf_context()->Reset();
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_EQ(Get("k1"), big1);
|
||||
EXPECT_EQ(stats->getTickerCount(BLOB_DB_CACHE_MISS), 1);
|
||||
EXPECT_EQ(stats->getTickerCount(BLOB_DB_CACHE_HIT), 0);
|
||||
EXPECT_EQ(stats->getTickerCount(BLOB_DB_CACHE_ADD), 1);
|
||||
EXPECT_GT(stats->getTickerCount(BLOB_DB_BLOB_FILE_BYTES_READ), 0);
|
||||
EXPECT_EQ(get_perf_context()->blob_read_count, 1);
|
||||
|
||||
// Second Get of the same key: blob cache hit, no disk read, no insert.
|
||||
ASSERT_OK(stats->Reset());
|
||||
get_perf_context()->Reset();
|
||||
ASSERT_EQ(Get("k1"), big1);
|
||||
EXPECT_EQ(stats->getTickerCount(BLOB_DB_CACHE_HIT), 1);
|
||||
EXPECT_EQ(stats->getTickerCount(BLOB_DB_CACHE_MISS), 0);
|
||||
EXPECT_EQ(stats->getTickerCount(BLOB_DB_CACHE_ADD), 0);
|
||||
EXPECT_EQ(stats->getTickerCount(BLOB_DB_BLOB_FILE_BYTES_READ), 0);
|
||||
EXPECT_EQ(get_perf_context()->blob_read_count, 0);
|
||||
EXPECT_EQ(get_perf_context()->blob_cache_hit_count, 1);
|
||||
|
||||
// Iterator path: first read of a fresh key misses + inserts.
|
||||
ASSERT_OK(stats->Reset());
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
iter->Seek("k2");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_EQ(iter->value(), big2);
|
||||
}
|
||||
EXPECT_EQ(stats->getTickerCount(BLOB_DB_CACHE_MISS), 1);
|
||||
EXPECT_EQ(stats->getTickerCount(BLOB_DB_CACHE_ADD), 1);
|
||||
|
||||
// Iterator path: second read of the same key hits the blob cache.
|
||||
ASSERT_OK(stats->Reset());
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
iter->Seek("k2");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_EQ(iter->value(), big2);
|
||||
}
|
||||
EXPECT_EQ(stats->getTickerCount(BLOB_DB_CACHE_HIT), 1);
|
||||
EXPECT_EQ(stats->getTickerCount(BLOB_DB_CACHE_ADD), 0);
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
}
|
||||
|
||||
// With the blob cache and block cache pointing at the same Cache, the SST-like
|
||||
// cache-key scheme (offset >> 2 on the SST's base key) keeps embedded blob
|
||||
// records disjoint from the SST's data blocks. Reading embedded blobs must not
|
||||
// alias or evict data blocks, and values must remain correct.
|
||||
TEST_F(DBBlobIndexTest, EmbeddedBlobSharedBlockAndBlobCache) {
|
||||
Options options = GetTestOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
LRUCacheOptions co;
|
||||
co.capacity = 16 << 20;
|
||||
auto shared_cache = NewLRUCache(co);
|
||||
options.blob_cache = shared_cache;
|
||||
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_cache = shared_cache;
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const std::string sst_path = dbname_ + "/embedded_shared.sst";
|
||||
SstFileWriterEmbeddedBlobOptions embedded_blob_options;
|
||||
embedded_blob_options.min_blob_size = 8;
|
||||
|
||||
const std::string big1(1024, 'x');
|
||||
const std::string big2(1024, 'y');
|
||||
|
||||
SstFileWriter writer(EnvOptions(), options);
|
||||
ASSERT_OK(writer.OpenWithEmbeddedBlobs(sst_path, embedded_blob_options));
|
||||
ASSERT_OK(writer.Put("k1", big1));
|
||||
ASSERT_OK(writer.Put("k2", big2));
|
||||
ASSERT_OK(writer.Finish());
|
||||
|
||||
ASSERT_OK(db_->IngestExternalFile({sst_path}, IngestExternalFileOptions()));
|
||||
|
||||
Statistics* const stats = options.statistics.get();
|
||||
|
||||
// Values are correct with a shared cache, and re-reads hit both the block
|
||||
// cache (data blocks) and the blob cache (embedded payloads) independently.
|
||||
ASSERT_EQ(Get("k1"), big1);
|
||||
ASSERT_EQ(Get("k2"), big2);
|
||||
ASSERT_EQ(Get("k1"), big1);
|
||||
ASSERT_EQ(Get("k2"), big2);
|
||||
EXPECT_GT(stats->getTickerCount(BLOB_DB_CACHE_HIT), 0);
|
||||
EXPECT_GT(stats->getTickerCount(BLOCK_CACHE_HIT), 0);
|
||||
|
||||
// Iterator yields correct values too.
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_EQ(iter->key(), "k1");
|
||||
EXPECT_EQ(iter->value(), big1);
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_EQ(iter->key(), "k2");
|
||||
EXPECT_EQ(iter->value(), big2);
|
||||
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
class PlainBlobValueFilterV3 : public CompactionFilter {
|
||||
public:
|
||||
PlainBlobValueFilterV3(std::atomic<int>* filter_call_count,
|
||||
|
||||
+4
-1757
File diff suppressed because it is too large.
Load diff
@@ -678,9 +678,6 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
internal_stats_->GetBlobFileReadHist(), io_tracer));
|
||||
blob_source_.reset(new BlobSource(ioptions_, mutable_cf_options_, db_id,
|
||||
db_session_id, blob_file_cache_.get()));
|
||||
// Let the table cache route same-file ("embedded") blob reads through the
|
||||
// blob value cache + stats. Both objects share this CFD's lifetime.
|
||||
table_cache_->SetBlobSource(blob_source_.get());
|
||||
|
||||
if (ioptions_.compaction_style == kCompactionStyleLevel) {
|
||||
compaction_picker_.reset(
|
||||
|
||||
+3
-3
@@ -5459,9 +5459,9 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
std::vector<std::string> cf_names_;
|
||||
};
|
||||
|
||||
class DBBasicTestWithParallelIO : public testing::WithParamInterface<
|
||||
std::tuple<bool, bool, bool, uint32_t>>,
|
||||
public DBBasicTestMultiGet {
|
||||
class DBBasicTestWithParallelIO : public DBBasicTestMultiGet,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<bool, bool, bool, uint32_t>> {
|
||||
public:
|
||||
DBBasicTestWithParallelIO()
|
||||
: DBBasicTestMultiGet("/db_basic_test_with_parallel_io", 1,
|
||||
|
||||
@@ -330,8 +330,6 @@ class DBTestSharedWriteBufferAcrossCFs
|
||||
|
||||
TEST_P(DBTestSharedWriteBufferAcrossCFs, SharedWriteBufferAcrossCFs) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.statistics->set_stats_level(StatsLevel::kAll);
|
||||
options.arena_block_size = 4096;
|
||||
auto flush_listener = std::make_shared<FlushCounterListener>();
|
||||
options.listeners.push_back(flush_listener);
|
||||
@@ -499,29 +497,6 @@ TEST_P(DBTestSharedWriteBufferAcrossCFs, SharedWriteBufferAcrossCFs) {
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_.get(), "nikitich"),
|
||||
static_cast<uint64_t>(2));
|
||||
}
|
||||
const uint64_t wbm_flushes =
|
||||
TestGetTickerCount(options, FLUSH_REASON_WRITE_BUFFER_MANAGER);
|
||||
ASSERT_GT(wbm_flushes, 0);
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, FLUSH_REASON_WRITE_BUFFER_FULL));
|
||||
|
||||
HistogramData all_memtable_memory;
|
||||
options.statistics->histogramData(FLUSH_MEMTABLE_MEMORY_BYTES,
|
||||
&all_memtable_memory);
|
||||
EXPECT_GE(all_memtable_memory.count, wbm_flushes);
|
||||
EXPECT_GT(all_memtable_memory.sum, 0);
|
||||
|
||||
HistogramData all_memtable_data_size;
|
||||
options.statistics->histogramData(FLUSH_MEMTABLE_TOTAL_DATA_SIZE,
|
||||
&all_memtable_data_size);
|
||||
EXPECT_GE(all_memtable_data_size.count, wbm_flushes);
|
||||
EXPECT_GT(all_memtable_data_size.sum, 0);
|
||||
|
||||
HistogramData wbm_memtable_memory;
|
||||
options.statistics->histogramData(
|
||||
FLUSH_WRITE_BUFFER_MANAGER_MEMTABLE_MEMORY_BYTES, &wbm_memtable_memory);
|
||||
EXPECT_EQ(wbm_flushes, wbm_memtable_memory.count);
|
||||
EXPECT_GT(wbm_memtable_memory.sum, 0);
|
||||
|
||||
if (cost_cache_) {
|
||||
ASSERT_GE(cache->GetUsage(), 256 * 1024);
|
||||
Close();
|
||||
|
||||
@@ -473,61 +473,6 @@ TEST_F(DBFlushTest, StatisticsGarbageBasic) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, FlushReasonStatsWriteBufferFull) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.statistics->set_stats_level(StatsLevel::kAll);
|
||||
options.create_if_missing = true;
|
||||
options.compression = kNoCompression;
|
||||
options.disable_auto_compactions = true;
|
||||
options.avoid_flush_during_shutdown = true;
|
||||
options.write_buffer_size = 16 * 1024;
|
||||
options.max_write_buffer_number = 8;
|
||||
|
||||
auto flush_listener = std::make_shared<FlushCounterListener>();
|
||||
flush_listener->expected_flush_reason = FlushReason::kWriteBufferFull;
|
||||
options.listeners.push_back(flush_listener);
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
WriteOptions write_options;
|
||||
write_options.disableWAL = true;
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
ASSERT_OK(Put(Key(i), DummyString(10 * 1024), write_options));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
|
||||
const uint64_t write_buffer_full_flushes =
|
||||
TestGetTickerCount(options, FLUSH_REASON_WRITE_BUFFER_FULL);
|
||||
ASSERT_GT(write_buffer_full_flushes, 0);
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, FLUSH_REASON_WRITE_BUFFER_MANAGER));
|
||||
|
||||
HistogramData all_memtable_memory;
|
||||
options.statistics->histogramData(FLUSH_MEMTABLE_MEMORY_BYTES,
|
||||
&all_memtable_memory);
|
||||
EXPECT_GE(all_memtable_memory.count, write_buffer_full_flushes);
|
||||
EXPECT_GT(all_memtable_memory.sum, 0);
|
||||
|
||||
HistogramData all_memtable_data_size;
|
||||
options.statistics->histogramData(FLUSH_MEMTABLE_TOTAL_DATA_SIZE,
|
||||
&all_memtable_data_size);
|
||||
EXPECT_GE(all_memtable_data_size.count, write_buffer_full_flushes);
|
||||
EXPECT_GT(all_memtable_data_size.sum, 0);
|
||||
|
||||
HistogramData write_buffer_full_memtable_memory;
|
||||
options.statistics->histogramData(
|
||||
FLUSH_WRITE_BUFFER_FULL_MEMTABLE_MEMORY_BYTES,
|
||||
&write_buffer_full_memtable_memory);
|
||||
EXPECT_EQ(write_buffer_full_flushes, write_buffer_full_memtable_memory.count);
|
||||
EXPECT_GT(write_buffer_full_memtable_memory.sum, 0);
|
||||
|
||||
HistogramData wbm_memtable_memory;
|
||||
options.statistics->histogramData(
|
||||
FLUSH_WRITE_BUFFER_MANAGER_MEMTABLE_MEMORY_BYTES, &wbm_memtable_memory);
|
||||
EXPECT_EQ(0, wbm_memtable_memory.count);
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, StatisticsGarbageInsertAndDeletes) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = CreateDBStatistics();
|
||||
@@ -2802,8 +2747,6 @@ TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = GetParam();
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.statistics->set_stats_level(StatsLevel::kAll);
|
||||
options.write_buffer_size = (static_cast<size_t>(64) << 20);
|
||||
auto flush_listener = std::make_shared<FlushCounterListener>();
|
||||
flush_listener->expected_flush_reason = FlushReason::kManualFlush;
|
||||
@@ -2830,16 +2773,6 @@ TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
||||
}
|
||||
ASSERT_OK(Flush(cf_ids));
|
||||
|
||||
EXPECT_EQ(options.atomic_flush ? 1 : 0,
|
||||
TestGetTickerCount(options, ATOMIC_FLUSH_REQUEST_REASON_OTHER));
|
||||
EXPECT_EQ(0, TestGetTickerCount(
|
||||
options, ATOMIC_FLUSH_REQUEST_REASON_WRITE_BUFFER_FULL));
|
||||
EXPECT_EQ(0, TestGetTickerCount(
|
||||
options, ATOMIC_FLUSH_REQUEST_REASON_WRITE_BUFFER_MANAGER));
|
||||
EXPECT_EQ(0, TestGetTickerCount(
|
||||
options,
|
||||
ATOMIC_FLUSH_REQUEST_REASON_MEMTABLE_MAX_RANGE_DELETIONS));
|
||||
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
|
||||
+5
-14
@@ -334,21 +334,12 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
// FetchAddLastAllocatedSequence() before writes complete, but only
|
||||
// published via SetLastSequence() after success. If we're recovering from
|
||||
// an error, there may be allocated-but-not-published sequence numbers.
|
||||
// Start recovery from a published sequence number that covers writers which
|
||||
// were already in flight. Recovery flushes repeat this sync at the actual
|
||||
// memtable-switch fence, after FlushAllColumnFamilies() has dropped and
|
||||
// re-acquired the DB mutex.
|
||||
if (two_write_queues_) {
|
||||
WriteThread::Writer w;
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
WriteThread::Writer nonmem_w;
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
WaitForPendingWrites();
|
||||
|
||||
// We must sync last_sequence_ with last_allocated_sequence_ before creating
|
||||
// any new memtables/WALs, otherwise the new WAL could start with a sequence
|
||||
// number lower than what was already written, causing "sequence number
|
||||
// going backwards" corruption on subsequent recovery.
|
||||
if (immutable_db_options_.two_write_queues) {
|
||||
versions_->SyncLastSequenceWithAllocated();
|
||||
|
||||
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::ResumeImpl:AfterSyncSeq");
|
||||
|
||||
@@ -1215,8 +1215,6 @@ class DBImpl : public DB {
|
||||
|
||||
bool TEST_IsRecoveryInProgress();
|
||||
|
||||
Status TEST_ResumeImpl(DBRecoverContext context);
|
||||
|
||||
// Return the maximum overlapping data (in bytes) at next level for any
|
||||
// file at a level >= 1.
|
||||
uint64_t TEST_MaxNextLevelOverlappingBytes(
|
||||
@@ -2485,10 +2483,6 @@ class DBImpl : public DB {
|
||||
const autovector<ColumnFamilyData*>& provided_candidate_cfds = {},
|
||||
bool entered_write_thread = false);
|
||||
|
||||
// REQUIRES: mutex locked and write queues drained up to the recovery flush
|
||||
// fence that is about to switch memtables.
|
||||
void MaybeSyncLastSequenceWithAllocatedForRecovery(FlushReason flush_reason);
|
||||
|
||||
Status RetryFlushesForErrorRecovery(FlushReason flush_reason, bool wait);
|
||||
|
||||
// Wait until flushing this column family won't stall writes
|
||||
|
||||
@@ -35,32 +35,6 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
|
||||
void RecordAtomicFlushRequestReason(Statistics* stats,
|
||||
FlushReason flush_reason) {
|
||||
// Keep this aligned with the existing rocksdb.flush.reason.* counters: they
|
||||
// cover automatic flush triggers used for write-stall debugging. Other
|
||||
// FlushReason values are counted by the catch-all other ticker.
|
||||
switch (flush_reason) {
|
||||
case FlushReason::kWriteBufferFull:
|
||||
RecordTick(stats, ATOMIC_FLUSH_REQUEST_REASON_WRITE_BUFFER_FULL);
|
||||
break;
|
||||
case FlushReason::kWriteBufferManager:
|
||||
RecordTick(stats, ATOMIC_FLUSH_REQUEST_REASON_WRITE_BUFFER_MANAGER);
|
||||
break;
|
||||
case FlushReason::kMemtableMaxRangeDeletions:
|
||||
RecordTick(stats,
|
||||
ATOMIC_FLUSH_REQUEST_REASON_MEMTABLE_MAX_RANGE_DELETIONS);
|
||||
break;
|
||||
default:
|
||||
RecordTick(stats, ATOMIC_FLUSH_REQUEST_REASON_OTHER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool DBImpl::EnoughRoomForCompaction(
|
||||
ColumnFamilyData* cfd, const std::vector<CompactionInputFiles>& inputs,
|
||||
bool* sfm_reserved_compact_space, LogBuffer* log_buffer) {
|
||||
@@ -2261,8 +2235,6 @@ Status DBImpl::FlushAllColumnFamilies(const FlushOptions& flush_options,
|
||||
Status status;
|
||||
if (immutable_db_options_.atomic_flush || flush_options.force_atomic_flush) {
|
||||
mutex_.Unlock();
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::FlushAllColumnFamilies:BeforeAtomicFlushMemTables");
|
||||
status = AtomicFlushMemTables(flush_options, flush_reason);
|
||||
if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
@@ -2274,7 +2246,6 @@ Status DBImpl::FlushAllColumnFamilies(const FlushOptions& flush_options,
|
||||
continue;
|
||||
}
|
||||
mutex_.Unlock();
|
||||
TEST_SYNC_POINT("DBImpl::FlushAllColumnFamilies:BeforeFlushMemTable");
|
||||
status = FlushMemTable(cfd, flush_options, flush_reason);
|
||||
TEST_SYNC_POINT("DBImpl::FlushAllColumnFamilies:1");
|
||||
TEST_SYNC_POINT("DBImpl::FlushAllColumnFamilies:2");
|
||||
@@ -2629,14 +2600,6 @@ void DBImpl::NotifyOnManualFlushScheduled(autovector<ColumnFamilyData*> cfds,
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::MaybeSyncLastSequenceWithAllocatedForRecovery(
|
||||
FlushReason flush_reason) {
|
||||
mutex_.AssertHeld();
|
||||
if (two_write_queues_ && IsRecoveryFlush(flush_reason)) {
|
||||
versions_->SyncLastSequenceWithAllocated();
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_options,
|
||||
FlushReason flush_reason,
|
||||
@@ -2676,12 +2639,6 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
}
|
||||
WaitForPendingWrites();
|
||||
|
||||
// Recovery may have released `mutex_` after the earlier `ResumeImpl()`
|
||||
// sync. Refresh sequence state at the actual memtable-switch fence, after
|
||||
// both write queues are drained and before `SwitchMemtable()` consumes
|
||||
// `LastSequence()`.
|
||||
MaybeSyncLastSequenceWithAllocatedForRecovery(flush_reason);
|
||||
|
||||
if (!cfd->mem()->IsEmpty() || !cached_recoverable_state_empty_.load() ||
|
||||
IsRecoveryFlush(flush_reason)) {
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
@@ -2871,11 +2828,6 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
}
|
||||
WaitForPendingWrites();
|
||||
|
||||
// Keep atomic recovery flushes consistent with the single-CF path: the
|
||||
// sequence sync must happen after both write queues are drained and before
|
||||
// any recovery memtable switch reads `LastSequence()`.
|
||||
MaybeSyncLastSequenceWithAllocatedForRecovery(flush_reason);
|
||||
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds, candidate_cfds, flush_reason);
|
||||
|
||||
// Unref the newly generated candidate cfds (when not provided) in
|
||||
@@ -3579,7 +3531,6 @@ bool DBImpl::EnqueuePendingFlush(const FlushRequest& flush_req) {
|
||||
}
|
||||
++unscheduled_flushes_;
|
||||
flush_queue_.push_back(flush_req);
|
||||
RecordAtomicFlushRequestReason(stats_, flush_req.flush_reason);
|
||||
enqueued = true;
|
||||
}
|
||||
return enqueued;
|
||||
|
||||
@@ -35,11 +35,6 @@ Status DBImpl::TEST_SwitchWAL() {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_ResumeImpl(DBRecoverContext context) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return ResumeImpl(context);
|
||||
}
|
||||
|
||||
uint64_t DBImpl::TEST_MaxNextLevelOverlappingBytes(
|
||||
ColumnFamilyHandle* column_family) {
|
||||
ColumnFamilyData* cfd;
|
||||
|
||||
@@ -3029,23 +3029,12 @@ Status DBImpl::TrimMemtableHistory(WriteContext* context) {
|
||||
|
||||
Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
FlushReason atomic_flush_reason = FlushReason::kWriteBufferFull;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
// Atomic flush has one request-level reason. Derive it only from CFs that
|
||||
// scheduled the flush, not from every CF selected into the atomic cut.
|
||||
ColumnFamilyData* trigger_cfd;
|
||||
while ((trigger_cfd = flush_scheduler_.TakeNextColumnFamily()) != nullptr) {
|
||||
if (!trigger_cfd->mem()->IsEmpty() &&
|
||||
trigger_cfd->mem()->GetFlushReason() ==
|
||||
FlushReason::kMemtableMaxRangeDeletions) {
|
||||
atomic_flush_reason = FlushReason::kMemtableMaxRangeDeletions;
|
||||
}
|
||||
trigger_cfd->UnrefAndTryDelete();
|
||||
}
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds);
|
||||
for (auto cfd : cfds) {
|
||||
cfd->Ref();
|
||||
}
|
||||
flush_scheduler_.Clear();
|
||||
} else {
|
||||
ColumnFamilyData* tmp_cfd;
|
||||
while ((tmp_cfd = flush_scheduler_.TakeNextColumnFamily()) != nullptr) {
|
||||
@@ -3061,15 +3050,10 @@ Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::ScheduleFlushes:PreSwitchMemtable",
|
||||
nullptr);
|
||||
autovector<FlushReason> flush_reasons;
|
||||
flush_reasons.reserve(cfds.size());
|
||||
for (auto& cfd : cfds) {
|
||||
FlushReason flush_reason = FlushReason::kWriteBufferFull;
|
||||
if (status.ok() && !cfd->mem()->IsEmpty()) {
|
||||
flush_reason = cfd->mem()->GetFlushReason();
|
||||
status = SwitchMemtable(cfd, context);
|
||||
}
|
||||
flush_reasons.push_back(flush_reason);
|
||||
if (cfd->UnrefAndTryDelete()) {
|
||||
cfd = nullptr;
|
||||
}
|
||||
@@ -3084,13 +3068,12 @@ Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
AssignAtomicFlushSeq(cfds);
|
||||
FlushRequest flush_req;
|
||||
flush_req.atomic_flush = true;
|
||||
GenerateFlushRequest(cfds, atomic_flush_reason, &flush_req);
|
||||
GenerateFlushRequest(cfds, FlushReason::kWriteBufferFull, &flush_req);
|
||||
EnqueuePendingFlush(flush_req);
|
||||
} else {
|
||||
assert(flush_reasons.size() == cfds.size());
|
||||
for (size_t i = 0; i < cfds.size(); ++i) {
|
||||
for (auto* cfd : cfds) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest({cfds[i]}, flush_reasons[i], &flush_req);
|
||||
GenerateFlushRequest({cfd}, FlushReason::kWriteBufferFull, &flush_req);
|
||||
EnqueuePendingFlush(flush_req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3559,79 +3559,6 @@ TEST_F(DBRangeDelTest, MemtableMaxRangeDeletions) {
|
||||
ASSERT_EQ(3, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, FlushReasonStatsMemtableMaxRangeDeletions) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.statistics->set_stats_level(StatsLevel::kAll);
|
||||
options.disable_auto_compactions = true;
|
||||
options.memtable_max_range_deletions = 1;
|
||||
|
||||
auto flush_listener = std::make_shared<FlushCounterListener>();
|
||||
flush_listener->expected_flush_reason =
|
||||
FlushReason::kMemtableMaxRangeDeletions;
|
||||
options.listeners.push_back(flush_listener);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(0),
|
||||
Key(10)));
|
||||
// The next write observes the pending flush request.
|
||||
ASSERT_OK(Put(Key(0), "value"));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
|
||||
const uint64_t range_delete_flushes =
|
||||
TestGetTickerCount(options, FLUSH_REASON_MEMTABLE_MAX_RANGE_DELETIONS);
|
||||
ASSERT_GT(range_delete_flushes, 0);
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, FLUSH_REASON_WRITE_BUFFER_FULL));
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, FLUSH_REASON_WRITE_BUFFER_MANAGER));
|
||||
EXPECT_EQ(0, TestGetTickerCount(
|
||||
options,
|
||||
ATOMIC_FLUSH_REQUEST_REASON_MEMTABLE_MAX_RANGE_DELETIONS));
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, ATOMIC_FLUSH_REQUEST_REASON_OTHER));
|
||||
EXPECT_EQ(range_delete_flushes,
|
||||
static_cast<uint64_t>(flush_listener->count.load()));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, AtomicFlushReasonStatsMemtableMaxRangeDeletions) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.statistics->set_stats_level(StatsLevel::kAll);
|
||||
options.atomic_flush = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.memtable_max_range_deletions = 1;
|
||||
|
||||
auto flush_listener = std::make_shared<FlushCounterListener>();
|
||||
flush_listener->expected_flush_reason =
|
||||
FlushReason::kMemtableMaxRangeDeletions;
|
||||
options.listeners.push_back(flush_listener);
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
ASSERT_OK(Put(1, Key(0), "value"));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(0),
|
||||
Key(10)));
|
||||
// The next write observes the pending flush request.
|
||||
ASSERT_OK(Put(Key(0), "value"));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
|
||||
const uint64_t range_delete_flushes =
|
||||
TestGetTickerCount(options, FLUSH_REASON_MEMTABLE_MAX_RANGE_DELETIONS);
|
||||
ASSERT_GE(range_delete_flushes, 2);
|
||||
EXPECT_EQ(1, TestGetTickerCount(
|
||||
options,
|
||||
ATOMIC_FLUSH_REQUEST_REASON_MEMTABLE_MAX_RANGE_DELETIONS));
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, FLUSH_REASON_WRITE_BUFFER_FULL));
|
||||
EXPECT_EQ(0, TestGetTickerCount(
|
||||
options, ATOMIC_FLUSH_REQUEST_REASON_WRITE_BUFFER_FULL));
|
||||
EXPECT_EQ(0, TestGetTickerCount(
|
||||
options, ATOMIC_FLUSH_REQUEST_REASON_WRITE_BUFFER_MANAGER));
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, ATOMIC_FLUSH_REQUEST_REASON_OTHER));
|
||||
EXPECT_EQ(range_delete_flushes,
|
||||
static_cast<uint64_t>(flush_listener->count.load()));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, RangeDelReseekAfterFileReadError) {
|
||||
// This is to test a bug that is fixed in
|
||||
// https://github.com/facebook/rocksdb/pull/11786.
|
||||
|
||||
+3
-18
@@ -33,7 +33,6 @@
|
||||
#include "logging/logging.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/statistics_impl.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -83,8 +82,6 @@ const char* GetFlushReasonString(FlushReason flush_reason) {
|
||||
return "WAL Full";
|
||||
case FlushReason::kCatchUpAfterErrorRecovery:
|
||||
return "Catch Up After Error Recovery";
|
||||
case FlushReason::kMemtableMaxRangeDeletions:
|
||||
return "Memtable Max Range Deletions";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
@@ -962,21 +959,9 @@ Status FlushJob::WriteLevel0Table() {
|
||||
total_num_range_deletes += m->NumRangeDeletion();
|
||||
}
|
||||
|
||||
RecordInHistogram(stats_, FLUSH_MEMTABLE_MEMORY_BYTES, total_memory_usage);
|
||||
RecordInHistogram(stats_, FLUSH_MEMTABLE_TOTAL_DATA_SIZE, total_data_size);
|
||||
if (flush_reason_ == FlushReason::kWriteBufferFull) {
|
||||
RecordTick(stats_, FLUSH_REASON_WRITE_BUFFER_FULL);
|
||||
RecordInHistogram(stats_, FLUSH_WRITE_BUFFER_FULL_MEMTABLE_MEMORY_BYTES,
|
||||
total_memory_usage);
|
||||
} else if (flush_reason_ == FlushReason::kWriteBufferManager) {
|
||||
RecordTick(stats_, FLUSH_REASON_WRITE_BUFFER_MANAGER);
|
||||
RecordInHistogram(stats_,
|
||||
FLUSH_WRITE_BUFFER_MANAGER_MEMTABLE_MEMORY_BYTES,
|
||||
total_memory_usage);
|
||||
} else if (flush_reason_ == FlushReason::kMemtableMaxRangeDeletions) {
|
||||
RecordTick(stats_, FLUSH_REASON_MEMTABLE_MAX_RANGE_DELETIONS);
|
||||
}
|
||||
|
||||
// TODO(cbi): when memtable is flushed due to number of range deletions
|
||||
// hitting limit memtable_max_range_deletions, flush_reason_ is still
|
||||
// "Write Buffer Full", should make update flush_reason_ accordingly.
|
||||
event_logger_->Log() << "job" << job_context_->job_id << "event"
|
||||
<< "flush_started" << "num_memtables" << mems_.size()
|
||||
<< "total_num_input_entries" << total_num_input_entries
|
||||
|
||||
@@ -390,15 +390,6 @@ bool MemTable::ShouldFlushNow() {
|
||||
return arena_.AllocatedAndUnused() < kArenaBlockSize / 4;
|
||||
}
|
||||
|
||||
FlushReason MemTable::GetFlushReason() const {
|
||||
if (memtable_max_range_deletions_ > 0 &&
|
||||
num_range_deletes_.LoadRelaxed() >=
|
||||
static_cast<uint64_t>(memtable_max_range_deletions_)) {
|
||||
return FlushReason::kMemtableMaxRangeDeletions;
|
||||
}
|
||||
return FlushReason::kWriteBufferFull;
|
||||
}
|
||||
|
||||
void MemTable::UpdateFlushState() {
|
||||
auto state = flush_state_.load(std::memory_order_relaxed);
|
||||
if (state == FLUSH_NOT_REQUESTED && ShouldFlushNow()) {
|
||||
|
||||
@@ -858,10 +858,6 @@ class MemTable final : public ReadOnlyMemTable {
|
||||
// Returns a heuristic flush decision
|
||||
bool ShouldFlushNow();
|
||||
|
||||
// Returns the reason for a flush scheduled because this memtable decided it
|
||||
// should stop accepting more writes.
|
||||
FlushReason GetFlushReason() const;
|
||||
|
||||
// Updates `fragmented_range_tombstone_list_` that will be used to serve reads
|
||||
// when this memtable becomes an immutable memtable (in some
|
||||
// MemtableListVersion::memlist_). Should be called when this memtable is
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates. 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).
|
||||
|
||||
+13
-15
@@ -179,22 +179,20 @@ Status TableCache::GetTableReader(
|
||||
} else {
|
||||
expected_unique_id = kNullUniqueId64x2; // null ID == no verification
|
||||
}
|
||||
TableReaderOptions table_reader_options(
|
||||
ioptions_, mutable_cf_options.prefix_extractor,
|
||||
mutable_cf_options.compression_manager.get(), file_options,
|
||||
internal_comparator, mutable_cf_options.block_protection_bytes_per_key,
|
||||
skip_filters, immortal_tables_, false /* force_direct_prefetch */,
|
||||
level, block_cache_tracer_, max_file_size_for_l0_meta_pin,
|
||||
db_session_id_, file_meta.fd.GetNumber(), expected_unique_id,
|
||||
file_meta.fd.largest_seqno, file_meta.tail_size,
|
||||
file_meta.user_defined_timestamps_persisted,
|
||||
avoid_shared_metadata_cache);
|
||||
// Route same-file ("embedded") blob reads through the CFD's BlobSource for
|
||||
// caching + stats. nullptr in non-DB contexts (e.g. repair).
|
||||
table_reader_options.blob_source = blob_source_;
|
||||
s = mutable_cf_options.table_factory->NewTableReader(
|
||||
ro, table_reader_options, std::move(file_reader),
|
||||
file_meta.fd.GetFileSize(), table_reader,
|
||||
ro,
|
||||
TableReaderOptions(
|
||||
ioptions_, mutable_cf_options.prefix_extractor,
|
||||
mutable_cf_options.compression_manager.get(), file_options,
|
||||
internal_comparator,
|
||||
mutable_cf_options.block_protection_bytes_per_key, skip_filters,
|
||||
immortal_tables_, false /* force_direct_prefetch */, level,
|
||||
block_cache_tracer_, max_file_size_for_l0_meta_pin, db_session_id_,
|
||||
file_meta.fd.GetNumber(), expected_unique_id,
|
||||
file_meta.fd.largest_seqno, file_meta.tail_size,
|
||||
file_meta.user_defined_timestamps_persisted,
|
||||
avoid_shared_metadata_cache),
|
||||
std::move(file_reader), file_meta.fd.GetFileSize(), table_reader,
|
||||
prefetch_index_and_filter_in_cache);
|
||||
TEST_SYNC_POINT("TableCache::GetTableReader:0");
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ class Arena;
|
||||
struct FileDescriptor;
|
||||
class GetContext;
|
||||
class HistogramImpl;
|
||||
class BlobSource;
|
||||
|
||||
struct TableCacheOpenOptions {
|
||||
// Open a new TableReader owned by the returned iterator instead of reusing
|
||||
@@ -70,15 +69,6 @@ class TableCache {
|
||||
const std::string& db_session_id, bool fast_sst_open = false);
|
||||
~TableCache();
|
||||
|
||||
// Sets the BlobSource used to route same-file ("embedded") blob reads of SSTs
|
||||
// opened through this cache via the blob value cache + BLOB_DB_* statistics.
|
||||
// Both the TableCache and the BlobSource are owned by the same
|
||||
// ColumnFamilyData and share its lifetime, so the raw pointer is safe.
|
||||
// Called once at CFD setup, after the BlobSource is constructed. Left unset
|
||||
// (nullptr) in non-DB contexts (e.g. repair), where embedded reads fall back
|
||||
// to a direct (uncached) read.
|
||||
void SetBlobSource(BlobSource* blob_source) { blob_source_ = blob_source; }
|
||||
|
||||
// Cache interface for table cache
|
||||
using CacheInterface =
|
||||
BasicTypedCacheInterface<TableReader, CacheEntryRole::kMisc>;
|
||||
@@ -363,9 +353,6 @@ class TableCache {
|
||||
Striped<CacheAlignedWrapper<port::Mutex>> loader_mutex_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
std::string db_session_id_;
|
||||
// Owned by the same ColumnFamilyData; see SetBlobSource(). nullptr in non-DB
|
||||
// contexts (e.g. repair).
|
||||
BlobSource* blob_source_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -68,23 +68,6 @@ Status FileMetaData::UpdateBoundaries(const Slice& key, const Slice& value,
|
||||
// Helper: update oldest_blob_file_number from a single BlobIndex.
|
||||
auto update_oldest_blob = [&](const BlobIndex& blob_index) -> Status {
|
||||
if (!blob_index.IsInlined() && !blob_index.HasTTL()) {
|
||||
// INTEGRITY TRIPWIRE -- do not weaken. This is generic file metadata on
|
||||
// the table-build output path (flush / compaction / recovery, via
|
||||
// BuildTable and CompactionOutputs), which must only ever see external
|
||||
// blob-file references. file_number 0 is kInvalidBlobFileNumber, which is
|
||||
// deliberately also the same-file ("embedded") sentinel
|
||||
// kCurrentFileBlobIndexFileNumber (see blob_constants.h). Reaching here
|
||||
// with 0 means one of:
|
||||
// * a same-file/embedded reference leaked into the output stream
|
||||
// unresolved -- e.g. EmbeddedBlobResolvingIterator failed to resolve
|
||||
// and fell back to the raw value on a blob-region read error
|
||||
// (T277566778); or
|
||||
// * an uninitialized/corrupt BlobIndex (default file number never set).
|
||||
// Rejecting it flags the bug closer to its root cause. Do NOT relax this
|
||||
// to skip same-file refs (e.g. via BlobIndex::IsSameFile()): that would
|
||||
// mask real corruption AND could persist unresolvable same-file
|
||||
// references into ordinary (non-embedded) SSTs, turning a transient error
|
||||
// into permanent data loss.
|
||||
if (blob_index.file_number() == kInvalidBlobFileNumber) {
|
||||
return Status::Corruption("Invalid blob file number");
|
||||
}
|
||||
|
||||
@@ -2813,12 +2813,6 @@ Status Version::GetBlob(const ReadOptions& read_options, const Slice& user_key,
|
||||
|
||||
auto blob_file_meta = storage_info_.GetBlobFileMetaData(blob_file_number);
|
||||
if (!blob_file_meta) {
|
||||
// INTEGRITY CHECK -- do not weaken. The blob index must reference a known
|
||||
// external blob file. No metadata (including file_number 0, the
|
||||
// kInvalidBlobFileNumber / same-file "embedded" sentinel) means a corrupt
|
||||
// index, or a same-file reference that should have been resolved by
|
||||
// EmbeddedBlobResolvingIterator before reaching the integrated-BlobDB read
|
||||
// path. See FileMetaData::UpdateBoundaries for the write-side tripwire.
|
||||
return Status::Corruption("Invalid blob file number");
|
||||
}
|
||||
|
||||
@@ -2860,9 +2854,6 @@ void Version::MultiGetBlob(
|
||||
}
|
||||
|
||||
if (!blob_file_meta) {
|
||||
// INTEGRITY CHECK -- do not weaken; see Version::GetBlob and
|
||||
// FileMetaData::UpdateBoundaries. A same-file/embedded reference must
|
||||
// be resolved before reaching the integrated-BlobDB read path.
|
||||
*key_context->s = Status::Corruption("Invalid blob file number");
|
||||
continue;
|
||||
}
|
||||
|
||||
+2
-3
@@ -1480,9 +1480,8 @@ class VersionSet {
|
||||
// If an error occurs and recovery creates new memtables, SwitchMemtable
|
||||
// uses LastSequence() which may be lower than already-allocated sequences.
|
||||
//
|
||||
// REQUIRED: DB mutex is held, and callers have reached a recovery fence
|
||||
// where no concurrent writer can advance last_allocated_sequence_ before
|
||||
// the memtable/WAL state that consumes last_sequence_ is created.
|
||||
// REQUIRED: DB mutex is held and no concurrent writers are active (i.e.,
|
||||
// after WaitForBackgroundWork() in ResumeImpl).
|
||||
void SyncLastSequenceWithAllocated() {
|
||||
uint64_t alloc_seq =
|
||||
last_allocated_sequence_.load(std::memory_order_seq_cst);
|
||||
|
||||
@@ -231,7 +231,6 @@ DECLARE_int32(ingest_external_file_one_in);
|
||||
DECLARE_int32(ingest_external_file_width);
|
||||
DECLARE_int32(ingest_external_file_prepare_commit_one_in);
|
||||
DECLARE_int32(ingest_external_file_use_file_info_one_in);
|
||||
DECLARE_bool(ingest_external_file_with_embedded_blobs);
|
||||
DECLARE_int32(compact_files_one_in);
|
||||
DECLARE_int32(compact_range_one_in);
|
||||
DECLARE_int32(promote_l0_one_in);
|
||||
@@ -353,7 +352,6 @@ DECLARE_int32(prepopulate_blob_cache);
|
||||
DECLARE_int32(approximate_size_one_in);
|
||||
DECLARE_bool(best_efforts_recovery);
|
||||
DECLARE_bool(skip_verifydb);
|
||||
DECLARE_string(verify_cpu_corruption_dir);
|
||||
DECLARE_bool(paranoid_file_checks);
|
||||
DECLARE_uint64(batch_protection_bytes_per_key);
|
||||
DECLARE_uint32(memtable_protection_bytes_per_key);
|
||||
|
||||
@@ -130,14 +130,6 @@ DEFINE_bool(enable_pipelined_write, false, "Pipeline WAL/memtable writes");
|
||||
|
||||
DEFINE_bool(verify_before_write, false, "Verify before write");
|
||||
|
||||
DEFINE_string(
|
||||
verify_cpu_corruption_dir, "",
|
||||
"When non-empty, activates a slow, meticulous verification mode intended "
|
||||
"only for use with a CPU fault injector; on the first corruption found it "
|
||||
"writes a result file here and fails the run. See "
|
||||
"StressTest::MaybeVerifyCpuCorruption for the full behavior and "
|
||||
"output-file contract. Empty (default) = off.");
|
||||
|
||||
DEFINE_bool(histogram, false, "Print histogram of operation timings");
|
||||
|
||||
DEFINE_bool(destroy_db_initially, true,
|
||||
@@ -714,6 +706,20 @@ DEFINE_string(
|
||||
"only tracked when --sync_fault_injection is set. See --seed and "
|
||||
"--nooverwritepercent for further requirements.");
|
||||
|
||||
DEFINE_bool(expected_state_trace_debug, true,
|
||||
"If true, print debug logs while replaying expected-state trace "
|
||||
"records during crash recovery verification.");
|
||||
|
||||
DEFINE_int64(
|
||||
expected_state_trace_debug_key, -1,
|
||||
"If non-negative, restrict expected-state trace debug logs to the "
|
||||
"specified logical key where possible. Raw-key roundtrip mismatches for "
|
||||
"that logical key are still logged.");
|
||||
|
||||
DEFINE_int32(expected_state_trace_debug_max_logs, 200,
|
||||
"Maximum number of expected-state trace debug log lines to emit "
|
||||
"per restore attempt.");
|
||||
|
||||
DEFINE_bool(verify_checksum, false,
|
||||
"Verify checksum for every block read from storage");
|
||||
|
||||
@@ -874,14 +880,6 @@ DEFINE_int32(ingest_external_file_use_file_info_one_in, 0,
|
||||
"SstFileWriter::Finish) once every N ingestions on average, so "
|
||||
"ingestion skips re-opening and scanning the files.");
|
||||
|
||||
DEFINE_bool(
|
||||
ingest_external_file_with_embedded_blobs, false,
|
||||
"If true, external files for ingestion are written with embedded "
|
||||
"blobs via SstFileWriter::OpenWithEmbeddedBlobs(). min_blob_size is "
|
||||
"set so that only the largest generated values are embedded. "
|
||||
"Requires block-based table format_version >= 7 and "
|
||||
"ingest_external_file_one_in > 0.");
|
||||
|
||||
DEFINE_int32(compact_files_one_in, 0,
|
||||
"If non-zero, then CompactFiles() will be called once for every N "
|
||||
"operations on average. 0 indicates CompactFiles() is disabled.");
|
||||
|
||||
@@ -8,12 +8,9 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
|
||||
@@ -33,7 +30,6 @@
|
||||
#include "db_stress_tool/db_stress_wide_merge_operator.h"
|
||||
#include "file/file_util.h"
|
||||
#include "options/options_parser.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/io_dispatcher.h"
|
||||
@@ -262,121 +258,6 @@ bool NeedsFaultInjection() {
|
||||
FLAGS_write_fault_one_in || FLAGS_sync_fault_injection;
|
||||
}
|
||||
|
||||
std::string GetFaultInjectionLogBaseDir() {
|
||||
if (!FLAGS_env_uri.empty() || !FLAGS_fs_uri.empty()) {
|
||||
return "/tmp";
|
||||
}
|
||||
// db_stress reads environment variables during single-threaded startup,
|
||||
// before worker threads are created.
|
||||
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
||||
const char* test_tmpdir = std::getenv("TEST_TMPDIR");
|
||||
return test_tmpdir != nullptr && test_tmpdir[0] != '\0' ? test_tmpdir
|
||||
: "/tmp";
|
||||
}
|
||||
|
||||
std::string GetFaultInjectionLogPath(const std::string& db_label) {
|
||||
const std::string log_dir =
|
||||
GetFaultInjectionLogBaseDir() + "/fault_injection_logs";
|
||||
Status s = Env::Default()->CreateDirIfMissing(log_dir);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "Failed to create directory %s: %s\n", log_dir.c_str(),
|
||||
s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
return log_dir + "/fault_injection_" + std::to_string(port::GetProcessID()) +
|
||||
"_" + std::to_string(Env::Default()->NowMicros()) + "_" + db_label +
|
||||
".bin";
|
||||
}
|
||||
|
||||
Slice ExpectedValueSlice(const ExpectedValue& expected_value, char* scratch) {
|
||||
const size_t size =
|
||||
GenerateValue(expected_value.GetValueBase(), scratch, kValueMaxLen);
|
||||
return Slice(scratch, size);
|
||||
}
|
||||
|
||||
struct DataCorruption {
|
||||
// "lost" | "resurrected" | "wrong-value" | "detected-corruption"
|
||||
const char* kind;
|
||||
Slice value_from_db;
|
||||
Slice value_from_expected;
|
||||
std::string op_status;
|
||||
};
|
||||
|
||||
std::optional<DataCorruption> ClassifyReadBack(
|
||||
const Status& read_status, const std::string& db_value,
|
||||
const ExpectedValue& expected_value, char* scratch) {
|
||||
if (read_status.IsCorruption()) {
|
||||
return DataCorruption{"detected-corruption", db_value, Slice(),
|
||||
"Get: " + read_status.ToString()};
|
||||
}
|
||||
// Fault injection is off (enforced at startup), so the read-back can only be
|
||||
// OK or NotFound here. Anything else is outside this tool's contract -- stop
|
||||
// now rather than guess a classification. ImmediateExit (not exit/abort): the
|
||||
// DB is open with live background threads, so a normal exit would race their
|
||||
// static teardown (see port::ImmediateExit).
|
||||
if (!read_status.ok() && !read_status.IsNotFound()) {
|
||||
fprintf(stderr, "verify_cpu_corruption: unexpected read-back status: %s\n",
|
||||
read_status.ToString().c_str());
|
||||
port::ImmediateExit(1);
|
||||
}
|
||||
const bool present_in_db = read_status.ok();
|
||||
// Exists() asserts the key is not mid-write -- which holds under --threads=1:
|
||||
// the just-run op committed before this read-back.
|
||||
const bool expected_to_exist = expected_value.Exists();
|
||||
|
||||
if (!expected_to_exist && !present_in_db) {
|
||||
return std::nullopt; // Agree: key absent.
|
||||
}
|
||||
if (expected_to_exist && !present_in_db) {
|
||||
return DataCorruption{"lost", Slice(),
|
||||
ExpectedValueSlice(expected_value, scratch),
|
||||
"Get: NotFound"};
|
||||
}
|
||||
if (!expected_to_exist && present_in_db) {
|
||||
return DataCorruption{"resurrected", db_value, Slice(), "Get: OK"};
|
||||
}
|
||||
// Present in both: compare the values.
|
||||
const Slice expected = ExpectedValueSlice(expected_value, scratch);
|
||||
if (Slice(db_value) == expected) {
|
||||
return std::nullopt; // Agree: same value.
|
||||
}
|
||||
return DataCorruption{"wrong-value", db_value, expected, "Get: OK"};
|
||||
}
|
||||
|
||||
void WriteDataCorruption(uint32_t thread_id, int cf, int64_t key,
|
||||
const DataCorruption& corruption) {
|
||||
std::ostringstream oss;
|
||||
oss << "{\n"
|
||||
<< " \"kind\": \"" << corruption.kind << "\",\n"
|
||||
<< " \"cf\": " << cf << ",\n"
|
||||
<< " \"key\": " << key << ",\n"
|
||||
<< " \"value_from_db\": \""
|
||||
<< corruption.value_from_db.ToString(/* hex */ true) << "\",\n"
|
||||
<< " \"value_from_expected\": \""
|
||||
<< corruption.value_from_expected.ToString(/* hex */ true) << "\",\n"
|
||||
<< " \"op_status\": " << std::quoted(corruption.op_status) << "\n"
|
||||
<< "}\n";
|
||||
const std::string path = FLAGS_verify_cpu_corruption_dir +
|
||||
"/data_corruption." + std::to_string(thread_id) +
|
||||
".json";
|
||||
FILE* f = fopen(path.c_str(), "w");
|
||||
if (f == nullptr) {
|
||||
fprintf(stderr, "Failed to open CPU corruption result file: %s\n",
|
||||
path.c_str());
|
||||
port::ImmediateExit(1);
|
||||
}
|
||||
if (fputs(oss.str().c_str(), f) == EOF) {
|
||||
fprintf(stderr, "Failed to write CPU corruption result file: %s\n",
|
||||
path.c_str());
|
||||
port::ImmediateExit(1);
|
||||
}
|
||||
if (fclose(f) != 0) {
|
||||
fprintf(stderr, "Failed to close CPU corruption result file: %s\n",
|
||||
path.c_str());
|
||||
port::ImmediateExit(1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const std::string& StressTest::GetDbLabel() const { return db_label_; }
|
||||
@@ -404,8 +285,7 @@ StressTest::StressTest(int db_index, const std::string& db_path,
|
||||
std::make_shared<DbStressFSWrapper>(raw_env->GetFileSystem())),
|
||||
db_fault_injection_fs_(
|
||||
NeedsFaultInjection()
|
||||
? std::make_shared<FaultInjectionTestFS>(
|
||||
db_stress_fs_, GetFaultInjectionLogPath(db_label_))
|
||||
? std::make_shared<FaultInjectionTestFS>(db_stress_fs_)
|
||||
: nullptr),
|
||||
db_env_(std::make_unique<CompositeEnvWrapper>(
|
||||
raw_env, db_fault_injection_fs_
|
||||
@@ -434,6 +314,23 @@ StressTest::StressTest(int db_index, const std::string& db_path,
|
||||
// StressTest::Open() for open fault injection and in RunStressTestImpl()
|
||||
// for proper fault injection setup.
|
||||
db_fault_injection_fs_->SetFilesystemDirectWritable(true);
|
||||
|
||||
// Set the fault injection log file path so that PrintAll() writes to a
|
||||
// file instead of stderr (signal-safe). PrintAll() opens this path with
|
||||
// plain POSIX open(), not through raw_env, so the log path must stay on
|
||||
// the local filesystem. Store it in the local test directory (TEST_TMPDIR
|
||||
// via Env::Default()) outside the DB directory so it survives DB reopen
|
||||
// and gets included in the sandcastle db.tar.gz artifact for post-failure
|
||||
// analysis.
|
||||
std::string log_dir;
|
||||
if (!Env::Default()->GetTestDirectory(&log_dir).ok() || log_dir.empty()) {
|
||||
log_dir = "/tmp";
|
||||
}
|
||||
std::string log_path = log_dir + "/fault_injection_" +
|
||||
std::to_string(getpid()) + "_" +
|
||||
std::to_string(Env::Default()->NowMicros()) + "_" +
|
||||
GetDbLabel() + ".log";
|
||||
db_fault_injection_fs_->SetInjectedErrorLogPath(log_path);
|
||||
}
|
||||
|
||||
if (FLAGS_destroy_db_initially) {
|
||||
@@ -1018,97 +915,6 @@ void StressTest::VerificationAbort(SharedState* shared, int cf, int64_t key,
|
||||
shared->SetVerificationFailure();
|
||||
}
|
||||
|
||||
// Under --verify_cpu_corruption_dir, right after each write op
|
||||
// (put/delete/deleterange), flush, and compaction (compactrange/compactfiles),
|
||||
// verifies that op for corruption and, on the first one found, writes a result
|
||||
// file into the directory and marks the run as a verification failure. It catches:
|
||||
// (a) a corruption returned by the op itself or by the read-back
|
||||
// (Status::Corruption); or (b) a silent data corruption (SDC) -- a read-back
|
||||
// that SUCCEEDS but returns the wrong result vs the committed expected state: a
|
||||
// lost key (NotFound), a resurrected key (deleted but present), or a wrong value
|
||||
// (bytes differ). Surfacing these with an immediate read right after the op
|
||||
// (instead of waiting for a later read or the end-of-run db verification) gives
|
||||
// the most immediate and accurate verification when an external CPU-fault
|
||||
// injector (e.g. gdb) flips a register inside that op.
|
||||
//
|
||||
// REQUIRES (enforced at startup) --threads=1 and all fault injection off (every
|
||||
// *_fault_one_in = 0 and sync_fault_injection = false): the full-keyspace
|
||||
// read-back is only well-defined with a single writer, and injected I/O faults
|
||||
// would otherwise taint it.
|
||||
//
|
||||
// OUTPUT CONTRACT (one file per worker thread):
|
||||
// <dir>/data_corruption.<thread_id>.json -- a JSON object with fields: kind (one
|
||||
// of lost|resurrected|wrong-value|detected-corruption), cf (int), key (int),
|
||||
// value_from_db (hex), value_from_expected (hex), op_status (string).
|
||||
//
|
||||
// PERFORMANCE: the read-back scans the entire keyspace after every op
|
||||
// (O(max_key) Get calls per op), so this is meant for single-op
|
||||
// CPU-fault-injection debugging, not general stress runs.
|
||||
void StressTest::MaybeVerifyCpuCorruption(ThreadState* thread,
|
||||
const char* op_label,
|
||||
const Status& op_status) {
|
||||
if (FLAGS_verify_cpu_corruption_dir.empty()) {
|
||||
return;
|
||||
}
|
||||
auto shared = thread->shared;
|
||||
|
||||
if (shared->HasVerificationFailedYet()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The op itself returned Corruption: an integrity check caught the
|
||||
// corruption.
|
||||
if (op_status.IsCorruption()) {
|
||||
WriteDataCorruption(thread->tid, /* cf */ -1, /* key */ -1,
|
||||
{"detected-corruption", Slice(), Slice(),
|
||||
std::string(op_label) + ": " + op_status.ToString()});
|
||||
VerificationAbort(shared,
|
||||
std::string(op_label) +
|
||||
" detected data corruption: " + op_status.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
// Single writer (the run is pinned to --threads=1): read every key back and
|
||||
// compare it against the committed expected state, ending the run on the
|
||||
// first data corruption found.
|
||||
// TODO: this full-keyspace read-back duplicates the canonical verifier
|
||||
// VerifyDb (no_batched_ops_stress.cc). Refactor that key/value compare into a
|
||||
// shared per-key helper both can call, so this stays in sync with the model.
|
||||
ReadOptions read_opts;
|
||||
read_opts.verify_checksums = true;
|
||||
// A user-timestamp DB needs a read timestamp; supply one like the canonical
|
||||
// verifier (no_batched_ops_stress.cc).
|
||||
std::string read_ts_str;
|
||||
Slice read_ts;
|
||||
if (FLAGS_user_timestamp_size > 0) {
|
||||
read_ts_str = GetNowNanos();
|
||||
read_ts = read_ts_str;
|
||||
read_opts.timestamp = &read_ts;
|
||||
}
|
||||
const int64_t max_key = shared->GetMaxKey();
|
||||
std::string db_value;
|
||||
char expected_scratch[kValueMaxLen];
|
||||
for (int cf = 0; cf < static_cast<int>(column_families_.size()); ++cf) {
|
||||
for (int64_t key = 0; key < max_key; ++key) {
|
||||
const ExpectedValue expected = shared->Get(cf, key);
|
||||
db_value.clear();
|
||||
const Status s =
|
||||
db_->Get(read_opts, column_families_[cf], Key(key), &db_value);
|
||||
const std::optional<DataCorruption> corruption =
|
||||
ClassifyReadBack(s, db_value, expected, expected_scratch);
|
||||
if (corruption.has_value()) {
|
||||
WriteDataCorruption(thread->tid, cf, key, *corruption);
|
||||
VerificationAbort(
|
||||
shared,
|
||||
std::string(corruption->kind) + " (" + corruption->op_status + ")",
|
||||
cf, key, corruption->value_from_db,
|
||||
corruption->value_from_expected);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string StressTest::DebugString(const Slice& value,
|
||||
const WideColumns& columns) {
|
||||
std::ostringstream oss;
|
||||
@@ -1401,18 +1207,13 @@ Status StressTest::CommitTxn(Transaction& txn, ThreadState* thread) {
|
||||
std::string last_resume_status;
|
||||
if (!rollback_s.ok() && IsErrorInjectedAndRetryable(rollback_s) &&
|
||||
db_ != nullptr) {
|
||||
// Retry budget: count only actual rollback attempts (after Resume
|
||||
// succeeds), not time spent waiting for recovery to finish on another
|
||||
// thread. With many concurrent threads hitting write errors, recovery
|
||||
// can take much longer than individual threads' retry budgets.
|
||||
constexpr int kMaxRollbackRetries = 100;
|
||||
constexpr int kMaxResumeBusyWaits = 3000;
|
||||
constexpr int kRetryIntervalMicros = 10 * 1000;
|
||||
for (; rollback_recovery_retries < kMaxRollbackRetries &&
|
||||
resume_busy_count < kMaxResumeBusyWaits && !rollback_s.ok();) {
|
||||
constexpr int kMaxRollbackAfterRecoveryRetries = 100;
|
||||
constexpr int kRollbackAfterRecoveryRetryIntervalMicros = 10 * 1000;
|
||||
for (; rollback_recovery_retries < kMaxRollbackAfterRecoveryRetries &&
|
||||
!rollback_s.ok();
|
||||
++rollback_recovery_retries) {
|
||||
const Status resume_s = db_->Resume();
|
||||
if (resume_s.ok()) {
|
||||
++rollback_recovery_retries;
|
||||
last_resume_status = resume_s.ToString();
|
||||
rollback_s = txn.Rollback();
|
||||
if (rollback_s.ok() || !IsErrorInjectedAndRetryable(rollback_s)) {
|
||||
@@ -1425,7 +1226,8 @@ Status StressTest::CommitTxn(Transaction& txn, ThreadState* thread) {
|
||||
++resume_busy_count;
|
||||
last_resume_status = resume_s.ToString();
|
||||
}
|
||||
clock_->SleepForMicroseconds(kRetryIntervalMicros);
|
||||
clock_->SleepForMicroseconds(
|
||||
kRollbackAfterRecoveryRetryIntervalMicros);
|
||||
}
|
||||
}
|
||||
if (!rollback_s.ok()) {
|
||||
@@ -1764,7 +1566,8 @@ void StressTest::OperateDb(ThreadState* thread) {
|
||||
GenerateColumnFamilies(FLAGS_column_families, rand_column_family);
|
||||
|
||||
if (thread->rand.OneInOpt(FLAGS_flush_one_in)) {
|
||||
TestFlush(thread, rand_column_families);
|
||||
Status status = TestFlush(rand_column_families);
|
||||
ProcessStatus(shared, "Flush", status);
|
||||
}
|
||||
|
||||
if (thread->rand.OneInOpt(FLAGS_get_live_files_apis_one_in)) {
|
||||
@@ -3749,21 +3552,23 @@ void StressTest::TestCompactFiles(ThreadState* thread,
|
||||
static_cast<int>(output_level));
|
||||
if (!s.ok()) {
|
||||
thread->stats.AddNumCompactFilesFailed(1);
|
||||
// TOOD (hx235): allow an exact list of tolerable failures under stress
|
||||
// test
|
||||
bool non_ok_status_allowed =
|
||||
s.IsManualCompactionPaused() || s.IsCompactionAborted() ||
|
||||
IsErrorInjectedAndRetryable(s) || s.IsAborted() ||
|
||||
s.IsInvalidArgument() || s.IsNotSupported();
|
||||
if (!non_ok_status_allowed) {
|
||||
fprintf(stderr,
|
||||
"Unable to perform CompactFiles(): %s under specified "
|
||||
"CompactionOptions: %s (Empty string or "
|
||||
"missing field indicates default option or value is used)\n",
|
||||
s.ToString().c_str(), compact_opt_oss.str().c_str());
|
||||
thread->shared->SafeTerminate();
|
||||
}
|
||||
} else {
|
||||
thread->stats.AddNumCompactFilesSucceed(1);
|
||||
}
|
||||
// Verify before the fail-fast: a corruption that CompactFiles itself
|
||||
// returns must be recorded (CORRUPTION) before we terminate, or it
|
||||
// mis-buckets as CRASH. No-op unless CPU-corruption verification is on.
|
||||
MaybeVerifyCpuCorruption(thread, "compactfiles", s);
|
||||
if (!s.ok() && !IsTolerableCompactionFailure(s)) {
|
||||
fprintf(stderr,
|
||||
"Unable to perform CompactFiles(): %s under specified "
|
||||
"CompactionOptions: %s (Empty string or missing field "
|
||||
"indicates default option or value is used)\n",
|
||||
s.ToString().c_str(), compact_opt_oss.str().c_str());
|
||||
thread->shared->SafeTerminate();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3793,24 +3598,16 @@ void StressTest::TestPromoteL0(ThreadState* thread,
|
||||
}
|
||||
}
|
||||
|
||||
void StressTest::TestFlush(ThreadState* thread,
|
||||
const std::vector<int>& rand_column_families) {
|
||||
Status StressTest::TestFlush(const std::vector<int>& rand_column_families) {
|
||||
FlushOptions flush_opts;
|
||||
assert(flush_opts.wait);
|
||||
Status status;
|
||||
if (FLAGS_atomic_flush) {
|
||||
status = db_->Flush(flush_opts, column_families_);
|
||||
} else {
|
||||
std::vector<ColumnFamilyHandle*> cfhs;
|
||||
std::for_each(
|
||||
rand_column_families.begin(), rand_column_families.end(),
|
||||
[this, &cfhs](int k) { cfhs.push_back(column_families_[k]); });
|
||||
status = db_->Flush(flush_opts, cfhs);
|
||||
return db_->Flush(flush_opts, column_families_);
|
||||
}
|
||||
// Verify before the fail-fast (a no-op unless CPU-corruption verification is
|
||||
// on), then route the status through the standard flush error handling.
|
||||
MaybeVerifyCpuCorruption(thread, "flush", status);
|
||||
ProcessStatus(thread->shared, "Flush", status);
|
||||
std::vector<ColumnFamilyHandle*> cfhs;
|
||||
std::for_each(rand_column_families.begin(), rand_column_families.end(),
|
||||
[this, &cfhs](int k) { cfhs.push_back(column_families_[k]); });
|
||||
return db_->Flush(flush_opts, cfhs);
|
||||
}
|
||||
|
||||
Status StressTest::TestResetStats() { return db_->ResetStats(); }
|
||||
@@ -4039,18 +3836,21 @@ void StressTest::TestCompactRange(ThreadState* thread, int64_t rand_key,
|
||||
<< cro.blob_garbage_collection_age_cutoff;
|
||||
Status status = db_->CompactRange(cro, column_family, &start_key, &end_key);
|
||||
|
||||
// Verify before the fail-fast: a corruption that CompactRange itself returns
|
||||
// must be recorded (CORRUPTION) before we flag failure, or it mis-buckets.
|
||||
// No-op unless CPU-corruption verification is on.
|
||||
MaybeVerifyCpuCorruption(thread, "compactrange", status);
|
||||
if (!status.ok() && !IsTolerableCompactionFailure(status)) {
|
||||
fprintf(stderr,
|
||||
"Unable to perform CompactRange(): %s under specified "
|
||||
"CompactRangeOptions: %s (Empty string or missing field "
|
||||
"indicates default option or value is used)\n",
|
||||
status.ToString().c_str(), compact_range_opt_oss.str().c_str());
|
||||
// Fail fast to preserve the DB state.
|
||||
thread->shared->SetVerificationFailure();
|
||||
if (!status.ok()) {
|
||||
// TOOD (hx235): allow an exact list of tolerable failures under stress test
|
||||
bool non_ok_status_allowed =
|
||||
status.IsManualCompactionPaused() || status.IsCompactionAborted() ||
|
||||
IsErrorInjectedAndRetryable(status) || status.IsAborted() ||
|
||||
status.IsInvalidArgument() || status.IsNotSupported();
|
||||
if (!non_ok_status_allowed) {
|
||||
fprintf(stderr,
|
||||
"Unable to perform CompactRange(): %s under specified "
|
||||
"CompactRangeOptions: %s (Empty string or "
|
||||
"missing field indicates default option or value is used)\n",
|
||||
status.ToString().c_str(), compact_range_opt_oss.str().c_str());
|
||||
// Fail fast to preserve the DB state.
|
||||
thread->shared->SetVerificationFailure();
|
||||
}
|
||||
}
|
||||
|
||||
if (pre_snapshot != nullptr) {
|
||||
|
||||
@@ -36,14 +36,6 @@ class StressTest {
|
||||
!status_to_io_status(Status(error_s)).GetDataLoss();
|
||||
}
|
||||
|
||||
static bool IsTolerableCompactionFailure(const Status& s) {
|
||||
// TOOD (hx235): allow an exact list of tolerable failures under stress
|
||||
// test
|
||||
return s.IsManualCompactionPaused() || s.IsCompactionAborted() ||
|
||||
IsErrorInjectedAndRetryable(s) || s.IsAborted() ||
|
||||
s.IsInvalidArgument() || s.IsNotSupported();
|
||||
}
|
||||
|
||||
// Returns true if the status is an expected transactional error, including
|
||||
// lock conflicts (deadlock or timeout) from MaybeAddKeyToTxnForRYW writing
|
||||
// to the same key space without the stress-test-level mutex, and TryAgain
|
||||
@@ -388,8 +380,7 @@ class StressTest {
|
||||
|
||||
void TestCompactFiles(ThreadState* thread, ColumnFamilyHandle* column_family);
|
||||
|
||||
void TestFlush(ThreadState* thread,
|
||||
const std::vector<int>& rand_column_families);
|
||||
Status TestFlush(const std::vector<int>& rand_column_families);
|
||||
|
||||
Status TestResetStats();
|
||||
|
||||
@@ -442,14 +433,6 @@ class StressTest {
|
||||
void VerificationAbort(SharedState* shared, int cf, int64_t key,
|
||||
const Slice& value, const WideColumns& columns) const;
|
||||
|
||||
// Under --verify_cpu_corruption_dir, verifies the just-run op (named by
|
||||
// `op_label`, e.g. "put"/"flush"/"compactrange") for a returned/read-back
|
||||
// corruption or a silent data corruption. A no-op when the flag is empty. See
|
||||
// the definition in db_stress_test_base.cc for the full behavior, startup
|
||||
// requirements, performance cost, and result-file contract.
|
||||
void MaybeVerifyCpuCorruption(ThreadState* thread, const char* op_label,
|
||||
const Status& op_status);
|
||||
|
||||
static std::string DebugString(const Slice& value,
|
||||
const WideColumns& columns);
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ static std::shared_ptr<ROCKSDB_NAMESPACE::Env> legacy_env_wrapper_guard;
|
||||
// Raw pointers for signal-safe crash callback. Signal handlers can only
|
||||
// access file-static/global variables; can't capture StressTest instances.
|
||||
static std::vector<ROCKSDB_NAMESPACE::FaultInjectionTestFS*>
|
||||
fault_fs_for_crash_flush;
|
||||
fault_fs_for_crash_report;
|
||||
|
||||
int ValidateNumDbsFlags() {
|
||||
if (FLAGS_num_dbs < 1) {
|
||||
@@ -88,20 +88,20 @@ int DestroyAllDbs() {
|
||||
|
||||
void RegisterCrashCallbacks(
|
||||
const std::vector<std::unique_ptr<StressTest>>& stress_tests, int num_dbs) {
|
||||
fault_fs_for_crash_flush.resize(num_dbs, nullptr);
|
||||
fault_fs_for_crash_report.resize(num_dbs, nullptr);
|
||||
bool any_fault_fs = false;
|
||||
for (int i = 0; i < num_dbs; i++) {
|
||||
fault_fs_for_crash_flush[i] =
|
||||
fault_fs_for_crash_report[i] =
|
||||
stress_tests[i]->GetDbFaultInjectionFs().get();
|
||||
if (fault_fs_for_crash_flush[i]) {
|
||||
if (fault_fs_for_crash_report[i]) {
|
||||
any_fault_fs = true;
|
||||
}
|
||||
}
|
||||
if (any_fault_fs) {
|
||||
port::RegisterCrashCallback([]() {
|
||||
for (auto* fs : fault_fs_for_crash_flush) {
|
||||
for (auto* fs : fault_fs_for_crash_report) {
|
||||
if (fs) {
|
||||
fs->FlushRecentInjectedErrors();
|
||||
fs->PrintRecentInjectedErrors();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -176,45 +176,6 @@ int db_stress_tool(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!FLAGS_verify_cpu_corruption_dir.empty()) {
|
||||
// The full-keyspace read-back is only well-defined with a single writer,
|
||||
// and injected I/O faults would taint it -- so require --threads=1 and all
|
||||
// fault injection off (see the flag's comment).
|
||||
if (FLAGS_threads != 1) {
|
||||
fprintf(stderr,
|
||||
"Error: --verify_cpu_corruption_dir requires --threads=1\n");
|
||||
exit(1); // NOLINT(concurrency-mt-unsafe)
|
||||
}
|
||||
if (FLAGS_read_fault_one_in != 0 || FLAGS_write_fault_one_in != 0 ||
|
||||
FLAGS_metadata_read_fault_one_in != 0 ||
|
||||
FLAGS_metadata_write_fault_one_in != 0 ||
|
||||
FLAGS_open_read_fault_one_in != 0 ||
|
||||
FLAGS_open_write_fault_one_in != 0 ||
|
||||
FLAGS_open_metadata_read_fault_one_in != 0 ||
|
||||
FLAGS_open_metadata_write_fault_one_in != 0 ||
|
||||
FLAGS_secondary_cache_fault_one_in != 0 || FLAGS_sync_fault_injection) {
|
||||
fprintf(stderr,
|
||||
"Error: --verify_cpu_corruption_dir requires all fault injection "
|
||||
"off (every *_fault_one_in = 0 and sync_fault_injection = "
|
||||
"false)\n");
|
||||
exit(1); // NOLINT(concurrency-mt-unsafe)
|
||||
}
|
||||
// The read-back compares against the expected-state model, which only the
|
||||
// default (state-tracked) stress test maintains. The
|
||||
// batched/cf-consistency/ multi-ops-txns variants set
|
||||
// IsStateTracked()=false, so every key would look absent -- reject them
|
||||
// rather than report false losses.
|
||||
if (FLAGS_test_batches_snapshots || FLAGS_test_cf_consistency ||
|
||||
FLAGS_test_multi_ops_txns) {
|
||||
fprintf(stderr,
|
||||
"Error: --verify_cpu_corruption_dir requires the default "
|
||||
"state-tracked stress test; it is incompatible with "
|
||||
"--test_batches_snapshots, --test_cf_consistency, and "
|
||||
"--test_multi_ops_txns\n");
|
||||
exit(1); // NOLINT(concurrency-mt-unsafe)
|
||||
}
|
||||
}
|
||||
|
||||
FLAGS_rep_factory = StringToRepFactory(FLAGS_memtablerep.c_str());
|
||||
|
||||
// The number of background threads should be at least as much the
|
||||
@@ -672,7 +633,7 @@ int db_stress_tool(int argc, char** argv) {
|
||||
}
|
||||
stress_tests[i]->CleanUp();
|
||||
}
|
||||
for (auto& fs : fault_fs_for_crash_flush) {
|
||||
for (auto& fs : fault_fs_for_crash_report) {
|
||||
fs = nullptr;
|
||||
}
|
||||
return all_passed ? 0 : 1;
|
||||
|
||||
+502
-146
@@ -4,8 +4,8 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#ifdef GFLAGS
|
||||
|
||||
#include "db/wide/wide_column_serialization.h"
|
||||
@@ -348,42 +348,6 @@ Status FileExpectedStateManager::Open() {
|
||||
return s;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class FatalExpectedStateTraceWriter : public TraceWriter {
|
||||
public:
|
||||
FatalExpectedStateTraceWriter(std::string trace_file_path,
|
||||
std::unique_ptr<TraceWriter>&& target)
|
||||
: trace_file_path_(std::move(trace_file_path)),
|
||||
target_(std::move(target)) {
|
||||
assert(target_ != nullptr);
|
||||
}
|
||||
|
||||
Status Write(const Slice& data) override {
|
||||
Status s = target_->Write(data);
|
||||
if (!s.ok()) {
|
||||
// Expected-state tracing is part of crash-recovery verification, not
|
||||
// best-effort observability. Stop immediately before history diverges.
|
||||
fprintf(stderr, "Fatal expected-state trace write failure for %s: %s\n",
|
||||
trace_file_path_.c_str(), s.ToString().c_str());
|
||||
fflush(stderr);
|
||||
fflush(stdout);
|
||||
std::_Exit(1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status Close() override { return target_->Close(); }
|
||||
|
||||
uint64_t GetFileSize() override { return target_->GetFileSize(); }
|
||||
|
||||
private:
|
||||
const std::string trace_file_path_;
|
||||
std::unique_ptr<TraceWriter> target_;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
Status FileExpectedStateManager::SaveAtAndAfter(DB* db) {
|
||||
SequenceNumber seqno = db->GetLatestSequenceNumber();
|
||||
|
||||
@@ -426,10 +390,6 @@ Status FileExpectedStateManager::SaveAtAndAfter(DB* db) {
|
||||
soptions.writable_file_max_buffer_size = 0;
|
||||
s = NewFileTraceWriter(Env::Default(), soptions, trace_file_path,
|
||||
&trace_writer);
|
||||
if (s.ok()) {
|
||||
trace_writer.reset(new FatalExpectedStateTraceWriter(
|
||||
trace_file_path, std::move(trace_writer)));
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
TraceOptions trace_opts;
|
||||
@@ -467,6 +427,67 @@ bool FileExpectedStateManager::HasHistory() {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string DescribeExpectedValue(const ExpectedValue& value) {
|
||||
std::ostringstream oss;
|
||||
oss << "{raw=0x" << std::hex << std::setw(8) << std::setfill('0')
|
||||
<< value.Read() << std::dec << std::setfill(' ')
|
||||
<< " value_base=" << value.GetValueBase()
|
||||
<< " next_value_base=" << value.NextValueBase()
|
||||
<< " del_counter=" << value.GetDelCounter()
|
||||
<< " pending_write=" << value.PendingWrite()
|
||||
<< " pending_delete=" << value.PendingDelete()
|
||||
<< " deleted=" << value.IsDeleted() << "}";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
size_t CountTrailingXs(const std::string& key) {
|
||||
size_t trailing_xs = 0;
|
||||
while (trailing_xs < key.size() && key[key.size() - trailing_xs - 1] == 'x') {
|
||||
++trailing_xs;
|
||||
}
|
||||
return trailing_xs;
|
||||
}
|
||||
|
||||
struct TraceKeyDebugInfo {
|
||||
std::string raw_key;
|
||||
std::string raw_key_hex;
|
||||
bool parse_ok = false;
|
||||
uint64_t parsed_key_id = 0;
|
||||
std::string roundtrip_key;
|
||||
std::string roundtrip_key_hex;
|
||||
bool roundtrip_matches_raw = false;
|
||||
bool raw_matches_focus_key = false;
|
||||
bool parsed_matches_focus_key = false;
|
||||
bool roundtrip_matches_focus_key = false;
|
||||
size_t trailing_bytes = 0;
|
||||
size_t trailing_xs = 0;
|
||||
|
||||
bool MatchesFocusKey() const {
|
||||
return raw_matches_focus_key || parsed_matches_focus_key ||
|
||||
roundtrip_matches_focus_key;
|
||||
}
|
||||
};
|
||||
|
||||
std::string DescribeTraceKeyDebugInfo(const TraceKeyDebugInfo& info) {
|
||||
std::ostringstream oss;
|
||||
oss << "{raw_key=" << info.raw_key_hex << " size=" << info.raw_key.size()
|
||||
<< " trailing_bytes=" << info.trailing_bytes
|
||||
<< " trailing_xs=" << info.trailing_xs << " parse_ok=" << info.parse_ok;
|
||||
if (info.parse_ok) {
|
||||
oss << " parsed_key=" << info.parsed_key_id
|
||||
<< " roundtrip_key=" << info.roundtrip_key_hex
|
||||
<< " roundtrip_matches_raw=" << info.roundtrip_matches_raw;
|
||||
}
|
||||
if (FLAGS_expected_state_trace_debug_key >= 0) {
|
||||
oss << " focus_key=" << FLAGS_expected_state_trace_debug_key
|
||||
<< " raw_matches_focus_key=" << info.raw_matches_focus_key
|
||||
<< " parsed_matches_focus_key=" << info.parsed_matches_focus_key
|
||||
<< " roundtrip_matches_focus_key=" << info.roundtrip_matches_focus_key;
|
||||
}
|
||||
oss << "}";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// An `ExpectedStateTraceRecordHandler` applies a configurable number of traced
|
||||
// write operations to the configured expected state. It is used in
|
||||
// `FileExpectedStateManager::Restore()` to sync the expected state with the
|
||||
@@ -477,12 +498,25 @@ class ExpectedStateTraceRecordHandler : public TraceRecord::Handler,
|
||||
ExpectedStateTraceRecordHandler(uint64_t max_write_ops, ExpectedState* state)
|
||||
: max_write_ops_(max_write_ops),
|
||||
state_(state),
|
||||
debug_enabled_(FLAGS_expected_state_trace_debug),
|
||||
debug_focus_key_(FLAGS_expected_state_trace_debug_key),
|
||||
debug_focus_key_raw_(debug_focus_key_ >= 0 ? Key(debug_focus_key_)
|
||||
: std::string()),
|
||||
debug_max_logs_(static_cast<uint64_t>(
|
||||
std::max(0, FLAGS_expected_state_trace_debug_max_logs))),
|
||||
buffered_writes_(nullptr) {}
|
||||
|
||||
// True if we have already reached the limit on write operations to apply.
|
||||
bool IsDone() const { return num_write_ops_ >= max_write_ops_; }
|
||||
|
||||
uint64_t NumWriteOps() const { return num_write_ops_; }
|
||||
uint64_t NumKeyDecodeFailures() const { return key_decode_failures_; }
|
||||
uint64_t NumKeyRoundtripMismatches() const {
|
||||
return key_roundtrip_mismatches_;
|
||||
}
|
||||
uint64_t NumFocusKeyOpHits() const { return focus_key_op_hits_; }
|
||||
uint64_t NumLogsEmitted() const { return emitted_debug_logs_; }
|
||||
uint64_t NumLogsSuppressed() const { return suppressed_debug_logs_; }
|
||||
|
||||
bool Continue() override { return !IsDone(); }
|
||||
|
||||
@@ -522,21 +556,37 @@ class ExpectedStateTraceRecordHandler : public TraceRecord::Handler,
|
||||
Slice key =
|
||||
StripTimestampFromUserKey(key_with_ts, FLAGS_user_timestamp_size);
|
||||
uint64_t key_id = 0;
|
||||
Status status = ParseTracedKey(key, "unable to parse key", &key_id);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
const int64_t expected_key_id = static_cast<int64_t>(key_id);
|
||||
const uint32_t value_base = GetValueBase(value);
|
||||
TraceKeyDebugInfo key_info;
|
||||
Status status =
|
||||
ParseTracedKey(key, "unable to parse key", &key_id, &key_info);
|
||||
if (status.ok()) {
|
||||
const int64_t expected_key_id = static_cast<int64_t>(key_id);
|
||||
const uint32_t value_base = GetValueBase(value);
|
||||
|
||||
if (buffered_writes_ != nullptr) {
|
||||
return WriteBatchInternal::Put(buffered_writes_.get(), column_family_id,
|
||||
key, value);
|
||||
}
|
||||
bool should_buffer_write = !(buffered_writes_ == nullptr);
|
||||
if (should_buffer_write) {
|
||||
MaybeLogKeyOperation("PutCF", column_family_id, true /* buffered */,
|
||||
key_info,
|
||||
"value_base=" + std::to_string(value_base) +
|
||||
" value_size=" + std::to_string(value.size()));
|
||||
return WriteBatchInternal::Put(buffered_writes_.get(), column_family_id,
|
||||
key, value);
|
||||
}
|
||||
|
||||
state_->SyncPut(column_family_id, expected_key_id, value_base);
|
||||
NoteWriteOpApplied();
|
||||
return Status::OK();
|
||||
const ExpectedValue before =
|
||||
state_->Get(column_family_id, expected_key_id);
|
||||
state_->SyncPut(column_family_id, expected_key_id, value_base);
|
||||
const ExpectedValue after =
|
||||
state_->Get(column_family_id, expected_key_id);
|
||||
NoteWriteOpApplied();
|
||||
MaybeLogKeyOperation("PutCF", column_family_id, false /* buffered */,
|
||||
key_info,
|
||||
"value_base=" + std::to_string(value_base) +
|
||||
" value_size=" + std::to_string(value.size()),
|
||||
&before, &after);
|
||||
status = Status::OK();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status TimedPutCF(uint32_t column_family_id, const Slice& key_with_ts,
|
||||
@@ -544,22 +594,40 @@ class ExpectedStateTraceRecordHandler : public TraceRecord::Handler,
|
||||
Slice key =
|
||||
StripTimestampFromUserKey(key_with_ts, FLAGS_user_timestamp_size);
|
||||
uint64_t key_id = 0;
|
||||
Status status = ParseTracedKey(key, "unable to parse key", &key_id);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
const int64_t expected_key_id = static_cast<int64_t>(key_id);
|
||||
const uint32_t value_base = GetValueBase(value);
|
||||
TraceKeyDebugInfo key_info;
|
||||
Status status =
|
||||
ParseTracedKey(key, "unable to parse key", &key_id, &key_info);
|
||||
if (status.ok()) {
|
||||
const int64_t expected_key_id = static_cast<int64_t>(key_id);
|
||||
const uint32_t value_base = GetValueBase(value);
|
||||
|
||||
if (buffered_writes_ != nullptr) {
|
||||
return WriteBatchInternal::TimedPut(buffered_writes_.get(),
|
||||
column_family_id, key, value,
|
||||
write_unix_time);
|
||||
}
|
||||
bool should_buffer_write = !(buffered_writes_ == nullptr);
|
||||
if (should_buffer_write) {
|
||||
MaybeLogKeyOperation(
|
||||
"TimedPutCF", column_family_id, true /* buffered */, key_info,
|
||||
"value_base=" + std::to_string(value_base) +
|
||||
" value_size=" + std::to_string(value.size()) +
|
||||
" write_unix_time=" + std::to_string(write_unix_time));
|
||||
return WriteBatchInternal::TimedPut(buffered_writes_.get(),
|
||||
column_family_id, key, value,
|
||||
write_unix_time);
|
||||
}
|
||||
|
||||
state_->SyncPut(column_family_id, expected_key_id, value_base);
|
||||
NoteWriteOpApplied();
|
||||
return Status::OK();
|
||||
const ExpectedValue before =
|
||||
state_->Get(column_family_id, expected_key_id);
|
||||
state_->SyncPut(column_family_id, expected_key_id, value_base);
|
||||
const ExpectedValue after =
|
||||
state_->Get(column_family_id, expected_key_id);
|
||||
NoteWriteOpApplied();
|
||||
MaybeLogKeyOperation(
|
||||
"TimedPutCF", column_family_id, false /* buffered */, key_info,
|
||||
"value_base=" + std::to_string(value_base) +
|
||||
" value_size=" + std::to_string(value.size()) +
|
||||
" write_unix_time=" + std::to_string(write_unix_time),
|
||||
&before, &after);
|
||||
status = Status::OK();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status PutEntityCF(uint32_t column_family_id, const Slice& key_with_ts,
|
||||
@@ -568,34 +636,52 @@ class ExpectedStateTraceRecordHandler : public TraceRecord::Handler,
|
||||
StripTimestampFromUserKey(key_with_ts, FLAGS_user_timestamp_size);
|
||||
|
||||
uint64_t key_id = 0;
|
||||
Status status = ParseTracedKey(key, "Unable to parse key", &key_id);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
const int64_t expected_key_id = static_cast<int64_t>(key_id);
|
||||
TraceKeyDebugInfo key_info;
|
||||
Status status =
|
||||
ParseTracedKey(key, "Unable to parse key", &key_id, &key_info);
|
||||
if (status.ok()) {
|
||||
const int64_t expected_key_id = static_cast<int64_t>(key_id);
|
||||
|
||||
Slice entity_copy = entity;
|
||||
WideColumns columns;
|
||||
if (!WideColumnSerialization::Deserialize(entity_copy, columns).ok()) {
|
||||
return Status::Corruption("Unable to deserialize entity",
|
||||
entity.ToString(/* hex */ true));
|
||||
}
|
||||
Slice entity_copy = entity;
|
||||
WideColumns columns;
|
||||
if (!WideColumnSerialization::Deserialize(entity_copy, columns).ok()) {
|
||||
return Status::Corruption("Unable to deserialize entity",
|
||||
entity.ToString(/* hex */ true));
|
||||
}
|
||||
|
||||
if (!VerifyWideColumns(columns)) {
|
||||
return Status::Corruption("Wide columns in entity inconsistent",
|
||||
entity.ToString(/* hex */ true));
|
||||
}
|
||||
if (!VerifyWideColumns(columns)) {
|
||||
return Status::Corruption("Wide columns in entity inconsistent",
|
||||
entity.ToString(/* hex */ true));
|
||||
}
|
||||
|
||||
if (buffered_writes_) {
|
||||
return WriteBatchInternal::PutEntity(buffered_writes_.get(),
|
||||
column_family_id, key, columns);
|
||||
}
|
||||
if (buffered_writes_) {
|
||||
MaybeLogKeyOperation(
|
||||
"PutEntityCF", column_family_id, true /* buffered */, key_info,
|
||||
"entity_size=" + std::to_string(entity.size()) +
|
||||
" num_columns=" + std::to_string(columns.size()));
|
||||
return WriteBatchInternal::PutEntity(buffered_writes_.get(),
|
||||
column_family_id, key, columns);
|
||||
}
|
||||
|
||||
const uint32_t value_base =
|
||||
GetValueBase(WideColumnsHelper::GetDefaultColumn(columns));
|
||||
state_->SyncPut(column_family_id, expected_key_id, value_base);
|
||||
NoteWriteOpApplied();
|
||||
return Status::OK();
|
||||
const uint32_t value_base =
|
||||
GetValueBase(WideColumnsHelper::GetDefaultColumn(columns));
|
||||
|
||||
const ExpectedValue before =
|
||||
state_->Get(column_family_id, expected_key_id);
|
||||
state_->SyncPut(column_family_id, expected_key_id, value_base);
|
||||
const ExpectedValue after =
|
||||
state_->Get(column_family_id, expected_key_id);
|
||||
NoteWriteOpApplied();
|
||||
MaybeLogKeyOperation(
|
||||
"PutEntityCF", column_family_id, false /* buffered */, key_info,
|
||||
"entity_size=" + std::to_string(entity.size()) +
|
||||
" num_columns=" + std::to_string(columns.size()) +
|
||||
" default_value_base=" + std::to_string(value_base),
|
||||
&before, &after);
|
||||
|
||||
status = Status::OK();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DeleteCF(uint32_t column_family_id,
|
||||
@@ -603,20 +689,31 @@ class ExpectedStateTraceRecordHandler : public TraceRecord::Handler,
|
||||
Slice key =
|
||||
StripTimestampFromUserKey(key_with_ts, FLAGS_user_timestamp_size);
|
||||
uint64_t key_id = 0;
|
||||
Status status = ParseTracedKey(key, "unable to parse key", &key_id);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
const int64_t expected_key_id = static_cast<int64_t>(key_id);
|
||||
TraceKeyDebugInfo key_info;
|
||||
Status status =
|
||||
ParseTracedKey(key, "unable to parse key", &key_id, &key_info);
|
||||
if (status.ok()) {
|
||||
const int64_t expected_key_id = static_cast<int64_t>(key_id);
|
||||
|
||||
if (buffered_writes_ != nullptr) {
|
||||
return WriteBatchInternal::Delete(buffered_writes_.get(),
|
||||
column_family_id, key);
|
||||
}
|
||||
bool should_buffer_write = !(buffered_writes_ == nullptr);
|
||||
if (should_buffer_write) {
|
||||
MaybeLogKeyOperation("DeleteCF", column_family_id, true /* buffered */,
|
||||
key_info, "");
|
||||
return WriteBatchInternal::Delete(buffered_writes_.get(),
|
||||
column_family_id, key);
|
||||
}
|
||||
|
||||
state_->SyncDelete(column_family_id, expected_key_id);
|
||||
NoteWriteOpApplied();
|
||||
return Status::OK();
|
||||
const ExpectedValue before =
|
||||
state_->Get(column_family_id, expected_key_id);
|
||||
state_->SyncDelete(column_family_id, expected_key_id);
|
||||
const ExpectedValue after =
|
||||
state_->Get(column_family_id, expected_key_id);
|
||||
NoteWriteOpApplied();
|
||||
MaybeLogKeyOperation("DeleteCF", column_family_id, false /* buffered */,
|
||||
key_info, "", &before, &after);
|
||||
status = Status::OK();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status SingleDeleteCF(uint32_t column_family_id,
|
||||
@@ -645,25 +742,56 @@ class ExpectedStateTraceRecordHandler : public TraceRecord::Handler,
|
||||
StripTimestampFromUserKey(end_key_with_ts, FLAGS_user_timestamp_size);
|
||||
uint64_t begin_key_id = 0;
|
||||
uint64_t end_key_id = 0;
|
||||
Status status =
|
||||
ParseTracedKey(begin_key, "unable to parse begin key", &begin_key_id);
|
||||
TraceKeyDebugInfo begin_info;
|
||||
TraceKeyDebugInfo end_info;
|
||||
Status status = ParseTracedKey(begin_key, "unable to parse begin key",
|
||||
&begin_key_id, &begin_info);
|
||||
if (status.ok()) {
|
||||
status = ParseTracedKey(end_key, "unable to parse end key", &end_key_id);
|
||||
}
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
status = ParseTracedKey(end_key, "unable to parse end key", &end_key_id,
|
||||
&end_info);
|
||||
}
|
||||
if (status.ok()) {
|
||||
bool should_buffer_write = !(buffered_writes_ == nullptr);
|
||||
if (should_buffer_write) {
|
||||
const uint64_t affected_keys =
|
||||
end_key_id > begin_key_id ? end_key_id - begin_key_id : 0;
|
||||
MaybeLogRangeOperation(
|
||||
"DeleteRangeCF", column_family_id, true /* buffered */, begin_info,
|
||||
end_info,
|
||||
"affected_keys=" + std::to_string(affected_keys) +
|
||||
" inverted_range=" +
|
||||
std::to_string(end_key_id < begin_key_id ? 1 : 0));
|
||||
return WriteBatchInternal::DeleteRange(
|
||||
buffered_writes_.get(), column_family_id, begin_key, end_key);
|
||||
}
|
||||
|
||||
if (buffered_writes_ != nullptr) {
|
||||
return WriteBatchInternal::DeleteRange(
|
||||
buffered_writes_.get(), column_family_id, begin_key, end_key);
|
||||
const bool focus_in_range = FocusKeyInRange(begin_key_id, end_key_id);
|
||||
const uint64_t affected_keys =
|
||||
end_key_id > begin_key_id ? end_key_id - begin_key_id : 0;
|
||||
ExpectedValue focus_before;
|
||||
ExpectedValue focus_after;
|
||||
if (focus_in_range) {
|
||||
focus_before = state_->Get(column_family_id, debug_focus_key_);
|
||||
}
|
||||
state_->SyncDeleteRange(column_family_id,
|
||||
static_cast<int64_t>(begin_key_id),
|
||||
static_cast<int64_t>(end_key_id));
|
||||
if (focus_in_range) {
|
||||
focus_after = state_->Get(column_family_id, debug_focus_key_);
|
||||
}
|
||||
NoteWriteOpApplied();
|
||||
MaybeLogRangeOperation(
|
||||
"DeleteRangeCF", column_family_id, false /* buffered */, begin_info,
|
||||
end_info,
|
||||
"affected_keys=" + std::to_string(affected_keys) +
|
||||
" inverted_range=" +
|
||||
std::to_string(end_key_id < begin_key_id ? 1 : 0) +
|
||||
" focus_in_range=" + std::to_string(focus_in_range ? 1 : 0),
|
||||
focus_in_range ? &focus_before : nullptr,
|
||||
focus_in_range ? &focus_after : nullptr);
|
||||
status = Status::OK();
|
||||
}
|
||||
|
||||
state_->SyncDeleteRange(column_family_id,
|
||||
static_cast<int64_t>(begin_key_id),
|
||||
static_cast<int64_t>(end_key_id));
|
||||
NoteWriteOpApplied();
|
||||
return Status::OK();
|
||||
return status;
|
||||
}
|
||||
|
||||
Status MergeCF(uint32_t column_family_id, const Slice& key_with_ts,
|
||||
@@ -685,26 +813,40 @@ class ExpectedStateTraceRecordHandler : public TraceRecord::Handler,
|
||||
Slice key =
|
||||
StripTimestampFromUserKey(key_with_ts, FLAGS_user_timestamp_size);
|
||||
uint64_t key_id = 0;
|
||||
Status status = ParseTracedKey(key, "unable to parse key", &key_id);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
const int64_t expected_key_id = static_cast<int64_t>(key_id);
|
||||
TraceKeyDebugInfo key_info;
|
||||
Status status =
|
||||
ParseTracedKey(key, "unable to parse key", &key_id, &key_info);
|
||||
if (status.ok()) {
|
||||
const int64_t expected_key_id = static_cast<int64_t>(key_id);
|
||||
|
||||
if (buffered_writes_ != nullptr) {
|
||||
return WriteBatchInternal::PutBlobIndex(buffered_writes_.get(),
|
||||
column_family_id, key, value);
|
||||
}
|
||||
bool should_buffer_write = !(buffered_writes_ == nullptr);
|
||||
if (should_buffer_write) {
|
||||
MaybeLogKeyOperation("PutBlobIndexCF", column_family_id,
|
||||
true /* buffered */, key_info,
|
||||
"blob_index_size=" + std::to_string(value.size()));
|
||||
return WriteBatchInternal::PutBlobIndex(buffered_writes_.get(),
|
||||
column_family_id, key, value);
|
||||
}
|
||||
|
||||
// Blob direct-write traces record the transformed BlobIndex write rather
|
||||
// than the original value bytes. For expected-state replay we only need
|
||||
// the logical effect of "another put to this key", and db_stress values
|
||||
// advance deterministically by one value_base per committed write.
|
||||
const uint32_t value_base =
|
||||
state_->Get(column_family_id, expected_key_id).NextValueBase();
|
||||
state_->SyncPut(column_family_id, expected_key_id, value_base);
|
||||
NoteWriteOpApplied();
|
||||
return Status::OK();
|
||||
// Blob direct-write traces record the transformed BlobIndex write rather
|
||||
// than the original value bytes. For expected-state replay we only need
|
||||
// the logical effect of "another put to this key", and db_stress values
|
||||
// advance deterministically by one value_base per committed write.
|
||||
const ExpectedValue before =
|
||||
state_->Get(column_family_id, expected_key_id);
|
||||
const uint32_t value_base = before.NextValueBase();
|
||||
state_->SyncPut(column_family_id, expected_key_id, value_base);
|
||||
const ExpectedValue after =
|
||||
state_->Get(column_family_id, expected_key_id);
|
||||
NoteWriteOpApplied();
|
||||
MaybeLogKeyOperation(
|
||||
"PutBlobIndexCF", column_family_id, false /* buffered */, key_info,
|
||||
"blob_index_size=" + std::to_string(value.size()) +
|
||||
" derived_value_base=" + std::to_string(value_base),
|
||||
&before, &after);
|
||||
status = Status::OK();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status MarkBeginPrepare(bool = false) override {
|
||||
@@ -749,15 +891,162 @@ class ExpectedStateTraceRecordHandler : public TraceRecord::Handler,
|
||||
}
|
||||
|
||||
private:
|
||||
bool HasFocusKey() const { return debug_focus_key_ >= 0; }
|
||||
|
||||
bool FocusKeyInRange(uint64_t begin_key_id, uint64_t end_key_id) const {
|
||||
return HasFocusKey() &&
|
||||
begin_key_id <= static_cast<uint64_t>(debug_focus_key_) &&
|
||||
static_cast<uint64_t>(debug_focus_key_) < end_key_id;
|
||||
}
|
||||
|
||||
void MaybeNoteFocusKeyHit(bool hit) {
|
||||
if (hit) {
|
||||
++focus_key_op_hits_;
|
||||
}
|
||||
}
|
||||
|
||||
void MaybeEmitDebugLog(const std::string& line) {
|
||||
if (!debug_enabled_) {
|
||||
return;
|
||||
}
|
||||
if (emitted_debug_logs_ >= debug_max_logs_) {
|
||||
++suppressed_debug_logs_;
|
||||
return;
|
||||
}
|
||||
++emitted_debug_logs_;
|
||||
fprintf(stdout, "[expected_state_trace_debug] %s\n", line.c_str());
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
TraceKeyDebugInfo BuildTraceKeyDebugInfo(const std::string& raw_key,
|
||||
bool parse_ok,
|
||||
uint64_t parsed_key_id) {
|
||||
TraceKeyDebugInfo info;
|
||||
if (!debug_enabled_) {
|
||||
return info;
|
||||
}
|
||||
|
||||
info.raw_key = raw_key;
|
||||
info.raw_key_hex = Slice(raw_key).ToString(/* hex */ true);
|
||||
info.parse_ok = parse_ok;
|
||||
info.trailing_bytes = raw_key.size() % sizeof(uint64_t);
|
||||
info.trailing_xs = CountTrailingXs(raw_key);
|
||||
info.raw_matches_focus_key =
|
||||
HasFocusKey() && raw_key == debug_focus_key_raw_;
|
||||
|
||||
if (!parse_ok) {
|
||||
++key_decode_failures_;
|
||||
return info;
|
||||
}
|
||||
|
||||
info.parsed_key_id = parsed_key_id;
|
||||
info.roundtrip_key = Key(static_cast<int64_t>(parsed_key_id));
|
||||
info.roundtrip_key_hex = Slice(info.roundtrip_key).ToString(/* hex */ true);
|
||||
info.roundtrip_matches_raw = raw_key == info.roundtrip_key;
|
||||
info.parsed_matches_focus_key =
|
||||
HasFocusKey() &&
|
||||
parsed_key_id == static_cast<uint64_t>(debug_focus_key_);
|
||||
info.roundtrip_matches_focus_key =
|
||||
HasFocusKey() && info.roundtrip_key == debug_focus_key_raw_;
|
||||
|
||||
if (!info.roundtrip_matches_raw) {
|
||||
++key_roundtrip_mismatches_;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
Status ParseTracedKey(const Slice& key, const char* error_msg,
|
||||
uint64_t* key_id) {
|
||||
uint64_t* key_id, TraceKeyDebugInfo* debug_info) {
|
||||
const std::string raw_key = key.ToString();
|
||||
if (!GetIntVal(raw_key, key_id)) {
|
||||
const bool parse_ok = GetIntVal(raw_key, key_id);
|
||||
if (debug_enabled_) {
|
||||
*debug_info =
|
||||
BuildTraceKeyDebugInfo(raw_key, parse_ok, parse_ok ? *key_id : 0);
|
||||
if (!parse_ok && (!HasFocusKey() || debug_info->MatchesFocusKey())) {
|
||||
std::ostringstream oss;
|
||||
oss << "parse_failure error=\"" << error_msg << "\" "
|
||||
<< DescribeTraceKeyDebugInfo(*debug_info);
|
||||
MaybeEmitDebugLog(oss.str());
|
||||
}
|
||||
}
|
||||
if (!parse_ok) {
|
||||
return Status::Corruption(error_msg, raw_key);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
bool ShouldLogKeyOperation(const TraceKeyDebugInfo& info) {
|
||||
if (!debug_enabled_) {
|
||||
return false;
|
||||
}
|
||||
const bool focus_hit = info.MatchesFocusKey();
|
||||
MaybeNoteFocusKeyHit(focus_hit);
|
||||
return !HasFocusKey() || focus_hit;
|
||||
}
|
||||
|
||||
bool ShouldLogRangeOperation(const TraceKeyDebugInfo& begin_info,
|
||||
const TraceKeyDebugInfo& end_info) {
|
||||
if (!debug_enabled_) {
|
||||
return false;
|
||||
}
|
||||
const bool focus_hit =
|
||||
begin_info.MatchesFocusKey() || end_info.MatchesFocusKey() ||
|
||||
(begin_info.parse_ok && end_info.parse_ok &&
|
||||
FocusKeyInRange(begin_info.parsed_key_id, end_info.parsed_key_id));
|
||||
MaybeNoteFocusKeyHit(focus_hit);
|
||||
return !HasFocusKey() || focus_hit;
|
||||
}
|
||||
|
||||
void MaybeLogKeyOperation(const char* op, uint32_t column_family_id,
|
||||
bool buffered, const TraceKeyDebugInfo& key_info,
|
||||
const std::string& details,
|
||||
const ExpectedValue* before = nullptr,
|
||||
const ExpectedValue* after = nullptr) {
|
||||
if (!ShouldLogKeyOperation(key_info)) {
|
||||
return;
|
||||
}
|
||||
std::ostringstream oss;
|
||||
oss << op << " cf=" << column_family_id << " buffered=" << buffered << " "
|
||||
<< DescribeTraceKeyDebugInfo(key_info);
|
||||
if (!details.empty()) {
|
||||
oss << " " << details;
|
||||
}
|
||||
if (before != nullptr) {
|
||||
oss << " before=" << DescribeExpectedValue(*before);
|
||||
}
|
||||
if (after != nullptr) {
|
||||
oss << " after=" << DescribeExpectedValue(*after);
|
||||
}
|
||||
MaybeEmitDebugLog(oss.str());
|
||||
}
|
||||
|
||||
void MaybeLogRangeOperation(const char* op, uint32_t column_family_id,
|
||||
bool buffered,
|
||||
const TraceKeyDebugInfo& begin_info,
|
||||
const TraceKeyDebugInfo& end_info,
|
||||
const std::string& details,
|
||||
const ExpectedValue* focus_before = nullptr,
|
||||
const ExpectedValue* focus_after = nullptr) {
|
||||
if (!ShouldLogRangeOperation(begin_info, end_info)) {
|
||||
return;
|
||||
}
|
||||
std::ostringstream oss;
|
||||
oss << op << " cf=" << column_family_id << " buffered=" << buffered
|
||||
<< " begin=" << DescribeTraceKeyDebugInfo(begin_info)
|
||||
<< " end=" << DescribeTraceKeyDebugInfo(end_info);
|
||||
if (!details.empty()) {
|
||||
oss << " " << details;
|
||||
}
|
||||
if (focus_before != nullptr) {
|
||||
oss << " focus_before=" << DescribeExpectedValue(*focus_before);
|
||||
}
|
||||
if (focus_after != nullptr) {
|
||||
oss << " focus_after=" << DescribeExpectedValue(*focus_after);
|
||||
}
|
||||
MaybeEmitDebugLog(oss.str());
|
||||
}
|
||||
|
||||
void NoteWriteOpApplied() {
|
||||
++num_write_ops_;
|
||||
assert(num_write_ops_ <= max_write_ops_);
|
||||
@@ -766,6 +1055,15 @@ class ExpectedStateTraceRecordHandler : public TraceRecord::Handler,
|
||||
uint64_t num_write_ops_ = 0;
|
||||
uint64_t max_write_ops_;
|
||||
ExpectedState* state_;
|
||||
bool debug_enabled_;
|
||||
int64_t debug_focus_key_;
|
||||
std::string debug_focus_key_raw_;
|
||||
uint64_t debug_max_logs_;
|
||||
uint64_t key_decode_failures_ = 0;
|
||||
uint64_t key_roundtrip_mismatches_ = 0;
|
||||
uint64_t focus_key_op_hits_ = 0;
|
||||
uint64_t emitted_debug_logs_ = 0;
|
||||
uint64_t suppressed_debug_logs_ = 0;
|
||||
std::unordered_map<std::string, std::unique_ptr<WriteBatch>>
|
||||
xid_to_buffered_writes_;
|
||||
std::unique_ptr<WriteBatch> buffered_writes_;
|
||||
@@ -779,6 +1077,7 @@ Status FileExpectedStateManager::Restore(DB* db) {
|
||||
if (seqno < saved_seqno_) {
|
||||
return Status::Corruption("DB is older than any restorable expected state");
|
||||
}
|
||||
const bool trace_debug = FLAGS_expected_state_trace_debug;
|
||||
const uint64_t replay_write_ops = seqno - saved_seqno_;
|
||||
|
||||
std::string state_filename =
|
||||
@@ -794,6 +1093,24 @@ Status FileExpectedStateManager::Restore(DB* db) {
|
||||
std::to_string(saved_seqno_) + kTraceFilenameSuffix;
|
||||
std::string trace_file_path = GetPathForFilename(trace_filename);
|
||||
|
||||
if (trace_debug) {
|
||||
std::string focus_key_hex = "<unset>";
|
||||
if (FLAGS_expected_state_trace_debug_key >= 0) {
|
||||
focus_key_hex = Slice(Key(FLAGS_expected_state_trace_debug_key))
|
||||
.ToString(/* hex */ true);
|
||||
}
|
||||
fprintf(stdout,
|
||||
"[expected_state_trace_debug] restore_begin saved_seqno=%" PRIu64
|
||||
" db_seqno=%" PRIu64 " replay_write_ops=%" PRIu64
|
||||
" state_path=%s trace_path=%s focus_key=%" PRIi64
|
||||
" focus_key_hex=%s max_logs=%d\n",
|
||||
static_cast<uint64_t>(saved_seqno_), static_cast<uint64_t>(seqno),
|
||||
replay_write_ops, state_file_path.c_str(), trace_file_path.c_str(),
|
||||
FLAGS_expected_state_trace_debug_key, focus_key_hex.c_str(),
|
||||
FLAGS_expected_state_trace_debug_max_logs);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
std::unique_ptr<TraceReader> trace_reader;
|
||||
Status s = NewFileTraceReader(Env::Default(), EnvOptions(), trace_file_path,
|
||||
&trace_reader);
|
||||
@@ -801,6 +1118,13 @@ Status FileExpectedStateManager::Restore(DB* db) {
|
||||
std::string persisted_seqno_file_path = GetPathForFilename(
|
||||
kPersistedSeqnoBasename + kPersistedSeqnoFilenameSuffix);
|
||||
|
||||
uint64_t replayed_write_ops = 0;
|
||||
uint64_t key_decode_failures = 0;
|
||||
uint64_t key_roundtrip_mismatches = 0;
|
||||
uint64_t focus_key_op_hits = 0;
|
||||
uint64_t logs_emitted = 0;
|
||||
uint64_t logs_suppressed = 0;
|
||||
|
||||
if (s.ok()) {
|
||||
// We are going to replay on top of "`seqno`.state" to create a new
|
||||
// "LATEST.state". Start off by creating a tempfile so we can later make the
|
||||
@@ -821,8 +1145,8 @@ Status FileExpectedStateManager::Restore(DB* db) {
|
||||
s = state->Open(false /* create */);
|
||||
}
|
||||
if (s.ok()) {
|
||||
handler.reset(
|
||||
new ExpectedStateTraceRecordHandler(replay_write_ops, state.get()));
|
||||
handler.reset(new ExpectedStateTraceRecordHandler(seqno - saved_seqno_,
|
||||
state.get()));
|
||||
// TODO(ajkr): An API limitation requires we provide `handles` although
|
||||
// they will be unused since we only use the replayer for reading records.
|
||||
// Just give a default CFH for now to satisfy the requirement.
|
||||
@@ -837,9 +1161,15 @@ Status FileExpectedStateManager::Restore(DB* db) {
|
||||
std::unique_ptr<TraceRecord> record;
|
||||
s = replayer->Next(&record);
|
||||
if (!s.ok()) {
|
||||
const bool handler_done = handler != nullptr && handler->IsDone();
|
||||
const bool tolerated_tail_corruption = s.IsCorruption() && handler_done;
|
||||
if (tolerated_tail_corruption) {
|
||||
if (trace_debug) {
|
||||
fprintf(stdout,
|
||||
"[expected_state_trace_debug] restore_replay_next status=%s "
|
||||
"handler_done=%d\n",
|
||||
s.ToString().c_str(),
|
||||
handler != nullptr && handler->IsDone());
|
||||
fflush(stdout);
|
||||
}
|
||||
if (s.IsCorruption() && handler->IsDone()) {
|
||||
// There could be a corruption reading the tail record of the trace
|
||||
// due to `db_stress` crashing while writing it. It shouldn't matter
|
||||
// as long as we already found all the write ops we need to catch up
|
||||
@@ -860,8 +1190,29 @@ Status FileExpectedStateManager::Restore(DB* db) {
|
||||
s = Status::Corruption(
|
||||
"Trace ended before replaying all expected write ops",
|
||||
std::to_string(handler->NumWriteOps()) + " < " +
|
||||
std::to_string(replay_write_ops));
|
||||
std::to_string(seqno - saved_seqno_));
|
||||
}
|
||||
if (handler) {
|
||||
replayed_write_ops = handler->NumWriteOps();
|
||||
key_decode_failures = handler->NumKeyDecodeFailures();
|
||||
key_roundtrip_mismatches = handler->NumKeyRoundtripMismatches();
|
||||
focus_key_op_hits = handler->NumFocusKeyOpHits();
|
||||
logs_emitted = handler->NumLogsEmitted();
|
||||
logs_suppressed = handler->NumLogsSuppressed();
|
||||
}
|
||||
}
|
||||
|
||||
if (trace_debug) {
|
||||
fprintf(stdout,
|
||||
"[expected_state_trace_debug] restore_replay_summary status=%s "
|
||||
"replayed_write_ops=%" PRIu64 "/%" PRIu64
|
||||
" key_decode_failures=%" PRIu64 " key_roundtrip_mismatches=%" PRIu64
|
||||
" focus_key_op_hits=%" PRIu64 " logs_emitted=%" PRIu64
|
||||
" logs_suppressed=%" PRIu64 "\n",
|
||||
s.ToString().c_str(), replayed_write_ops, replay_write_ops,
|
||||
key_decode_failures, key_roundtrip_mismatches, focus_key_op_hits,
|
||||
logs_emitted, logs_suppressed);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -909,6 +1260,11 @@ Status FileExpectedStateManager::Restore(DB* db) {
|
||||
saved_seqno_ = kMaxSequenceNumber;
|
||||
}
|
||||
}
|
||||
if (trace_debug) {
|
||||
fprintf(stdout, "[expected_state_trace_debug] restore_end status=%s\n",
|
||||
s.ToString().c_str());
|
||||
fflush(stdout);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -2021,22 +2021,7 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
if (IsErrorInjectedAndRetryable(s)) {
|
||||
assert(!initial_wal_write_may_succeed);
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
PrintWriteRecoveryWaitTimeIfNeeded(
|
||||
raw_env, initial_write_s, initial_wal_write_may_succeed,
|
||||
wait_for_recover_start_time, "TestPut");
|
||||
pending_expected_value.Commit();
|
||||
thread->stats.AddBytesForWrites(1, sz);
|
||||
PrintKeyValue(rand_column_family, static_cast<uint32_t>(rand_key), value,
|
||||
sz);
|
||||
}
|
||||
// Single write verification point (no_batched owns it): on success a
|
||||
// read-back after Commit; on failure the op status before the fail-fast.
|
||||
// No-op unless CPU-corruption verification is on.
|
||||
MaybeVerifyCpuCorruption(thread, "put", s);
|
||||
if (!s.ok()) {
|
||||
if (FLAGS_inject_error_severity == 2) {
|
||||
} else if (FLAGS_inject_error_severity == 2) {
|
||||
if (!is_db_stopped_ && s.severity() >= Status::Severity::kFatalError) {
|
||||
is_db_stopped_ = true;
|
||||
} else if (!is_db_stopped_ ||
|
||||
@@ -2048,6 +2033,14 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
fprintf(stderr, "put or merge error: %s\n", s.ToString().c_str());
|
||||
thread->shared->SafeTerminate();
|
||||
}
|
||||
} else {
|
||||
PrintWriteRecoveryWaitTimeIfNeeded(
|
||||
raw_env, initial_write_s, initial_wal_write_may_succeed,
|
||||
wait_for_recover_start_time, "TestPut");
|
||||
pending_expected_value.Commit();
|
||||
thread->stats.AddBytesForWrites(1, sz);
|
||||
PrintKeyValue(rand_column_family, static_cast<uint32_t>(rand_key), value,
|
||||
sz);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -2133,17 +2126,7 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
if (IsErrorInjectedAndRetryable(s)) {
|
||||
assert(!initial_wal_write_may_succeed);
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
PrintWriteRecoveryWaitTimeIfNeeded(
|
||||
raw_env, initial_write_s, initial_wal_write_may_succeed,
|
||||
wait_for_recover_start_time, "TestDelete");
|
||||
pending_expected_value.Commit();
|
||||
thread->stats.AddDeletes(1);
|
||||
}
|
||||
MaybeVerifyCpuCorruption(thread, "delete", s);
|
||||
if (!s.ok()) {
|
||||
if (FLAGS_inject_error_severity == 2) {
|
||||
} else if (FLAGS_inject_error_severity == 2) {
|
||||
if (!is_db_stopped_ &&
|
||||
s.severity() >= Status::Severity::kFatalError) {
|
||||
is_db_stopped_ = true;
|
||||
@@ -2156,6 +2139,12 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
fprintf(stderr, "delete error: %s\n", s.ToString().c_str());
|
||||
thread->shared->SafeTerminate();
|
||||
}
|
||||
} else {
|
||||
PrintWriteRecoveryWaitTimeIfNeeded(
|
||||
raw_env, initial_write_s, initial_wal_write_may_succeed,
|
||||
wait_for_recover_start_time, "TestDelete");
|
||||
pending_expected_value.Commit();
|
||||
thread->stats.AddDeletes(1);
|
||||
}
|
||||
} else {
|
||||
PendingExpectedValue pending_expected_value =
|
||||
@@ -2209,17 +2198,7 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
if (IsErrorInjectedAndRetryable(s)) {
|
||||
assert(!initial_wal_write_may_succeed);
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
PrintWriteRecoveryWaitTimeIfNeeded(
|
||||
raw_env, initial_write_s, initial_wal_write_may_succeed,
|
||||
wait_for_recover_start_time, "TestDelete");
|
||||
pending_expected_value.Commit();
|
||||
thread->stats.AddSingleDeletes(1);
|
||||
}
|
||||
MaybeVerifyCpuCorruption(thread, "singledelete", s);
|
||||
if (!s.ok()) {
|
||||
if (FLAGS_inject_error_severity == 2) {
|
||||
} else if (FLAGS_inject_error_severity == 2) {
|
||||
if (!is_db_stopped_ &&
|
||||
s.severity() >= Status::Severity::kFatalError) {
|
||||
is_db_stopped_ = true;
|
||||
@@ -2232,6 +2211,12 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
fprintf(stderr, "single delete error: %s\n", s.ToString().c_str());
|
||||
thread->shared->SafeTerminate();
|
||||
}
|
||||
} else {
|
||||
PrintWriteRecoveryWaitTimeIfNeeded(
|
||||
raw_env, initial_write_s, initial_wal_write_may_succeed,
|
||||
wait_for_recover_start_time, "TestDelete");
|
||||
pending_expected_value.Commit();
|
||||
thread->stats.AddSingleDeletes(1);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
@@ -2307,24 +2292,7 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
if (IsErrorInjectedAndRetryable(s)) {
|
||||
assert(!initial_wal_write_may_succeed);
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
PrintWriteRecoveryWaitTimeIfNeeded(
|
||||
raw_env, initial_write_s, initial_wal_write_may_succeed,
|
||||
wait_for_recover_start_time, "TestDeleteRange");
|
||||
for (PendingExpectedValue& pending_expected_value :
|
||||
pending_expected_values) {
|
||||
pending_expected_value.Commit();
|
||||
}
|
||||
thread->stats.AddRangeDeletions(1);
|
||||
thread->stats.AddCoveredByRangeDeletions(covered);
|
||||
}
|
||||
// Single write verification point (no_batched owns it): on success a
|
||||
// read-back after Commit; on failure the op status before the fail-fast.
|
||||
// No-op unless CPU-corruption verification is on.
|
||||
MaybeVerifyCpuCorruption(thread, "deleterange", s);
|
||||
if (!s.ok()) {
|
||||
if (FLAGS_inject_error_severity == 2) {
|
||||
} else if (FLAGS_inject_error_severity == 2) {
|
||||
if (!is_db_stopped_ && s.severity() >= Status::Severity::kFatalError) {
|
||||
is_db_stopped_ = true;
|
||||
} else if (!is_db_stopped_ ||
|
||||
@@ -2336,6 +2304,16 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
fprintf(stderr, "delete range error: %s\n", s.ToString().c_str());
|
||||
thread->shared->SafeTerminate();
|
||||
}
|
||||
} else {
|
||||
PrintWriteRecoveryWaitTimeIfNeeded(
|
||||
raw_env, initial_write_s, initial_wal_write_may_succeed,
|
||||
wait_for_recover_start_time, "TestDeleteRange");
|
||||
for (PendingExpectedValue& pending_expected_value :
|
||||
pending_expected_values) {
|
||||
pending_expected_value.Commit();
|
||||
}
|
||||
thread->stats.AddRangeDeletions(1);
|
||||
thread->stats.AddCoveredByRangeDeletions(covered);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -2458,27 +2436,11 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
}
|
||||
|
||||
std::vector<ExternalSstFileInfo> file_infos(data_file_count);
|
||||
// Embedded blobs are only supported by block-based table format_version >=
|
||||
// 7 (the default db_stress table factory is block-based).
|
||||
const bool use_embedded_blobs =
|
||||
FLAGS_ingest_external_file_with_embedded_blobs &&
|
||||
FLAGS_format_version >= 7;
|
||||
// Set the embedded-blob threshold so that only the largest values generated
|
||||
// by GenerateValue() (size kRandomValueMaxFactor * value_size_mult) are
|
||||
// written as same-file blob records; smaller values stay inline.
|
||||
SstFileWriterEmbeddedBlobOptions embedded_blob_options;
|
||||
embedded_blob_options.min_blob_size =
|
||||
static_cast<uint64_t>(kRandomValueMaxFactor) * FLAGS_value_size_mult;
|
||||
std::deque<SstFileWriter> sst_file_writers;
|
||||
for (size_t file_idx = 0; s.ok() && file_idx < data_file_count;
|
||||
++file_idx) {
|
||||
sst_file_writers.emplace_back(EnvOptions(options_), options_);
|
||||
if (use_embedded_blobs) {
|
||||
s = sst_file_writers.back().OpenWithEmbeddedBlobs(
|
||||
data_filenames[file_idx], embedded_blob_options);
|
||||
} else {
|
||||
s = sst_file_writers.back().Open(data_filenames[file_idx]);
|
||||
}
|
||||
s = sst_file_writers.back().Open(data_filenames[file_idx]);
|
||||
}
|
||||
SstFileWriter standalone_rangedel_sst_file_writer(EnvOptions(options_),
|
||||
options_);
|
||||
|
||||
@@ -105,9 +105,3 @@ poojam23:
|
||||
joshkang97:
|
||||
full_name: Josh Kang
|
||||
fbid: 180489168460795
|
||||
|
||||
xbw:
|
||||
full_name: Xingbo Wang
|
||||
fbid: 1420025126077275
|
||||
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
---
|
||||
title: "FIFO KV-Ratio Compaction for BlobDB-Backed TTL Workloads"
|
||||
layout: post
|
||||
author: xbw
|
||||
category: blog
|
||||
---
|
||||
|
||||
RocksDB 11.0 added `CompactionOptionsFIFO::max_data_files_size` and `CompactionOptionsFIFO::use_kv_ratio_compaction` for a specific but important shape of workload: FIFO compaction, integrated BlobDB, large values, point lookups, and data that naturally expires by TTL or by a bounded data-size budget. The implementation was added in [pull request #14326](https://github.com/facebook/rocksdb/pull/14326).
|
||||
|
||||
The goal is to keep FIFO's low write amplification while reducing the read overhead caused by many small L0 files. The new picker uses the observed ratio between SST bytes and blob bytes to choose a stable target SST size, then moves L0 files through size tiers until they reach that target.
|
||||
|
||||
## Background: FIFO and BlobDB
|
||||
|
||||
FIFO compaction is designed for time-ordered or log-like data. All files remain in L0. When files become old enough for `ttl`, or when the configured size limit is exceeded, RocksDB drops the oldest files instead of rewriting them into lower levels. That is what keeps FIFO write amplification low.
|
||||
|
||||
Integrated BlobDB changes the file-size picture. Large values are stored in blob files, while SST files mostly contain keys, metadata, filters, indexes, and blob references. For point lookup workloads with large values, this can be a good fit: the SST portion can stay small and cached, and the read can fetch the large value from the blob file.
|
||||
|
||||
However, FIFO without intra-L0 compaction can accumulate many small L0 SST files. A point lookup may then need to probe many L0 files and many filters before finding the key. FIFO's optional intra-L0 compaction, enabled with `CompactionOptionsFIFO::allow_compaction`, addresses that by merging several small L0 SST files into fewer larger SST files. Intra-L0 compaction rewrites SST metadata only; it does not rewrite blob files.
|
||||
|
||||
## Why the old intra-L0 picker is not enough
|
||||
|
||||
The existing FIFO intra-L0 picker is cost based. It tries to reduce L0 file count while limiting how many bytes are rewritten for each file removed:
|
||||
|
||||
```
|
||||
compact_bytes_per_del_file = total_input_bytes / (num_input_files - 1)
|
||||
```
|
||||
|
||||
It keeps expanding the input set while that ratio improves, and it has a guard based on `write_buffer_size`:
|
||||
|
||||
```
|
||||
compact_bytes_per_del_file < 1.1 * write_buffer_size
|
||||
```
|
||||
|
||||
That guard assumes SST output size is roughly related to the memtable flush size. With BlobDB, that assumption often breaks. If values are large enough to be stored in blob files, a memtable flush can produce a large blob file and a much smaller SST file.
|
||||
|
||||

|
||||
{: style="display: block; margin-left: auto; margin-right: auto; width: 92%"}
|
||||
|
||||
*With large values in blob files, compacted SST files can remain far below `write_buffer_size`, so the old guard may keep allowing them into later intra-L0 compactions.*
|
||||
{: style="text-align: center"}
|
||||
|
||||
For example, if `write_buffer_size` is 64 MB, the old guard is about 70 MB. In a BlobDB-heavy workload, a fresh SST might be tens of KB, and an already compacted SST might still be only a few MB. From the old picker's point of view, that file can still look cheap to compact even after it has already grown to a useful size.
|
||||
|
||||
This has two practical consequences:
|
||||
|
||||
* SST files can keep growing without a workload-appropriate target.
|
||||
* FIFO dropping becomes less predictable, because TTL and size-based reclamation happen at file granularity.
|
||||
|
||||
The problem is not that an SST can be compacted more than once. The problem is that the old picker has no BlobDB-aware definition of when an L0 SST file should stop growing.
|
||||
|
||||
## The KV-ratio target
|
||||
|
||||
The new picker starts from a different question: given the current ratio of SST bytes to blob bytes, how large should a mature L0 SST file be when the database is near its configured data-size budget?
|
||||
|
||||
When `max_compaction_bytes` is left at `0`, RocksDB computes the target as:
|
||||
|
||||
```
|
||||
sst_ratio = total_l0_sst / (total_l0_sst + total_blob)
|
||||
target_sst_size = max_data_files_size * sst_ratio /
|
||||
level0_file_num_compaction_trigger
|
||||
```
|
||||
|
||||
If `max_compaction_bytes` is set to a non-zero value, RocksDB uses it directly as the target SST size. That is an advanced override for users who want to choose the graduated file size explicitly.
|
||||
|
||||
An SST file at or above the target is considered graduated. Graduated files stay in L0 until FIFO drops them; they are not repeatedly pulled into future KV-ratio intra-L0 compactions.
|
||||
|
||||
## Tiered merging
|
||||
|
||||
The picker does not try to merge every small SST directly to the final target size. Instead, it builds geometric size tiers using `level0_file_num_compaction_trigger` as the growth factor.
|
||||
|
||||
Suppose the target SST size is 1 MB and `level0_file_num_compaction_trigger` is 10. The tiers look like:
|
||||
|
||||
```
|
||||
10 KB -> 100 KB -> 1 MB
|
||||
```
|
||||
|
||||
Small flush outputs are merged into the first useful tier. Once enough files accumulate in that tier, they are merged into the next tier. Once a file reaches the target, it graduates.
|
||||
|
||||

|
||||
{: style="display: block; margin-left: auto; margin-right: auto; width: 92%"}
|
||||
|
||||
*The target comes from the observed SST/blob ratio and the configured data-size budget, not from `write_buffer_size`. Files move through bounded tiers and then graduate.*
|
||||
{: style="text-align: center"}
|
||||
|
||||
Tiering is the trade-off. It allows some intermediate L0 files to exist so that write amplification for SST metadata grows logarithmically with the ratio between the target size and the flush SST size, rather than forcing a large merge every time. Blob files are not rewritten by this intra-L0 compaction path, so when blob bytes dominate the database size, total write amplification remains close to FIFO's original design goal.
|
||||
|
||||
## Configuration
|
||||
|
||||
Use KV-ratio compaction when all of these are true:
|
||||
|
||||
* The column family uses FIFO compaction.
|
||||
* Integrated BlobDB stores a large fraction of total data.
|
||||
* SST files are much smaller than `write_buffer_size`.
|
||||
* Point lookups are sensitive to the number of L0 files.
|
||||
* Data expires through TTL or through a bounded data-size budget.
|
||||
|
||||
A typical configuration looks like this:
|
||||
|
||||
```cpp
|
||||
Options options;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
|
||||
// Use a combined SST + blob size budget for FIFO trimming.
|
||||
options.compaction_options_fifo.max_data_files_size =
|
||||
10ULL * 1024 * 1024 * 1024; // 10 GB
|
||||
|
||||
// Enable FIFO intra-L0 compaction and select the KV-ratio picker.
|
||||
options.compaction_options_fifo.allow_compaction = true;
|
||||
options.compaction_options_fifo.use_kv_ratio_compaction = true;
|
||||
|
||||
// Controls both the L0 file-count trigger and the tier growth factor.
|
||||
options.level0_file_num_compaction_trigger = 10;
|
||||
|
||||
// BlobDB stores values at or above min_blob_size in blob files.
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 1024;
|
||||
|
||||
// Optional: expire old files by age.
|
||||
options.ttl = 24 * 60 * 60;
|
||||
```
|
||||
|
||||
`max_data_files_size` is important for BlobDB-backed FIFO because it counts SST files and blob files together. When it is zero, FIFO uses the older `max_table_files_size` behavior, which only accounts for SST files. For KV-ratio compaction, `max_data_files_size` must be non-zero and should be at least as large as `max_table_files_size`; otherwise RocksDB falls back to the old cost-based intra-L0 picker.
|
||||
|
||||
Leave `max_compaction_bytes` at `0` unless you want to override the target SST size directly. With KV-ratio compaction, `0` means "derive the target from `max_data_files_size` and the observed SST/blob ratio."
|
||||
|
||||
## What to expect
|
||||
|
||||
In steady state, L0 should look less like a long tail of tiny flush SSTs and less like a few oversized SSTs. Instead, it should contain a bounded set of tier files plus graduated SST files near the target size.
|
||||
|
||||
The expected benefits are:
|
||||
|
||||
* Fewer tiny L0 SST files to probe during point lookup.
|
||||
* More predictable graduated SST sizes.
|
||||
* Smoother TTL and size-based FIFO dropping, since files are closer to a known target size.
|
||||
* Bounded SST metadata rewrite cost, while blob files are left untouched.
|
||||
|
||||
There are also trade-offs:
|
||||
|
||||
* The picker can leave intermediate tier files in L0, so steady-state L0 file count can be higher than a strategy that always merges directly to the final target.
|
||||
* SST metadata can still be rewritten across tiers.
|
||||
* This is intended for BlobDB-heavy FIFO workloads. If SST files are already close to `write_buffer_size`, the default cost-based FIFO intra-L0 picker may be simpler and sufficient.
|
||||
|
||||
KV-ratio compaction does not change FIFO's reclamation model. FIFO still reclaims space by dropping whole files. The new picker only shapes the SST side of L0 so reads do less work and file dropping is easier to reason about for BlobDB-backed TTL workloads.
|
||||
@@ -1,131 +0,0 @@
|
||||
---
|
||||
title: "Range Tombstone Conversion: Faster Scans Over Long Runs of Deletes"
|
||||
layout: post
|
||||
author: joshkang97
|
||||
category: blog
|
||||
---
|
||||
|
||||
RocksDB has historically been known for poor performance when tombstones accumulate. This has become a common problem within Meta, and the community has raised it as well. Here, we introduce an optimization that attempts to convert contiguous tombstones into a range tombstone during scans. As a result, instead of skipping through N tombstones, we only need to skip through a single range tombstone.
|
||||
|
||||
## Background: point tombstones and range tombstones
|
||||
|
||||
RocksDB is an LSM-tree, so a delete does not erase data in place. It writes a *tombstone*: a marker that shadows older values. A **point tombstone** (from `Delete` or `SingleDelete`) shadows exactly one key, while a **range tombstone** (from `DeleteRange`) shadows an entire half-open key range `[start, end)` with a single entry. Because newer data (usually) sits above older data in the tree, a read merges from the top down and takes the first entry it finds for a key, so a tombstone at an upper level hides any value for that key, or for any key in a range tombstone's span, at the levels below.
|
||||
|
||||

|
||||
{: style="display: block; margin-left: auto; margin-right: auto; width: 85%"}
|
||||
|
||||
*Point and range tombstones hide the values below them. The scan steps over each point tombstone but skips the range tombstone in one hop, and only the live keys (a, e, j) are returned to the user.*
|
||||
{: style="text-align: center"}
|
||||
|
||||
In both cases the space is reclaimed only later, during compaction, and only once the tombstone reaches the bottommost level with no live snapshot still needing it. Until then the tombstones sit in the way of reads. A scan never returns a deleted key, but to work out which keys are live it still has to step through every entry in key order. A point tombstone is just an ordinary entry, so the scan walks each one individually, and a run of N point tombstones costs N steps. A range tombstone is different: it is a single entry that covers the whole span, so when a scan reaches it, it can skip straight to the end of the range in one step instead of walking every key inside.
|
||||
|
||||
## Existing solutions
|
||||
|
||||
A bulk delete leaves a region of the key space full of tombstones, and until compaction removes them, every scan across that region pays for them. The iterator steps over each point tombstone in turn to reach the live keys, because a run of point tombstones is just ordinary consecutive keys with no shortcut. That is O(N) in the number of dead entries, paid on every scan, and N grows with the size of the deleted region. The natural question is whether we can simply get rid of the tombstones faster, and RocksDB already gives you a few tools for that.
|
||||
|
||||
**Deletion-triggered compaction.** `NewCompactOnDeletionCollectorFactory` marks an SST file for compaction once it holds a high density of tombstones (at least `D` deletions within any `N` consecutive entries, or a whole-file tombstone ratio above a threshold), so RocksDB schedules those files for compaction sooner than the normal LSM compaction schedule would.
|
||||
|
||||
**Scan-triggered memtable flush.** `memtable_op_scan_flush_trigger` (and its averaged sibling `memtable_avg_op_scan_flush_trigger`) flush the active memtable once a single iterator operation scans through too many invisible entries (tombstones or shadowed values), moving them into an SST where deletion-triggered compaction can then clean them up. The two are designed to be used together.
|
||||
|
||||
These all help, but they share the same limitations, because they all work by *removing* tombstones through flush and compaction:
|
||||
|
||||
* **They are reactive and asynchronous.** Compaction runs in the background; a scan happening right now, before compaction catches up, still pays the full cost.
|
||||
* **They add write amplification.** Every extra flush and compaction is more I/O and CPU spent rewriting data.
|
||||
* **They are defeated by a long-lived snapshot.** Compaction can only drop a tombstone when no snapshot still needs the data it shadows, so a backup, a long analytics query, or replication holding a snapshot forces RocksDB to keep every tombstone created during that snapshot's lifetime, no matter how aggressively you schedule compaction. This is both the case where tombstones pile up the most and the case these tools cannot fix.
|
||||
|
||||
## The naive fix
|
||||
|
||||
The tempting fix is to notice a long run of contiguous point tombstones (say, during a flush or a compaction) and simply replace it with a single range tombstone. One entry instead of N, and reads skip it. Done.
|
||||
|
||||
The catch is *visibility*. A flush sees a single memtable; a compaction sees only the levels it happens to be merging. From that partial vantage, a run of tombstones can look perfectly contiguous even though live keys sit between them at other levels. Remember that a point tombstone shadows only its own key, while a range tombstone shadows everything in its span. So collapsing a locally-contiguous run into a range tombstone can delete live data that the original point tombstones never touched.
|
||||
|
||||

|
||||
{: style="display: block; margin-left: auto; margin-right: auto; width: 90%"}
|
||||
|
||||
*On the LSM the L0 tombstones look contiguous, but live keys sit one level below; collapsing them into a range tombstone would delete that live data.*
|
||||
{: style="text-align: center"}
|
||||
|
||||
In the figure, L0 holds tombstones for 10, 20, and 30 that look contiguous, with 40 the next live key just past them, but 15, 25, and 35 are live one level down in L1. A naive range tombstone over `[10, 40)` would also erase 15, 25, and 35. Only something that can see the entire LSM at once can tell whether a run of deletes is *truly* contiguous.
|
||||
|
||||
## Application iterators
|
||||
|
||||
A read iterator merges every source (the mutable and immutable memtables and every SST level) into one ordered, snapshot-consistent stream of the keys actually visible at the read's sequence number. As long as the scan can observe every interior live key, this gives a global view of the database, so the iterator can safely decide whether a run of point tombstones can be converted into a range tombstone (at the same snapshot sequence number, of course). That full-visibility requirement is also why the feature disables itself for `table_filter`, partial-timestamp reads, and prefix iterators that are neither total-order nor bounded by `prefix_same_as_start`.
|
||||
|
||||
As the iterator moves forward (or backward) across contiguous point tombstones with no live key in between, it synthesizes a single range tombstone `[first_tombstone_key, next_live_key)` and inserts it into the mutable memtable using the same sequence number as the iterator is using to read the LSM.
|
||||
|
||||

|
||||
{: style="display: block; margin-left: auto; margin-right: auto; width: 95%"}
|
||||
|
||||
*Before: the run's point tombstones are spread across levels. After: the scan adds one range tombstone to the memtable that summarizes them. The point tombstones remain, but later scans hit the range tombstone first and skip the run.*
|
||||
{: style="text-align: center"}
|
||||
|
||||
The inserted tombstone is *logically redundant*: those keys were already deleted, so it changes no query's result and is purely a performance optimization. A conversion is not always guaranteed to succeed: there are numerous guards that discard one to avoid corrupting the database. See the comments on `min_tombstones_for_range_conversion` for details.
|
||||
|
||||
Because the tombstone lives in the memtable, it follows the normal lifecycle from there: it flushes to an SST and is eventually compacted away together with the point tombstones it summarizes, once no snapshot needs them. The cost is one redundant entry; the benefit is repaid across every scan in between.
|
||||
|
||||
## Enabling it
|
||||
|
||||
Range tombstone conversion landed in [PR #14448](https://github.com/facebook/rocksdb/pull/14448) (plus a few additional follow-up bug fixes) and is available in RocksDB 11.3.0 and later. It is controlled by the column-family option `min_tombstones_for_range_conversion`: the minimum length of a contiguous point-tombstone run that triggers a conversion. The default is `0`, which disables the feature. Set it to a positive value to turn it on; the option is dynamically changeable through `SetOptions`, so you can enable or tune it without reopening the database.
|
||||
|
||||
```cpp
|
||||
Options options;
|
||||
// Convert a run into a range tombstone once 100 contiguous
|
||||
// point tombstones are seen with no live key between them.
|
||||
options.min_tombstones_for_range_conversion = 100;
|
||||
|
||||
// Or change it dynamically on a running DB:
|
||||
db->SetOptions({{"min_tombstones_for_range_conversion", "100"}});
|
||||
```
|
||||
|
||||
Pick the threshold to match your workload: a higher value restricts conversions to genuinely long runs, where the payoff is largest, and avoids adding redundant tombstones for short gaps.
|
||||
|
||||
Two statistics tickers let you see the feature at work:
|
||||
|
||||
* `rocksdb.read.path.range.tombstones.inserted` counts the range tombstones synthesized by conversion.
|
||||
* `rocksdb.read.path.range.tombstones.discarded` counts the attempts that were discarded for safety reasons.
|
||||
|
||||
## Performance
|
||||
|
||||
The optimization targets read workloads over data that has accumulated tombstones. To measure it we use the following `db_bench` setup: fill and compact a database, scatter sets of contiguous point deletes through it, then run a `seekrandom` scan with conversion off versus on.
|
||||
|
||||
```
|
||||
# Fill and compact 1M keys
|
||||
./db_bench --benchmarks=fillseq,compact --compression_type=none --num=1000000 --db=$DB
|
||||
|
||||
# Scatter tombstones: seek to random keys and delete 100 keys after each seek
|
||||
./db_bench --benchmarks=seekrandom,flush --compression_type=none --num=2000 \
|
||||
--seek_nexts=0 --seek_nexts_to_delete=100 --use_existing_db=1 --threads=1 --db=$DB
|
||||
|
||||
# Scan workload: forward or reverse, conversion off (=0) or on (=8)
|
||||
./db_bench --benchmarks=seekrandom --seek_nexts=100 --threads=8 --reverse_iterator=<true|false> \
|
||||
--use_existing_db=1 --compression_type=none --num=1000000 --duration=10 \
|
||||
--disable_auto_compactions --min_tombstones_for_range_conversion=<0|8> --db=$DB
|
||||
```
|
||||
|
||||
`seekrandom` over the delete-heavy database, throughput in ops/s:
|
||||
|
||||
|Workload |Conversion off |Conversion on |Speedup |
|
||||
|--- |--- |--- |--- |
|
||||
|Forward scan |2,685 |266,733 |~99x |
|
||||
|Reverse scan |519 |191,119 |~368x |
|
||||
|
||||
### Regression check
|
||||
|
||||
Enabling the feature adds a little per-scan bookkeeping (it tracks runs of contiguous tombstones) even on scans that never convert anything, so it is worth confirming there is no slowdown when there are no tombstones to collapse. Here we fill and compact without scattering any deletes, then run the same scan with conversion off versus on:
|
||||
|
||||
```
|
||||
# Fill and compact 1M keys, no deletes
|
||||
./db_bench --benchmarks=fillseq,compact --compression_type=none --num=1000000 --db=$DB
|
||||
|
||||
# Same scan workload: forward or reverse, conversion off (=0) or on (=8)
|
||||
./db_bench --benchmarks=seekrandom --seek_nexts=100 --threads=8 --reverse_iterator=<true|false> \
|
||||
--use_existing_db=1 --compression_type=none --num=1000000 --duration=10 \
|
||||
--disable_auto_compactions --min_tombstones_for_range_conversion=<0|8> --db=$DB
|
||||
```
|
||||
|
||||
|Workload |Conversion off |Conversion on |Speedup |
|
||||
|--- |--- |--- |--- |
|
||||
|Forward scan, no deletes |310,052 |311,185 |no change (within noise) |
|
||||
|Reverse scan, no deletes |237,484 |236,541 |no change (within noise) |
|
||||
|
||||
Throughput is unchanged, so the bookkeeping is cheap and the feature is safe to leave enabled for mixed workloads.
|
||||
@@ -399,55 +399,35 @@ loop still keeps reading trace records until `Next()` returns EOF, footer, or
|
||||
corruption, at which point restore decides whether the trace prefix it already
|
||||
consumed was sufficient.
|
||||
|
||||
## Offline trace inspection
|
||||
## Debugging support
|
||||
|
||||
`<N>.trace` uses RocksDB's generic binary query-trace format, so there is
|
||||
already an offline printer for it: `trace_analyzer`.
|
||||
Three flags control replay debugging:
|
||||
|
||||
Before adding any expected-state-specific debug logging, use this tool to dump
|
||||
the trace to a readable text file. This is the easiest path for both humans
|
||||
and agents to inspect replay inputs.
|
||||
- `--expected_state_trace_debug`
|
||||
- `--expected_state_trace_debug_key`
|
||||
- `--expected_state_trace_debug_max_logs`
|
||||
|
||||
Build it with:
|
||||
When enabled, restore prints lines prefixed with
|
||||
`[expected_state_trace_debug]`, including:
|
||||
|
||||
```bash
|
||||
make -j128 trace_analyzer
|
||||
```
|
||||
- restore begin/end markers
|
||||
- `Next()` failures such as EOF or corruption
|
||||
- per-key or per-range replay details
|
||||
- parse failures and key roundtrip mismatches
|
||||
- a replay summary with counters
|
||||
|
||||
Create an output directory first, then run:
|
||||
Useful counters in the summary include:
|
||||
|
||||
```bash
|
||||
mkdir -p /tmp/trace_dump
|
||||
./trace_analyzer \
|
||||
-trace_path=/path/to/<N>.trace \
|
||||
-output_dir=/tmp/trace_dump \
|
||||
-output_prefix=<N> \
|
||||
-convert_to_human_readable_trace \
|
||||
-try_process_corrupted_trace \
|
||||
-no_print
|
||||
```
|
||||
- `replayed_write_ops`
|
||||
- `key_decode_failures`
|
||||
- `key_roundtrip_mismatches`
|
||||
- `focus_key_op_hits`
|
||||
- `logs_emitted`
|
||||
- `logs_suppressed`
|
||||
|
||||
This writes:
|
||||
|
||||
- `/tmp/trace_dump/<N>-human_readable_trace.txt`
|
||||
|
||||
The line format is:
|
||||
|
||||
- normal record: `<hex_key> type_id cf_id value_size timestamp_us`
|
||||
- range delete: `<begin_hex> <end_hex> type_id cf_id 0 timestamp_us`
|
||||
|
||||
Useful flags:
|
||||
|
||||
- `-no_key` omits the hex key columns to reduce output size
|
||||
- `-try_process_corrupted_trace` is recommended for `db_stress` crash traces,
|
||||
since they can legitimately have a truncated or corrupt tail record
|
||||
|
||||
Two important caveats:
|
||||
|
||||
- `trace_analyzer` expects `-output_dir` to already exist
|
||||
- expected-state replay only needs the write prefix of the trace, but the file
|
||||
format itself is the generic RocksDB trace format rather than an
|
||||
expected-state-specific one
|
||||
`--expected_state_trace_debug_key=<k>` narrows logging to a particular logical
|
||||
key where possible. This is useful when the trace is large and only one key's
|
||||
history matters.
|
||||
|
||||
## Crash-safety rules encoded in file deletion order
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="280" viewBox="0 0 760 280" font-family="Helvetica, Arial, sans-serif">
|
||||
<defs>
|
||||
<marker id="tier-arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L8,3 L0,6 z" fill="#1565c0"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect x="0" y="0" width="760" height="280" fill="#ffffff"/>
|
||||
|
||||
<text x="380" y="30" text-anchor="middle" font-size="17" font-weight="bold" fill="#263238">KV-ratio FIFO intra-L0 compaction</text>
|
||||
<text x="380" y="52" text-anchor="middle" font-size="12" fill="#546e7a">Compute a BlobDB-aware target, then merge SST files through bounded size tiers.</text>
|
||||
|
||||
<rect x="38" y="76" width="288" height="76" rx="6" fill="#fafafa" stroke="#cfd8dc" stroke-width="1.3"/>
|
||||
<text x="54" y="99" font-size="12" font-weight="bold" fill="#37474f">Observed data shape</text>
|
||||
<rect x="54" y="113" width="64" height="18" rx="3" fill="#e3f2fd" stroke="#1565c0" stroke-width="1.2"/>
|
||||
<text x="86" y="126" text-anchor="middle" font-size="10" fill="#0d47a1">SST</text>
|
||||
<rect x="124" y="113" width="170" height="18" rx="3" fill="#ede7f6" stroke="#5e35b1" stroke-width="1.2"/>
|
||||
<text x="209" y="126" text-anchor="middle" font-size="10" fill="#4527a0">blob files</text>
|
||||
<text x="54" y="143" font-size="11" fill="#546e7a">sst_ratio = SST / (SST + blob)</text>
|
||||
|
||||
<line x1="326" y1="114" x2="404" y2="114" stroke="#1565c0" stroke-width="2" marker-end="url(#tier-arrow)"/>
|
||||
|
||||
<rect x="420" y="76" width="300" height="76" rx="6" fill="#f1f8e9" stroke="#7cb342" stroke-width="1.3"/>
|
||||
<text x="436" y="99" font-size="12" font-weight="bold" fill="#33691e">Target graduated SST size</text>
|
||||
<text x="436" y="119" font-size="10.5" fill="#33691e">target = max_data_files_size x sst_ratio</text>
|
||||
<text x="436" y="132" font-size="10.5" fill="#33691e">divided by trigger</text>
|
||||
<text x="436" y="147" font-size="10.5" fill="#546e7a">Files at or above target graduate.</text>
|
||||
|
||||
<text x="64" y="193" font-size="12" font-weight="bold" fill="#37474f">Small flush SSTs</text>
|
||||
<rect x="64" y="205" width="40" height="28" rx="4" fill="#e3f2fd" stroke="#1565c0" stroke-width="1.3"/>
|
||||
<rect x="110" y="205" width="40" height="28" rx="4" fill="#e3f2fd" stroke="#1565c0" stroke-width="1.3"/>
|
||||
<rect x="156" y="205" width="40" height="28" rx="4" fill="#e3f2fd" stroke="#1565c0" stroke-width="1.3"/>
|
||||
<rect x="202" y="205" width="40" height="28" rx="4" fill="#e3f2fd" stroke="#1565c0" stroke-width="1.3"/>
|
||||
|
||||
<line x1="260" y1="219" x2="320" y2="219" stroke="#1565c0" stroke-width="2" marker-end="url(#tier-arrow)"/>
|
||||
|
||||
<text x="346" y="193" font-size="12" font-weight="bold" fill="#37474f">Tier files</text>
|
||||
<rect x="346" y="204" width="62" height="30" rx="4" fill="#fff3e0" stroke="#ef6c00" stroke-width="1.4"/>
|
||||
<text x="377" y="223" text-anchor="middle" font-size="10" font-weight="bold" fill="#e65100">10 KB</text>
|
||||
<line x1="418" y1="219" x2="466" y2="219" stroke="#1565c0" stroke-width="2" marker-end="url(#tier-arrow)"/>
|
||||
<rect x="488" y="204" width="70" height="30" rx="4" fill="#fff3e0" stroke="#ef6c00" stroke-width="1.4"/>
|
||||
<text x="523" y="223" text-anchor="middle" font-size="10" font-weight="bold" fill="#e65100">100 KB</text>
|
||||
|
||||
<line x1="568" y1="219" x2="614" y2="219" stroke="#1565c0" stroke-width="2" marker-end="url(#tier-arrow)"/>
|
||||
|
||||
<text x="636" y="193" font-size="12" font-weight="bold" fill="#37474f">Graduated</text>
|
||||
<rect x="626" y="202" width="94" height="34" rx="5" fill="#e8f5e9" stroke="#2e7d32" stroke-width="1.7"/>
|
||||
<text x="673" y="223" text-anchor="middle" font-size="11" font-weight="bold" fill="#1b5e20">~1 MB</text>
|
||||
|
||||
<text x="380" y="255" text-anchor="middle" font-size="10.5" fill="#546e7a">Example: trigger = 10 and target = 1 MB.</text>
|
||||
<text x="380" y="270" text-anchor="middle" font-size="10.5" fill="#546e7a">By default, targets come from the live SST/blob ratio.</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -1,47 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="260" viewBox="0 0 760 260" font-family="Helvetica, Arial, sans-serif">
|
||||
<defs>
|
||||
<marker id="old-arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L8,3 L0,6 z" fill="#546e7a"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect x="0" y="0" width="760" height="260" fill="#ffffff"/>
|
||||
|
||||
<text x="380" y="30" text-anchor="middle" font-size="17" font-weight="bold" fill="#263238">Old FIFO intra-L0 picker</text>
|
||||
<text x="380" y="52" text-anchor="middle" font-size="12" fill="#546e7a">The guard is based on write_buffer_size, but BlobDB SST files can be much smaller.</text>
|
||||
|
||||
<text x="36" y="90" font-size="12" font-weight="bold" fill="#37474f">Round 1</text>
|
||||
<rect x="100" y="72" width="54" height="36" rx="5" fill="#e3f2fd" stroke="#1565c0" stroke-width="1.4"/>
|
||||
<text x="127" y="94" text-anchor="middle" font-size="11" font-weight="bold" fill="#0d47a1">64 KB</text>
|
||||
<rect x="164" y="72" width="54" height="36" rx="5" fill="#e3f2fd" stroke="#1565c0" stroke-width="1.4"/>
|
||||
<text x="191" y="94" text-anchor="middle" font-size="11" font-weight="bold" fill="#0d47a1">64 KB</text>
|
||||
<rect x="228" y="72" width="54" height="36" rx="5" fill="#e3f2fd" stroke="#1565c0" stroke-width="1.4"/>
|
||||
<text x="255" y="94" text-anchor="middle" font-size="11" font-weight="bold" fill="#0d47a1">64 KB</text>
|
||||
<rect x="292" y="72" width="54" height="36" rx="5" fill="#e3f2fd" stroke="#1565c0" stroke-width="1.4"/>
|
||||
<text x="319" y="94" text-anchor="middle" font-size="11" font-weight="bold" fill="#0d47a1">64 KB</text>
|
||||
<line x1="365" y1="90" x2="440" y2="90" stroke="#546e7a" stroke-width="2" marker-end="url(#old-arrow)"/>
|
||||
<rect x="465" y="72" width="86" height="36" rx="5" fill="#fff3e0" stroke="#ef6c00" stroke-width="1.6"/>
|
||||
<text x="508" y="94" text-anchor="middle" font-size="12" font-weight="bold" fill="#e65100">256 KB</text>
|
||||
<text x="590" y="88" font-size="10.5" fill="#546e7a">still below</text>
|
||||
<text x="590" y="101" font-size="10.5" fill="#546e7a">70 MB guard</text>
|
||||
|
||||
<text x="36" y="153" font-size="12" font-weight="bold" fill="#37474f">Later</text>
|
||||
<rect x="100" y="135" width="74" height="36" rx="5" fill="#fff3e0" stroke="#ef6c00" stroke-width="1.6"/>
|
||||
<text x="137" y="157" text-anchor="middle" font-size="11" font-weight="bold" fill="#e65100">2.5 MB</text>
|
||||
<rect x="184" y="135" width="74" height="36" rx="5" fill="#fff3e0" stroke="#ef6c00" stroke-width="1.6"/>
|
||||
<text x="221" y="157" text-anchor="middle" font-size="11" font-weight="bold" fill="#e65100">2.5 MB</text>
|
||||
<rect x="268" y="135" width="74" height="36" rx="5" fill="#fff3e0" stroke="#ef6c00" stroke-width="1.6"/>
|
||||
<text x="305" y="157" text-anchor="middle" font-size="11" font-weight="bold" fill="#e65100">2.5 MB</text>
|
||||
<line x1="365" y1="153" x2="440" y2="153" stroke="#546e7a" stroke-width="2" marker-end="url(#old-arrow)"/>
|
||||
<rect x="465" y="135" width="110" height="36" rx="5" fill="#ffebee" stroke="#c62828" stroke-width="1.8"/>
|
||||
<text x="520" y="157" text-anchor="middle" font-size="12" font-weight="bold" fill="#b71c1c">larger SST</text>
|
||||
<text x="590" y="150" font-size="10.5" fill="#546e7a">can be</text>
|
||||
<text x="590" y="163" font-size="10.5" fill="#546e7a">selected again</text>
|
||||
|
||||
<rect x="100" y="202" width="560" height="30" rx="5" fill="#fafafa" stroke="#cfd8dc" stroke-width="1.2"/>
|
||||
<rect x="100" y="202" width="420" height="30" rx="5" fill="#eceff1"/>
|
||||
<text x="110" y="222" font-size="11" fill="#37474f">write_buffer_size guard: 1.1 x 64 MB ~= 70 MB</text>
|
||||
<line x1="520" y1="198" x2="520" y2="236" stroke="#c62828" stroke-width="2"/>
|
||||
<text x="533" y="215" font-size="10" font-weight="bold" fill="#b71c1c">BlobDB SSTs</text>
|
||||
<text x="533" y="227" font-size="10" font-weight="bold" fill="#b71c1c">stay below this</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,87 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="196" viewBox="0 30 760 196" font-family="Helvetica, Arial, sans-serif">
|
||||
<defs>
|
||||
<marker id="arrow-cba" markerWidth="11" markerHeight="11" refX="8" refY="3.2" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L8,3.2 L0,6.4 z" fill="#1565c0"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect x="0" y="0" width="760" height="262" fill="#ffffff"/>
|
||||
|
||||
<text x="190" y="46" text-anchor="middle" font-size="14" font-weight="bold" fill="#37474f">Before</text>
|
||||
<text x="582" y="46" text-anchor="middle" font-size="14" font-weight="bold" fill="#0d47a1">After</text>
|
||||
|
||||
<!-- conversion arrow between the panels -->
|
||||
<line x1="360" y1="120" x2="414" y2="120" stroke="#1565c0" stroke-width="2.5" marker-end="url(#arrow-cba)"/>
|
||||
<text x="387" y="110" text-anchor="middle" font-size="10" fill="#1565c0">convert</text>
|
||||
<text x="387" y="135" text-anchor="middle" font-size="9" fill="#1565c0">(scan)</text>
|
||||
|
||||
<!-- ===== LEFT (Before) ===== -->
|
||||
<text x="8" y="76" font-size="9" fill="#37474f">memtable</text>
|
||||
<text x="8" y="111" font-size="11" font-weight="bold" fill="#37474f">L0</text>
|
||||
<text x="8" y="145" font-size="11" font-weight="bold" fill="#37474f">L1</text>
|
||||
<text x="8" y="179" font-size="11" font-weight="bold" fill="#37474f">L2</text>
|
||||
|
||||
<rect x="56" y="58" width="296" height="30" rx="4" fill="#fafafa" stroke="#e0e0e0" stroke-width="1.1"/>
|
||||
<rect x="56" y="92" width="296" height="30" rx="4" fill="#fafafa" stroke="#e0e0e0" stroke-width="1.1"/>
|
||||
<rect x="56" y="126" width="296" height="30" rx="4" fill="#fafafa" stroke="#e0e0e0" stroke-width="1.1"/>
|
||||
<rect x="56" y="160" width="296" height="30" rx="4" fill="#fafafa" stroke="#e0e0e0" stroke-width="1.1"/>
|
||||
|
||||
<!-- L0: point tombstones k1, k3 -->
|
||||
<rect x="67" y="95" width="58" height="24" rx="4" fill="#ffcdd2" stroke="#c62828" stroke-width="1.4"/>
|
||||
<text x="96" y="111" text-anchor="middle" font-size="11" font-weight="bold" fill="#b71c1c">k1</text>
|
||||
<rect x="211" y="95" width="58" height="24" rx="4" fill="#ffcdd2" stroke="#c62828" stroke-width="1.4"/>
|
||||
<text x="240" y="111" text-anchor="middle" font-size="11" font-weight="bold" fill="#b71c1c">k3</text>
|
||||
<!-- L1: point tombstone k2 -->
|
||||
<rect x="139" y="129" width="58" height="24" rx="4" fill="#ffcdd2" stroke="#c62828" stroke-width="1.4"/>
|
||||
<text x="168" y="145" text-anchor="middle" font-size="11" font-weight="bold" fill="#b71c1c">k2</text>
|
||||
<!-- L2: hidden values k1,k2,k3 + live k4 -->
|
||||
<rect x="67" y="163" width="58" height="24" rx="4" fill="#ffe3e3" stroke="#ef9a9a" stroke-width="1.4"/>
|
||||
<text x="96" y="179" text-anchor="middle" font-size="11" font-weight="bold" fill="#b71c1c">k1</text>
|
||||
<rect x="139" y="163" width="58" height="24" rx="4" fill="#ffe3e3" stroke="#ef9a9a" stroke-width="1.4"/>
|
||||
<text x="168" y="179" text-anchor="middle" font-size="11" font-weight="bold" fill="#b71c1c">k2</text>
|
||||
<rect x="211" y="163" width="58" height="24" rx="4" fill="#ffe3e3" stroke="#ef9a9a" stroke-width="1.4"/>
|
||||
<text x="240" y="179" text-anchor="middle" font-size="11" font-weight="bold" fill="#b71c1c">k3</text>
|
||||
<rect x="283" y="163" width="58" height="24" rx="4" fill="#c8e6c9" stroke="#2e7d32" stroke-width="1.4"/>
|
||||
<text x="312" y="179" text-anchor="middle" font-size="11" font-weight="bold" fill="#1b5e20">k4</text>
|
||||
|
||||
<!-- ===== RIGHT (After) ===== -->
|
||||
<text x="420" y="76" font-size="9" fill="#37474f">memtable</text>
|
||||
<text x="420" y="111" font-size="11" font-weight="bold" fill="#37474f">L0</text>
|
||||
<text x="420" y="145" font-size="11" font-weight="bold" fill="#37474f">L1</text>
|
||||
<text x="420" y="179" font-size="11" font-weight="bold" fill="#37474f">L2</text>
|
||||
|
||||
<rect x="466" y="58" width="288" height="30" rx="4" fill="#fafafa" stroke="#e0e0e0" stroke-width="1.1"/>
|
||||
<rect x="466" y="92" width="288" height="30" rx="4" fill="#fafafa" stroke="#e0e0e0" stroke-width="1.1"/>
|
||||
<rect x="466" y="126" width="288" height="30" rx="4" fill="#fafafa" stroke="#e0e0e0" stroke-width="1.1"/>
|
||||
<rect x="466" y="160" width="288" height="30" rx="4" fill="#fafafa" stroke="#e0e0e0" stroke-width="1.1"/>
|
||||
|
||||
<!-- memtable: NEW range tombstone [k1, k4) -->
|
||||
<rect x="473" y="61" width="216" height="24" rx="4" fill="#bbdefb" stroke="#1565c0" stroke-width="2"/>
|
||||
<text x="581" y="77" text-anchor="middle" font-size="9" font-weight="bold" fill="#0d47a1">range tombstone [k1, k4)</text>
|
||||
<!-- L0: unchanged -->
|
||||
<rect x="473" y="95" width="58" height="24" rx="4" fill="#ffcdd2" stroke="#c62828" stroke-width="1.4"/>
|
||||
<text x="502" y="111" text-anchor="middle" font-size="11" font-weight="bold" fill="#b71c1c">k1</text>
|
||||
<rect x="617" y="95" width="58" height="24" rx="4" fill="#ffcdd2" stroke="#c62828" stroke-width="1.4"/>
|
||||
<text x="646" y="111" text-anchor="middle" font-size="11" font-weight="bold" fill="#b71c1c">k3</text>
|
||||
<rect x="545" y="129" width="58" height="24" rx="4" fill="#ffcdd2" stroke="#c62828" stroke-width="1.4"/>
|
||||
<text x="574" y="145" text-anchor="middle" font-size="11" font-weight="bold" fill="#b71c1c">k2</text>
|
||||
<rect x="473" y="163" width="58" height="24" rx="4" fill="#ffe3e3" stroke="#ef9a9a" stroke-width="1.4"/>
|
||||
<text x="502" y="179" text-anchor="middle" font-size="11" font-weight="bold" fill="#b71c1c">k1</text>
|
||||
<rect x="545" y="163" width="58" height="24" rx="4" fill="#ffe3e3" stroke="#ef9a9a" stroke-width="1.4"/>
|
||||
<text x="574" y="179" text-anchor="middle" font-size="11" font-weight="bold" fill="#b71c1c">k2</text>
|
||||
<rect x="617" y="163" width="58" height="24" rx="4" fill="#ffe3e3" stroke="#ef9a9a" stroke-width="1.4"/>
|
||||
<text x="646" y="179" text-anchor="middle" font-size="11" font-weight="bold" fill="#b71c1c">k3</text>
|
||||
<rect x="689" y="163" width="58" height="24" rx="4" fill="#c8e6c9" stroke="#2e7d32" stroke-width="1.4"/>
|
||||
<text x="718" y="179" text-anchor="middle" font-size="11" font-weight="bold" fill="#1b5e20">k4</text>
|
||||
|
||||
<!-- legend -->
|
||||
<rect x="60" y="206" width="14" height="10" rx="2" fill="#ffcdd2" stroke="#c62828" stroke-width="1.1"/>
|
||||
<text x="78" y="215" font-size="10" fill="#616161">point tombstone</text>
|
||||
<rect x="190" y="206" width="14" height="10" rx="2" fill="#ffe3e3" stroke="#ef9a9a" stroke-width="1.1"/>
|
||||
<text x="208" y="215" font-size="10" fill="#616161">shadowed value</text>
|
||||
<rect x="306" y="206" width="14" height="10" rx="2" fill="#c8e6c9" stroke="#2e7d32" stroke-width="1.1"/>
|
||||
<text x="324" y="215" font-size="10" fill="#616161">live</text>
|
||||
<rect x="368" y="206" width="14" height="10" rx="2" fill="#bbdefb" stroke="#1565c0" stroke-width="1.1"/>
|
||||
<text x="386" y="215" font-size="10" fill="#616161">range tombstone</text>
|
||||
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.5 KiB |
Loaded 100 of 180 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user