mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-08 15:15:23 +08:00
Compare commits
371 Commits
6.8.fb
...
siying-patch-4
| Author | SHA1 | Date | |
|---|---|---|---|
| d9ed290bfa | |||
| 9cc25190e1 | |||
| 50d6969816 | |||
| 83a4dd1a67 | |||
| 8efb5cfb6f | |||
| 9f21d08660 | |||
| 5b2bbacb6f | |||
| 1092f19d95 | |||
| d739318ba7 | |||
| 096beb787e | |||
| dea4063b13 | |||
| 25a0d0ca30 | |||
| d76eed4839 | |||
| 88b4210701 | |||
| a607f3efaa | |||
| f4583f7480 | |||
| 552fd765b3 | |||
| d6b7b7712f | |||
| 569b87e8c7 | |||
| 775dc623ad | |||
| 312f23c92d | |||
| 223b57eeb8 | |||
| 94d04529de | |||
| 742b452863 | |||
| b7bab48099 | |||
| 9bfd46d0d8 | |||
| aa8f1331af | |||
| 88db97b06d | |||
| 9c24a5cb4d | |||
| f7613e2a9e | |||
| 15d9f28da5 | |||
| bacd6edcbe | |||
| 7e2ac0c3a0 | |||
| d63f86e506 | |||
| 9810f40075 | |||
| af58d92760 | |||
| 83833637c1 | |||
| 717749f4c0 | |||
| d854abad78 | |||
| 722ebba834 | |||
| b3585a11b4 | |||
| fbe2d259cb | |||
| e6be168aa5 | |||
| 02db03af8d | |||
| 5abda3bb8b | |||
| edf74d1cb1 | |||
| 6a8ddd374d | |||
| 1fb3593f25 | |||
| f5e649453c | |||
| 119b26fac0 | |||
| fb08330f74 | |||
| 3020df9df5 | |||
| f8c2e5a608 | |||
| 8988f831d8 | |||
| f941adef88 | |||
| 23e446a157 | |||
| 2e7070b194 | |||
| 98b0cbea88 | |||
| 1bee0fca05 | |||
| 2677bd5967 | |||
| aaece2a98d | |||
| c7432cc3c0 | |||
| a8170d774c | |||
| 6cbe9d9762 | |||
| 31bd2d790e | |||
| e85cbdb4e8 | |||
| 0b45a68c59 | |||
| 2f3261831b | |||
| 02df00d97b | |||
| 055b4d25f8 | |||
| b7c825d5cf | |||
| afa3518839 | |||
| 0f85d163e6 | |||
| f005dac2d9 | |||
| 0b8c549b3f | |||
| ffe08ffcc2 | |||
| 22e5c513c2 | |||
| 0a17d95357 | |||
| 172adce767 | |||
| 43f8a9dcce | |||
| 78e291b17e | |||
| 9360776cb9 | |||
| 2adb7e3768 | |||
| 556972e964 | |||
| bfc9737aca | |||
| 38f988d3b4 | |||
| 14eca6bf04 | |||
| 298b00a396 | |||
| 8d87e9cea1 | |||
| 66942e8158 | |||
| 02f59ed669 | |||
| bcb9e41080 | |||
| 038e02d8d9 | |||
| 0c56fc4d66 | |||
| c5abf78bca | |||
| 961c7590d6 | |||
| e3f953a863 | |||
| bcefc59e9f | |||
| 9060e6fa79 | |||
| 82a82c76e7 | |||
| bd68bfb41b | |||
| b464a85e33 | |||
| b559dba817 | |||
| 803a517b48 | |||
| bcd32560dd | |||
| 35a25a3fb9 | |||
| 826295a5e9 | |||
| 292bcf6227 | |||
| 38be686160 | |||
| c7aedf1b48 | |||
| eb04bb86c6 | |||
| 545e14b53b | |||
| aaafcb80ab | |||
| a1523efcdf | |||
| 39b24432d4 | |||
| 1551f1011a | |||
| 0ac0098705 | |||
| ada700b906 | |||
| b9d65f5aa6 | |||
| d790e6004f | |||
| 50ba245f6d | |||
| 91b7553293 | |||
| b11a8b1b9a | |||
| 06a2dcebea | |||
| 50d63a2af0 | |||
| 3bea276fc8 | |||
| 244797aa4b | |||
| 07204837ce | |||
| 4a4b8a1344 | |||
| 70aaa9ceeb | |||
| c384c08a4f | |||
| 3f218074ee | |||
| 2e9324718a | |||
| f0e8731b72 | |||
| 3a1c29d40e | |||
| a50ea71c00 | |||
| d9cd33516a | |||
| e72e2167fd | |||
| 1c84660457 | |||
| 12825894a2 | |||
| 94265234de | |||
| 3730b05dc9 | |||
| f286fb344b | |||
| b27a1448b6 | |||
| e9ba4ba348 | |||
| ac3ae1df0b | |||
| 1f20df2f38 | |||
| c1e1185b7a | |||
| 06c3b85b9a | |||
| 53f84470d2 | |||
| c21c459771 | |||
| 394f2bbd13 | |||
| a96461d169 | |||
| 2f1700c8c5 | |||
| 91bc0130fa | |||
| 5584595f80 | |||
| 5a61e7864d | |||
| a00ddf1574 | |||
| 680c416348 | |||
| d9e170d82b | |||
| 211088df6e | |||
| c8643edfc3 | |||
| 6acbbbf9fc | |||
| 6504ae0c4e | |||
| ef0c3eda27 | |||
| 079e50d2ba | |||
| 6277e28039 | |||
| ab13d43e1d | |||
| eecd8fba46 | |||
| fe238e5438 | |||
| cf342464ca | |||
| e619a20e93 | |||
| 8c694025e9 | |||
| b938e6042b | |||
| 28fe8e4620 | |||
| 5272305437 | |||
| 8086e5e294 | |||
| 335ea73e49 | |||
| 618bf638aa | |||
| b810e62b39 | |||
| bae6f58696 | |||
| a7f0b27b39 | |||
| 4574d7513d | |||
| d4398e08fc | |||
| 4cd859edf1 | |||
| bea91d5d61 | |||
| cc8d16efd6 | |||
| 249eff0f30 | |||
| 75b13ea94a | |||
| 791e5714a5 | |||
| 3b2f2719eb | |||
| 0a77617820 | |||
| 40497a875a | |||
| 1758f76f2d | |||
| 9e7b7e2c08 | |||
| e04f3bce4f | |||
| 51bdfae010 | |||
| bc51e33d9c | |||
| ae77880223 | |||
| 4cbc19d2a1 | |||
| c1ccd6b6af | |||
| 6ee66cf8f0 | |||
| f9155a3404 | |||
| 03a1d95db0 | |||
| 31da5e34c1 | |||
| fe206f4f7c | |||
| 45d2b4efca | |||
| 243852ec15 | |||
| 63d82e57b9 | |||
| 9e6f3efcd2 | |||
| 9ca49bd4df | |||
| 5fef0ffd66 | |||
| 6717ada899 | |||
| 73523baeb1 | |||
| 5801af4646 | |||
| 2767972386 | |||
| e45673dece | |||
| 610a09ccff | |||
| 165560fb32 | |||
| 234e2ed5b6 | |||
| 9ae8058d95 | |||
| 3d6d7bcf17 | |||
| 38dfa406ff | |||
| 41563b61db | |||
| d9cad3a526 | |||
| 9eca6d651d | |||
| 79c838eb0f | |||
| eeb3cf3f58 | |||
| 5c19a441c4 | |||
| 0c05624d50 | |||
| a0faff126d | |||
| d600e5b0eb | |||
| 1be3be5522 | |||
| f08630b914 | |||
| c8c739a877 | |||
| 9e89ffb776 | |||
| 66a95f0fac | |||
| 9d6974d3c9 | |||
| e860f8840a | |||
| 6e6f807917 | |||
| eb287c72d7 | |||
| 31759a7094 | |||
| 83fc90b3df | |||
| 94f90ac6bc | |||
| a91613dd06 | |||
| d648a0e17f | |||
| 0b136308b0 | |||
| 487ebe4fd5 | |||
| e5f1bfc263 | |||
| 3ff603171d | |||
| fcd7bee925 | |||
| 0355d14dd9 | |||
| f53cdab3d7 | |||
| a56439bb7f | |||
| a67fb4c9bd | |||
| 00f8016b36 | |||
| e60ea7fe57 | |||
| 259b6ec8da | |||
| 079e77ff9e | |||
| df62cd5b35 | |||
| ef088f0e93 | |||
| 0709cd04ca | |||
| d0f3894cf1 | |||
| 278911a2d9 | |||
| 8088482dd6 | |||
| 2165c3bacc | |||
| 2b02ea25e2 | |||
| 03a781a90c | |||
| 719c0f91bf | |||
| 57096ab13e | |||
| b5818f87f0 | |||
| e6f86cfb36 | |||
| 80979f81c7 | |||
| fd3ddaf90d | |||
| 18cf0de640 | |||
| eaf95c7d1a | |||
| 2ae91c6097 | |||
| cb671ea1ca | |||
| e8d332d97e | |||
| ee50b8d499 | |||
| 3a35542f86 | |||
| 3881a678d5 | |||
| 4246888101 | |||
| 2e276973e4 | |||
| 8abd41a544 | |||
| e91d1a21a6 | |||
| 6f62322fe4 | |||
| 6301dbe7a7 | |||
| 963af52f15 | |||
| e70629e5f7 | |||
| 8599efabab | |||
| 93b80ca7ba | |||
| ccf7676455 | |||
| 6fd0ed4993 | |||
| ceeca7542d | |||
| a6ce5c823b | |||
| 921cdd37e2 | |||
| a9d168cfd7 | |||
| 43aee93d2b | |||
| d300d10962 | |||
| 617f479266 | |||
| 5c6346c420 | |||
| fb09ef05dc | |||
| 4fc216649d | |||
| 5fd152b7ad | |||
| 6c50fe1ec9 | |||
| 66cd07c6d9 | |||
| d2e3822d67 | |||
| 093ff0b2ce | |||
| e10553f2a6 | |||
| 442404558a | |||
| 217ce20021 | |||
| 8ad4b32c5d | |||
| 1df9b01680 | |||
| 712bc4b6a2 | |||
| a8149aef1e | |||
| 66ed58083a | |||
| 098dce2d1a | |||
| 6c595f008a | |||
| db02664f35 | |||
| 58918d4ccc | |||
| 488b1e6739 | |||
| d66908091d | |||
| 402da454cb | |||
| 23eae14d24 | |||
| a824727db4 | |||
| 85dbdf2586 | |||
| 5c30e6c088 | |||
| 674cf41732 | |||
| dd7a4a8f03 | |||
| a8851f2d05 | |||
| 2ccb794eb6 | |||
| 0d2c8e47e8 | |||
| 0772768d07 | |||
| c15e85bdcb | |||
| 07a3f7f008 | |||
| 6dea7530b5 | |||
| 2d9efc9ab2 | |||
| 331e6199df | |||
| 37a635cfe6 | |||
| 8fc20ac468 | |||
| 65b60db9e1 | |||
| 00c4ab01b9 | |||
| f5bc3b99d5 | |||
| 4028eba67b | |||
| fd1da22111 | |||
| d93812c9ae | |||
| 0a0151fb99 | |||
| f6c2777d95 | |||
| e171a219d5 | |||
| afb97094ae | |||
| e62fe50634 | |||
| 8bbd76edbf | |||
| 03dbd11ead | |||
| 48d8d076a3 | |||
| 17bef7d3a8 | |||
| 8d73137ae8 | |||
| 904a60ff63 | |||
| 8637bc1eea | |||
| 5f2f8cd97c | |||
| 9b3c9ef0e8 | |||
| 86f1ad7046 | |||
| 051696bf98 | |||
| 41535d0218 | |||
| 72ee067b90 | |||
| 741decfe37 | |||
| f52db84650 | |||
| 69679e7375 | |||
| d87c10c6ab | |||
| eb367d45c0 | |||
| b47a714051 |
@@ -0,0 +1,148 @@
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
win: circleci/windows@2.4.0
|
||||
|
||||
executors:
|
||||
windows-2xlarge:
|
||||
machine:
|
||||
image: 'windows-server-2019-vs2019:201908-06'
|
||||
resource_class: windows.2xlarge
|
||||
shell: bash.exe
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: pyenv global 3.5.2
|
||||
- run: sudo apt-get update -y
|
||||
- run: sudo apt-get install -y libgflags-dev
|
||||
- run: SKIP_FORMAT_BUCK_CHECKS=1 PRINT_PARALLEL_OUTPUTS=1 make J=32 all check -j32
|
||||
|
||||
build-linux-release:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: make release -j32
|
||||
|
||||
build-linux-lite:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: pyenv global 3.5.2
|
||||
- run: SKIP_FORMAT_BUCK_CHECKS=1 PRINT_PARALLEL_OUTPUTS=1 LITE=1 make J=32 all check -j32
|
||||
|
||||
build-linux-lite-release:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: make release -j32
|
||||
|
||||
build-linux-clang-no-test:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: sudo apt-get update -y
|
||||
- run: sudo apt-get install -y clang
|
||||
- run: CC=clang CXX=clang++ USE_CLANG=1 PORTABLE=1 make all -j32
|
||||
|
||||
build-linux-clang10-no-test:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: sudo sh -c 'echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" >> /etc/apt/sources.list'
|
||||
- run: sudo sh -c 'echo "deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" >> /etc/apt/sources.list'
|
||||
- run: sudo apt-get update -y
|
||||
- run: sudo apt-get install -y clang-10
|
||||
- run: CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make all -j32 # aligned new doesn't work for reason we haven't figured out
|
||||
|
||||
build-linux-cmake:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: mkdir build && cd build && cmake -DWITH_GFLAGS=0 .. && make -j32
|
||||
|
||||
build-windows:
|
||||
executor: windows-2xlarge
|
||||
|
||||
environment:
|
||||
THIRDPARTY_HOME: C:/Users/circleci/thirdparty
|
||||
CMAKE_HOME: C:/Users/circleci/thirdparty/cmake-3.16.4-win64-x64
|
||||
CMAKE_BIN: C:/Users/circleci/thirdparty/cmake-3.16.4-win64-x64/bin/cmake.exe
|
||||
CMAKE_GENERATOR: Visual Studio 16 2019
|
||||
SNAPPY_HOME: C:/Users/circleci/thirdparty/snappy-1.1.7
|
||||
SNAPPY_INCLUDE: C:/Users/circleci/thirdparty/snappy-1.1.7;C:/Users/circleci/thirdparty/snappy-1.1.7/build
|
||||
SNAPPY_LIB_DEBUG: C:/Users/circleci/thirdparty/snappy-1.1.7/build/Debug/snappy.lib
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Install thirdparty dependencies"
|
||||
command: |
|
||||
mkdir ${THIRDPARTY_HOME}
|
||||
cd ${THIRDPARTY_HOME}
|
||||
echo "Installing CMake..."
|
||||
curl --fail --silent --show-error --output cmake-3.16.4-win64-x64.zip --location https://github.com/Kitware/CMake/releases/download/v3.16.4/cmake-3.16.4-win64-x64.zip
|
||||
unzip -q cmake-3.16.4-win64-x64.zip
|
||||
echo "Building Snappy dependency..."
|
||||
curl --fail --silent --show-error --output snappy-1.1.7.zip --location https://github.com/google/snappy/archive/1.1.7.zip
|
||||
unzip -q snappy-1.1.7.zip
|
||||
cd snappy-1.1.7
|
||||
mkdir build
|
||||
cd build
|
||||
${CMAKE_BIN} -G "${CMAKE_GENERATOR}" ..
|
||||
msbuild.exe Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
- run:
|
||||
name: "Build RocksDB"
|
||||
command: |
|
||||
mkdir build
|
||||
cd build
|
||||
${CMAKE_BIN} -G "${CMAKE_GENERATOR}" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DJNI=1 ..
|
||||
cd ..
|
||||
msbuild.exe build/rocksdb.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
- run:
|
||||
name: "Test RocksDB"
|
||||
shell: powershell.exe
|
||||
command: |
|
||||
build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test,db_test2,env_basic_test,env_test,db_merge_operand_test -Concurrency 16
|
||||
|
||||
workflows:
|
||||
build-linux:
|
||||
jobs:
|
||||
- build-linux
|
||||
build-linux-lite:
|
||||
jobs:
|
||||
- build-linux-lite
|
||||
build-linux-release:
|
||||
jobs:
|
||||
- build-linux-release
|
||||
build-linux-lite-release:
|
||||
jobs:
|
||||
- build-linux-lite-release
|
||||
build-linux-clang-no-test:
|
||||
jobs:
|
||||
- build-linux-clang-no-test
|
||||
build-linux-clang10-no-test:
|
||||
jobs:
|
||||
- build-linux-clang10-no-test
|
||||
build-linux-cmake:
|
||||
jobs:
|
||||
- build-linux-cmake
|
||||
build-windows:
|
||||
jobs:
|
||||
- build-windows
|
||||
@@ -0,0 +1,41 @@
|
||||
name: Check buck targets and code format
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
check:
|
||||
name: Check TARGETS file and code format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout feature branch
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Fetch from upstream
|
||||
run: |
|
||||
git remote add upstream https://github.com/facebook/rocksdb.git && git fetch upstream
|
||||
|
||||
- name: Where am I
|
||||
run: |
|
||||
echo git status && git status
|
||||
echo "git remote -v" && git remote -v
|
||||
echo git branch && git branch
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v1
|
||||
|
||||
- name: Install Dependencies
|
||||
run: python -m pip install --upgrade pip
|
||||
|
||||
- name: Install argparse
|
||||
run: pip install argparse
|
||||
|
||||
- name: Download clang-format-diff.py
|
||||
uses: wei/wget@v1
|
||||
with:
|
||||
args: https://raw.githubusercontent.com/llvm-mirror/clang/master/tools/clang-format/clang-format-diff.py
|
||||
|
||||
- name: Check format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
|
||||
- name: Compare buckify output
|
||||
run: make check-buck-targets
|
||||
@@ -34,6 +34,7 @@ manifest_dump
|
||||
sst_dump
|
||||
blob_dump
|
||||
block_cache_trace_analyzer
|
||||
db_with_timestamp_basic_test
|
||||
tools/block_cache_analyzer/*.pyc
|
||||
column_aware_encoding_exp
|
||||
util/build_version.cc
|
||||
@@ -82,3 +83,7 @@ fbcode/
|
||||
fbcode
|
||||
buckifier/*.pyc
|
||||
buckifier/__pycache__
|
||||
|
||||
compile_commands.json
|
||||
clang-format-diff.py
|
||||
.py3/
|
||||
|
||||
+209
-20
@@ -3,12 +3,14 @@ language: cpp
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
arch:
|
||||
- amd64
|
||||
- arm64
|
||||
- ppc64le
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
osx_image: xcode9.4
|
||||
jdk:
|
||||
- openjdk7
|
||||
cache:
|
||||
- ccache
|
||||
|
||||
@@ -48,26 +50,174 @@ env:
|
||||
- JOB_NAME=examples # 5-7 minutes
|
||||
- JOB_NAME=cmake # 3-5 minutes
|
||||
- JOB_NAME=cmake-gcc8 # 3-5 minutes
|
||||
- JOB_NAME=cmake-gcc9 # 3-5 minutes
|
||||
- JOB_NAME=cmake-gcc9-c++20 # 3-5 minutes
|
||||
- JOB_NAME=cmake-mingw # 3 minutes
|
||||
- JOB_NAME=make-gcc4.8
|
||||
- JOB_NAME=status_checked
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
env: TEST_GROUP=1
|
||||
- os: osx
|
||||
env: TEST_GROUP=2
|
||||
- os: osx
|
||||
env: TEST_GROUP=3
|
||||
- os: osx
|
||||
env: TEST_GROUP=4
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- os : osx
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: osx
|
||||
env: JOB_NAME=make-gcc4.8
|
||||
- os: osx
|
||||
arch: ppc64le
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
- os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=make-gcc4.8
|
||||
- os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=make-gcc4.8
|
||||
- os: linux
|
||||
compiler: clang
|
||||
- os : osx
|
||||
compiler: gcc
|
||||
# Exclude all but most unique cmake variants for pull requests, but build all in branches
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: amd64
|
||||
env: JOB_NAME=cmake
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: amd64
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: amd64
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
# Exclude most osx, arm64 and ppc64le tests for pull requests, but build in branches
|
||||
# Temporarily disable ppc64le unit tests in PRs until Travis gets its act together (#6653)
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=platform_dependent
|
||||
# NB: the cmake build is a partial java test
|
||||
- if: type = pull_request
|
||||
os: osx
|
||||
env: TEST_GROUP=1
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: TEST_GROUP=1
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=1
|
||||
- if: type = pull_request
|
||||
os: osx
|
||||
env: TEST_GROUP=2
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: TEST_GROUP=2
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=2
|
||||
- if: type = pull_request
|
||||
os: osx
|
||||
env: TEST_GROUP=3
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: TEST_GROUP=3
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=3
|
||||
- if: type = pull_request
|
||||
os: osx
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request AND commit_message !~ /java/
|
||||
os : osx
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request AND commit_message !~ /java/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request AND commit_message !~ /java/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request
|
||||
os : osx
|
||||
env: JOB_NAME=lite_build
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=lite_build
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=lite_build
|
||||
- if: type = pull_request
|
||||
os : osx
|
||||
env: JOB_NAME=examples
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=examples
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=examples
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- if: type = pull_request
|
||||
os : osx
|
||||
env: JOB_NAME=status_checked
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=status_checked
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=status_checked
|
||||
|
||||
install:
|
||||
- if [ "${TRAVIS_OS_NAME}" == osx ]; then
|
||||
@@ -77,14 +227,41 @@ install:
|
||||
sudo apt-get install -y g++-8;
|
||||
CC=gcc-8 && CXX=g++-8;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == cmake-gcc9 ] || [ "${JOB_NAME}" == cmake-gcc9-c++20 ]; then
|
||||
sudo apt-get install -y g++-9;
|
||||
CC=gcc-9 && CXX=g++-9;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == cmake-mingw ]; then
|
||||
sudo apt-get install -y mingw-w64 ;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == cmake* ]] && [ "${TRAVIS_OS_NAME}" == linux ]; then
|
||||
mkdir cmake-dist && curl --silent --fail --show-error --location https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.tar.gz | tar --strip-components=1 -C cmake-dist -xz && export PATH=$PWD/cmake-dist/bin:$PATH;
|
||||
- if [ "${JOB_NAME}" == make-gcc4.8 ]; then
|
||||
sudo apt-get install -y g++-4.8 ;
|
||||
CC=gcc-4.8 && CXX=g++-4.8;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == java_test ]]; then
|
||||
java -version && echo "JAVA_HOME=${JAVA_HOME}";
|
||||
- if [[ "${JOB_NAME}" == cmake* ]] && [ "${TRAVIS_OS_NAME}" == linux ]; then
|
||||
CMAKE_DIST_URL="https://rocksdb-deps.s3-us-west-2.amazonaws.com/cmake/cmake-3.14.5-Linux-$(uname -m).tar.bz2";
|
||||
TAR_OPT="--strip-components=1 -xj";
|
||||
if [ "aarch64" == "$(uname -m)" ]; then
|
||||
sudo apt-get install -y libuv1 librhash0;
|
||||
sudo apt-get upgrade -y libstdc++6;
|
||||
fi;
|
||||
mkdir cmake-dist && curl --silent --fail --show-error --location "${CMAKE_DIST_URL}" | tar -C cmake-dist ${TAR_OPT} && export PATH=$PWD/cmake-dist/bin:$PATH;
|
||||
fi
|
||||
- |
|
||||
if [[ "${JOB_NAME}" == java_test || "${JOB_NAME}" == cmake* ]]; then
|
||||
# Ensure JDK 8
|
||||
if [ "${TRAVIS_OS_NAME}" == osx ]; then
|
||||
brew tap AdoptOpenJDK/openjdk
|
||||
brew cask install adoptopenjdk8
|
||||
export JAVA_HOME=$(/usr/libexec/java_home)
|
||||
else
|
||||
sudo apt-get install -y openjdk-8-jdk
|
||||
export PATH=/usr/lib/jvm/java-8-openjdk-$(dpkg --print-architecture)/bin:$PATH
|
||||
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-$(dpkg --print-architecture)
|
||||
fi
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
fi
|
||||
|
||||
before_script:
|
||||
@@ -93,7 +270,7 @@ before_script:
|
||||
- ulimit -n 8192
|
||||
|
||||
script:
|
||||
- ${CXX} --version
|
||||
- date; ${CXX} --version
|
||||
- if [ `command -v ccache` ]; then ccache -C; fi
|
||||
- case $TEST_GROUP in
|
||||
platform_dependent)
|
||||
@@ -117,7 +294,7 @@ script:
|
||||
OPT=-DTRAVIS V=1 make rocksdbjava jtest
|
||||
;;
|
||||
lite_build)
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' V=1 make -j4 static_lib tools
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' V=1 make -j4 all
|
||||
;;
|
||||
examples)
|
||||
OPT=-DTRAVIS V=1 make -j4 static_lib && cd examples && make -j4
|
||||
@@ -127,7 +304,19 @@ script:
|
||||
mkdir build && cd build && cmake -DJNI=1 -DWITH_GFLAGS=OFF .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb rocksdbjni
|
||||
;;
|
||||
cmake*)
|
||||
mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_BUILD_TYPE=Release && make -j4 rocksdb rocksdbjni
|
||||
case $JOB_NAME in
|
||||
*-c++20)
|
||||
OPT=-DCMAKE_CXX_STANDARD=20
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=0 -DWITH_GFLAGS=0 -DWITH_BENCHMARK_TOOLS=0 -DWITH_TOOLS=0 -DWITH_CORE_TOOLS=1 .. && make -j4 && cd .. && rm -rf build && mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_BUILD_TYPE=Release $OPT && make -j4 rocksdb rocksdbjni
|
||||
;;
|
||||
make-gcc4.8)
|
||||
OPT=-DTRAVIS V=1 SKIP_LINK=1 make -j4 all && [ "Linking broken because libgflags compiled with newer ABI" ]
|
||||
;;
|
||||
status_checked)
|
||||
OPT=-DTRAVIS V=1 ASSERT_STATUS_CHECKED=1 make -j4 check_some
|
||||
;;
|
||||
esac
|
||||
notifications:
|
||||
|
||||
+139
-41
@@ -83,6 +83,10 @@ else()
|
||||
option(WITH_FOLLY_DISTRIBUTED_MUTEX "build with folly::DistributedMutex" OFF)
|
||||
endif()
|
||||
|
||||
if( NOT DEFINED CMAKE_CXX_STANDARD )
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
endif()
|
||||
|
||||
include(CMakeDependentOption)
|
||||
CMAKE_DEPENDENT_OPTION(WITH_GFLAGS "build with GFlags" ON
|
||||
"NOT MSVC;NOT MINGW" OFF)
|
||||
@@ -91,7 +95,7 @@ if(MSVC)
|
||||
option(WITH_XPRESS "build with windows built in compression" OFF)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
else()
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD" AND NOT CMAKE_SYSTEM_NAME MATCHES "kFreeBSD")
|
||||
# FreeBSD has jemalloc as default malloc
|
||||
# but it does not have all the jemalloc files in include/...
|
||||
set(WITH_JEMALLOC ON)
|
||||
@@ -103,18 +107,32 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# No config file for this
|
||||
if(WITH_GFLAGS)
|
||||
find_package(gflags REQUIRED)
|
||||
# Config with namespace available since gflags 2.2.2
|
||||
option(GFLAGS_USE_TARGET_NAMESPACE "Use gflags import target with namespace." ON)
|
||||
find_package(gflags CONFIG)
|
||||
if(gflags_FOUND)
|
||||
if(TARGET ${GFLAGS_TARGET})
|
||||
# Config with GFLAGS_TARGET available since gflags 2.2.0
|
||||
list(APPEND THIRDPARTY_LIBS ${GFLAGS_TARGET})
|
||||
else()
|
||||
# Config with GFLAGS_LIBRARIES available since gflags 2.1.0
|
||||
list(APPEND THIRDPARTY_LIBS ${GFLAGS_LIBRARIES})
|
||||
endif()
|
||||
else()
|
||||
find_package(gflags REQUIRED)
|
||||
list(APPEND THIRDPARTY_LIBS gflags::gflags)
|
||||
endif()
|
||||
add_definitions(-DGFLAGS=1)
|
||||
include_directories(${gflags_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS gflags::gflags)
|
||||
endif()
|
||||
|
||||
if(WITH_SNAPPY)
|
||||
find_package(snappy REQUIRED)
|
||||
find_package(Snappy CONFIG)
|
||||
if(NOT Snappy_FOUND)
|
||||
find_package(Snappy REQUIRED)
|
||||
endif()
|
||||
add_definitions(-DSNAPPY)
|
||||
list(APPEND THIRDPARTY_LIBS snappy::snappy)
|
||||
list(APPEND THIRDPARTY_LIBS Snappy::snappy)
|
||||
endif()
|
||||
|
||||
if(WITH_ZLIB)
|
||||
@@ -191,7 +209,6 @@ else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format -fno-asynchronous-unwind-tables")
|
||||
add_definitions(-D_POSIX_C_SOURCE=1)
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")
|
||||
include(CheckCXXCompilerFlag)
|
||||
@@ -203,16 +220,25 @@ else()
|
||||
endif()
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64")
|
||||
CHECK_C_COMPILER_FLAG("-mcpu=power9" HAS_POWER9)
|
||||
if(HAS_POWER9)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=power9 -mtune=power9")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=power9 -mtune=power9")
|
||||
else()
|
||||
CHECK_C_COMPILER_FLAG("-mcpu=power8" HAS_POWER8)
|
||||
if(HAS_POWER8)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=power8 -mtune=power8")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=power8 -mtune=power8")
|
||||
endif(HAS_POWER8)
|
||||
endif(HAS_POWER9)
|
||||
CHECK_C_COMPILER_FLAG("-maltivec" HAS_ALTIVEC)
|
||||
if(HAS_ALTIVEC)
|
||||
message(STATUS " HAS_ALTIVEC yes")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maltivec")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maltivec")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=power8")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=power8")
|
||||
endif(HAS_ALTIVEC)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64")
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
|
||||
CHECK_C_COMPILER_FLAG("-march=armv8-a+crc+crypto" HAS_ARMV8_CRC)
|
||||
@@ -235,7 +261,7 @@ else()
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
else()
|
||||
if(NOT HAVE_POWER8 AND NOT HAS_ARMV8_CRC)
|
||||
if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64" AND NOT HAS_ARMV8_CRC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
endif()
|
||||
endif()
|
||||
@@ -245,6 +271,7 @@ include(CheckCXXSourceCompiles)
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
@@ -377,7 +404,15 @@ if(MSVC)
|
||||
message(STATUS "Debug optimization is enabled")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "/Oxt")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1 /Gm")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1")
|
||||
|
||||
# Minimal Build is deprecated after MSVC 2015
|
||||
if( MSVC_VERSION GREATER 1900 )
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gm-")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gm")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
if(WITH_RUNTIME_DEBUG)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /${RUNTIME_LIBRARY}d")
|
||||
@@ -413,6 +448,8 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
add_definitions(-DOS_LINUX)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "SunOS")
|
||||
add_definitions(-DOS_SOLARIS)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "kFreeBSD")
|
||||
add_definitions(-DOS_GNU_KFREEBSD)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
add_definitions(-DOS_FREEBSD)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "NetBSD")
|
||||
@@ -481,9 +518,13 @@ if(HAVE_SCHED_GETCPU)
|
||||
add_definitions(-DROCKSDB_SCHED_GETCPU_PRESENT)
|
||||
endif()
|
||||
|
||||
check_cxx_symbol_exists(getauxval auvx.h HAVE_AUXV_GETAUXVAL)
|
||||
if(HAVE_AUXV_GETAUXVAL)
|
||||
add_definitions(-DROCKSDB_AUXV_GETAUXVAL_PRESENT)
|
||||
endif()
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/third-party/gtest-1.8.1/fused-src)
|
||||
if(WITH_FOLLY_DISTRIBUTED_MUTEX)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third-party/folly)
|
||||
endif()
|
||||
@@ -492,10 +533,17 @@ find_package(Threads REQUIRED)
|
||||
# Main library source code
|
||||
|
||||
set(SOURCES
|
||||
cache/cache.cc
|
||||
cache/clock_cache.cc
|
||||
cache/lru_cache.cc
|
||||
cache/sharded_cache.cc
|
||||
db/arena_wrapped_db_iter.cc
|
||||
db/blob/blob_file_addition.cc
|
||||
db/blob/blob_file_garbage.cc
|
||||
db/blob/blob_file_meta.cc
|
||||
db/blob/blob_log_format.cc
|
||||
db/blob/blob_log_reader.cc
|
||||
db/blob/blob_log_writer.cc
|
||||
db/builder.cc
|
||||
db/c.cc
|
||||
db/column_family.cc
|
||||
@@ -549,6 +597,7 @@ set(SOURCES
|
||||
db/trim_history_scheduler.cc
|
||||
db/version_builder.cc
|
||||
db/version_edit.cc
|
||||
db/version_edit_handler.cc
|
||||
db/version_set.cc
|
||||
db/wal_manager.cc
|
||||
db/write_batch.cc
|
||||
@@ -577,6 +626,7 @@ set(SOURCES
|
||||
memory/arena.cc
|
||||
memory/concurrent_arena.cc
|
||||
memory/jemalloc_nodump_allocator.cc
|
||||
memory/memkind_kmem_allocator.cc
|
||||
memtable/alloc_tracker.cc
|
||||
memtable/hash_linklist_rep.cc
|
||||
memtable/hash_skiplist_rep.cc
|
||||
@@ -601,15 +651,17 @@ set(SOURCES
|
||||
options/options.cc
|
||||
options/options_helper.cc
|
||||
options/options_parser.cc
|
||||
options/options_sanity_check.cc
|
||||
port/stack_trace.cc
|
||||
table/adaptive/adaptive_table_factory.cc
|
||||
table/block_based/binary_search_index_reader.cc
|
||||
table/block_based/block.cc
|
||||
table/block_based/block_based_filter_block.cc
|
||||
table/block_based/block_based_table_builder.cc
|
||||
table/block_based/block_based_table_factory.cc
|
||||
table/block_based/block_based_table_iterator.cc
|
||||
table/block_based/block_based_table_reader.cc
|
||||
table/block_based/block_builder.cc
|
||||
table/block_based/block_prefetcher.cc
|
||||
table/block_based/block_prefix_index.cc
|
||||
table/block_based/data_block_hash_index.cc
|
||||
table/block_based/data_block_footer.cc
|
||||
@@ -617,9 +669,14 @@ set(SOURCES
|
||||
table/block_based/filter_policy.cc
|
||||
table/block_based/flush_block_policy.cc
|
||||
table/block_based/full_filter_block.cc
|
||||
table/block_based/hash_index_reader.cc
|
||||
table/block_based/index_builder.cc
|
||||
table/block_based/index_reader_common.cc
|
||||
table/block_based/parsed_full_filter_block.cc
|
||||
table/block_based/partitioned_filter_block.cc
|
||||
table/block_based/partitioned_index_iterator.cc
|
||||
table/block_based/partitioned_index_reader.cc
|
||||
table/block_based/reader_common.cc
|
||||
table/block_based/uncompression_dict_reader.cc
|
||||
table/block_fetcher.cc
|
||||
table/cuckoo/cuckoo_table_builder.cc
|
||||
@@ -678,9 +735,6 @@ set(SOURCES
|
||||
utilities/blob_db/blob_db_impl_filesnapshot.cc
|
||||
utilities/blob_db/blob_dump_tool.cc
|
||||
utilities/blob_db/blob_file.cc
|
||||
utilities/blob_db/blob_log_reader.cc
|
||||
utilities/blob_db/blob_log_writer.cc
|
||||
utilities/blob_db/blob_log_format.cc
|
||||
utilities/cassandra/cassandra_compaction_filter.cc
|
||||
utilities/cassandra/format.cc
|
||||
utilities/cassandra/merge_operator.cc
|
||||
@@ -734,11 +788,11 @@ if(HAVE_SSE42 AND NOT MSVC)
|
||||
PROPERTIES COMPILE_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
|
||||
if(HAVE_POWER8)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64")
|
||||
list(APPEND SOURCES
|
||||
util/crc32c_ppc.c
|
||||
util/crc32c_ppc_asm.S)
|
||||
endif(HAVE_POWER8)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64")
|
||||
|
||||
if(HAS_ARMV8_CRC)
|
||||
list(APPEND SOURCES
|
||||
@@ -751,9 +805,13 @@ if(WIN32)
|
||||
port/win/env_win.cc
|
||||
port/win/env_default.cc
|
||||
port/win/port_win.cc
|
||||
port/win/win_logger.cc
|
||||
port/win/win_thread.cc)
|
||||
|
||||
port/win/win_logger.cc)
|
||||
if(NOT MINGW)
|
||||
# Mingw only supports std::thread when using
|
||||
# posix threads.
|
||||
list(APPEND SOURCES
|
||||
port/win/win_thread.cc)
|
||||
endif()
|
||||
if(WITH_XPRESS)
|
||||
list(APPEND SOURCES
|
||||
port/win/xpress_win.cc)
|
||||
@@ -800,12 +858,12 @@ else()
|
||||
endif()
|
||||
|
||||
add_library(${ROCKSDB_STATIC_LIB} STATIC ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB}
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB} PRIVATE
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(ROCKSDB_BUILD_SHARED)
|
||||
add_library(${ROCKSDB_SHARED_LIB} SHARED ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_SHARED_LIB}
|
||||
target_link_libraries(${ROCKSDB_SHARED_LIB} PRIVATE
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(WIN32)
|
||||
@@ -822,7 +880,6 @@ if(ROCKSDB_BUILD_SHARED)
|
||||
LINKER_LANGUAGE CXX
|
||||
VERSION ${rocksdb_VERSION}
|
||||
SOVERSION ${rocksdb_VERSION_MAJOR}
|
||||
CXX_STANDARD 11
|
||||
OUTPUT_NAME "rocksdb")
|
||||
endif()
|
||||
endif()
|
||||
@@ -834,6 +891,16 @@ else()
|
||||
endif()
|
||||
|
||||
option(WITH_JNI "build with JNI" OFF)
|
||||
# Tests are excluded from Release builds
|
||||
CMAKE_DEPENDENT_OPTION(WITH_TESTS "build with tests" ON
|
||||
"CMAKE_BUILD_TYPE STREQUAL Debug" OFF)
|
||||
option(WITH_BENCHMARK_TOOLS "build with benchmarks" ON)
|
||||
option(WITH_CORE_TOOLS "build with ldb and sst_dump" ON)
|
||||
option(WITH_TOOLS "build with tools" ON)
|
||||
|
||||
if(WITH_TESTS OR WITH_BENCHMARK_TOOLS OR WITH_TOOLS OR WITH_JNI OR JNI)
|
||||
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/third-party/gtest-1.8.1/fused-src)
|
||||
endif()
|
||||
if(WITH_JNI OR JNI)
|
||||
message(STATUS "JNI library is enabled")
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/java)
|
||||
@@ -871,6 +938,8 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
|
||||
install(DIRECTORY include/rocksdb COMPONENT devel DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
|
||||
install(DIRECTORY "${PROJECT_SOURCE_DIR}/cmake/modules" COMPONENT devel DESTINATION ${package_config_destination})
|
||||
|
||||
install(
|
||||
TARGETS ${ROCKSDB_STATIC_LIB}
|
||||
EXPORT RocksDBTargets
|
||||
@@ -907,18 +976,24 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
)
|
||||
endif()
|
||||
|
||||
# Tests are excluded from Release builds
|
||||
CMAKE_DEPENDENT_OPTION(WITH_TESTS "build with tests" ON
|
||||
"CMAKE_BUILD_TYPE STREQUAL Debug" OFF)
|
||||
option(WITH_ALL_TESTS "Build all test, rather than a small subset" ON)
|
||||
|
||||
if(WITH_TESTS)
|
||||
add_subdirectory(third-party/gtest-1.8.1/fused-src/gtest)
|
||||
add_library(testharness STATIC
|
||||
test_util/testharness.cc)
|
||||
target_link_libraries(testharness gtest)
|
||||
|
||||
set(TESTS
|
||||
db/db_basic_test.cc
|
||||
env/env_basic_test.cc
|
||||
)
|
||||
if(WITH_ALL_TESTS)
|
||||
list(APPEND TESTS
|
||||
cache/cache_test.cc
|
||||
cache/lru_cache_test.cc
|
||||
db/blob/blob_file_addition_test.cc
|
||||
db/blob/blob_file_garbage_test.cc
|
||||
db/blob/db_blob_index_test.cc
|
||||
db/column_family_test.cc
|
||||
db/compact_files_test.cc
|
||||
db/compaction/compaction_job_stats_test.cc
|
||||
@@ -928,8 +1003,7 @@ if(WITH_TESTS)
|
||||
db/comparator_db_test.cc
|
||||
db/corruption_test.cc
|
||||
db/cuckoo_table_db_test.cc
|
||||
db/db_basic_test.cc
|
||||
db/db_blob_index_test.cc
|
||||
db/db_with_timestamp_basic_test.cc
|
||||
db/db_block_cache_test.cc
|
||||
db/db_bloom_filter_test.cc
|
||||
db/db_compaction_filter_test.cc
|
||||
@@ -955,12 +1029,14 @@ if(WITH_TESTS)
|
||||
db/db_tailing_iter_test.cc
|
||||
db/db_test.cc
|
||||
db/db_test2.cc
|
||||
db/db_logical_block_size_cache_test.cc
|
||||
db/db_universal_compaction_test.cc
|
||||
db/db_wal_test.cc
|
||||
db/db_with_timestamp_compaction_test.cc
|
||||
db/db_write_test.cc
|
||||
db/dbformat_test.cc
|
||||
db/deletefile_test.cc
|
||||
db/error_handler_test.cc
|
||||
db/error_handler_fs_test.cc
|
||||
db/obsolete_files_test.cc
|
||||
db/external_sst_file_basic_test.cc
|
||||
db/external_sst_file_test.cc
|
||||
@@ -989,14 +1065,16 @@ if(WITH_TESTS)
|
||||
db/write_batch_test.cc
|
||||
db/write_callback_test.cc
|
||||
db/write_controller_test.cc
|
||||
env/env_basic_test.cc
|
||||
env/env_test.cc
|
||||
env/io_posix_test.cc
|
||||
env/mock_env_test.cc
|
||||
file/delete_scheduler_test.cc
|
||||
file/random_access_file_reader_test.cc
|
||||
logging/auto_roll_logger_test.cc
|
||||
logging/env_logger_test.cc
|
||||
logging/event_logger_test.cc
|
||||
memory/arena_test.cc
|
||||
memory/memkind_kmem_allocator_test.cc
|
||||
memtable/inlineskiplist_test.cc
|
||||
memtable/skiplist_test.cc
|
||||
memtable/write_buffer_manager_test.cc
|
||||
@@ -1007,6 +1085,7 @@ if(WITH_TESTS)
|
||||
options/options_settable_test.cc
|
||||
options/options_test.cc
|
||||
table/block_based/block_based_filter_block_test.cc
|
||||
table/block_based/block_based_table_reader_test.cc
|
||||
table/block_based/block_test.cc
|
||||
table/block_based/data_block_hash_index_test.cc
|
||||
table/block_based/full_filter_block_test.cc
|
||||
@@ -1017,6 +1096,8 @@ if(WITH_TESTS)
|
||||
table/merger_test.cc
|
||||
table/sst_file_reader_test.cc
|
||||
table/table_test.cc
|
||||
table/block_fetcher_test.cc
|
||||
test_util/testutil_test.cc
|
||||
tools/block_cache_analyzer/block_cache_trace_analyzer_test.cc
|
||||
tools/ldb_cmd_test.cc
|
||||
tools/reduce_levels_test.cc
|
||||
@@ -1038,8 +1119,10 @@ if(WITH_TESTS)
|
||||
util/slice_test.cc
|
||||
util/slice_transform_test.cc
|
||||
util/timer_queue_test.cc
|
||||
util/timer_test.cc
|
||||
util/thread_list_test.cc
|
||||
util/thread_local_test.cc
|
||||
util/work_queue_test.cc
|
||||
utilities/backupable/backupable_db_test.cc
|
||||
utilities/blob_db/blob_db_test.cc
|
||||
utilities/cassandra/cassandra_functional_test.cc
|
||||
@@ -1059,11 +1142,13 @@ if(WITH_TESTS)
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
utilities/transactions/transaction_test.cc
|
||||
utilities/transactions/transaction_lock_mgr_test.cc
|
||||
utilities/transactions/write_prepared_transaction_test.cc
|
||||
utilities/transactions/write_unprepared_transaction_test.cc
|
||||
utilities/ttl/ttl_test.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_test.cc
|
||||
)
|
||||
)
|
||||
endif()
|
||||
if(WITH_LIBRADOS)
|
||||
list(APPEND TESTS utilities/env_librados_test.cc)
|
||||
endif()
|
||||
@@ -1077,6 +1162,7 @@ if(WITH_TESTS)
|
||||
monitoring/thread_status_updater_debug.cc
|
||||
table/mock_table.cc
|
||||
test_util/fault_injection_test_env.cc
|
||||
test_util/fault_injection_test_fs.cc
|
||||
utilities/cassandra/test_utils.cc
|
||||
)
|
||||
enable_testing()
|
||||
@@ -1091,7 +1177,7 @@ if(WITH_TESTS)
|
||||
PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD_RELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_RELWITHDEBINFO 1
|
||||
)
|
||||
)
|
||||
|
||||
foreach(sourcefile ${TESTS})
|
||||
get_filename_component(exename ${sourcefile} NAME_WE)
|
||||
@@ -1101,12 +1187,16 @@ if(WITH_TESTS)
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_RELWITHDEBINFO 1
|
||||
OUTPUT_NAME ${exename}${ARTIFACT_SUFFIX}
|
||||
)
|
||||
)
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} testharness gtest ${ROCKSDB_LIB})
|
||||
if(NOT "${exename}" MATCHES "db_sanity_test")
|
||||
add_test(NAME ${exename} COMMAND ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX})
|
||||
endif()
|
||||
if("${exename}" MATCHES "env_librados_test")
|
||||
# env_librados_test.cc uses librados directly
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX} rados)
|
||||
endif()
|
||||
endforeach(sourcefile ${TESTS})
|
||||
|
||||
if(WIN32)
|
||||
@@ -1130,7 +1220,6 @@ if(WITH_TESTS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(WITH_BENCHMARK_TOOLS "build with benchmarks" ON)
|
||||
if(WITH_BENCHMARK_TOOLS)
|
||||
add_executable(db_bench
|
||||
tools/db_bench.cc
|
||||
@@ -1169,10 +1258,19 @@ if(WITH_BENCHMARK_TOOLS)
|
||||
${ROCKSDB_LIB})
|
||||
endif()
|
||||
|
||||
option(WITH_TOOLS "build with tools" ON)
|
||||
if(WITH_TOOLS)
|
||||
if(WITH_CORE_TOOLS OR WITH_TOOLS)
|
||||
add_subdirectory(tools)
|
||||
add_custom_target(core_tools
|
||||
DEPENDS ${core_tool_deps})
|
||||
endif()
|
||||
|
||||
if(WITH_TOOLS)
|
||||
add_subdirectory(db_stress_tool)
|
||||
add_custom_target(tools
|
||||
DEPENDS ${tool_deps})
|
||||
endif()
|
||||
|
||||
option(WITH_EXAMPLES "build with examples" OFF)
|
||||
if(WITH_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
+114
@@ -1,5 +1,119 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
### Public API Change
|
||||
* Encryption file classes now exposed for inheritance in env_encryption.h
|
||||
|
||||
### Behavior Changes
|
||||
* Best-efforts recovery ignores CURRENT file completely. If CURRENT file is missing during recovery, best-efforts recovery still proceeds with MANIFEST file(s).
|
||||
* In best-efforts recovery, an error that is not Corruption or IOError::kNotFound or IOError::kPathNotFound will be overwritten silently. Fix this by checking all non-ok cases and return early.
|
||||
* Compressed block cache was automatically disabled with read-only DBs by mistake. Now it is fixed: compressed block cache will be in effective with read-only DB too.
|
||||
* Fix a bug of wrong iterator result if another thread finishes an update and a DB flush between two statement.
|
||||
|
||||
### Public API Change
|
||||
* `DB::GetDbSessionId(std::string& session_id)` is added. `session_id` stores a unique identifier that gets reset every time the DB is opened. This DB session ID should be unique among all open DB instances on all hosts, and should be unique among re-openings of the same or other DBs. This identifier is recorded in the LOG file on the line starting with "DB Session ID:".
|
||||
|
||||
### New Features
|
||||
* DB identity (`db_id`) and DB session identity (`db_session_id`) are added to table properties and stored in SST files. SST files generated from SstFileWriter and Repairer have DB identity “SST Writer” and “DB Repairer”, respectively. Their DB session IDs are generated in the same way as `DB::GetDbSessionId`. The session ID for SstFileWriter (resp., Repairer) resets every time `SstFileWriter::Open` (resp., `Repairer::Run`) is called.
|
||||
* Added experimental option BlockBasedTableOptions::optimize_filters_for_memory for reducing allocated memory size of Bloom filters (~10% savings with Jemalloc) while preserving the same general accuracy. To have an effect, the option requires format_version=5 and malloc_usable_size. Enabling this option is forward and backward compatible with existing format_version=5.
|
||||
|
||||
### Bug Fixes
|
||||
* Fail recovery and report once hitting a physical log record checksum mismatch, while reading MANIFEST. RocksDB should not continue processing the MANIFEST any further.
|
||||
|
||||
## 6.11 (6/12/2020)
|
||||
### Bug Fixes
|
||||
* Fix consistency checking error swallowing in some cases when options.force_consistency_checks = true.
|
||||
* Fix possible false NotFound status from batched MultiGet using index type kHashSearch.
|
||||
* Fix corruption caused by enabling delete triggered compaction (NewCompactOnDeletionCollectorFactory) in universal compaction mode, along with parallel compactions. The bug can result in two parallel compactions picking the same input files, resulting in the DB resurrecting older and deleted versions of some keys.
|
||||
* Fix a use-after-free bug in best-efforts recovery. column_family_memtables_ needs to point to valid ColumnFamilySet.
|
||||
* Let best-efforts recovery ignore corrupted files during table loading.
|
||||
* Fix corrupt key read from ingested file when iterator direction switches from reverse to forward at a key that is a prefix of another key in the same file. It is only possible in files with a non-zero global seqno.
|
||||
* Fix abnormally large estimate from GetApproximateSizes when a range starts near the end of one SST file and near the beginning of another. Now GetApproximateSizes consistently and fairly includes the size of SST metadata in addition to data blocks, attributing metadata proportionally among the data blocks based on their size.
|
||||
* Fix potential file descriptor leakage in PosixEnv's IsDirectory() and NewRandomAccessFile().
|
||||
* Fix false negative from the VerifyChecksum() API when there is a checksum mismatch in an index partition block in a BlockBasedTable format table file (index_type is kTwoLevelIndexSearch).
|
||||
* Fix sst_dump to return non-zero exit code if the specified file is not a recognized SST file or fails requested checks.
|
||||
* Fix incorrect results from batched MultiGet for duplicate keys, when the duplicate key matches the largest key of an SST file and the value type for the key in the file is a merge value.
|
||||
|
||||
### Public API Change
|
||||
* Flush(..., column_family) may return Status::ColumnFamilyDropped() instead of Status::InvalidArgument() if column_family is dropped while processing the flush request.
|
||||
* BlobDB now explicitly disallows using the default column family's storage directories as blob directory.
|
||||
* DeleteRange now returns `Status::InvalidArgument` if the range's end key comes before its start key according to the user comparator. Previously the behavior was undefined.
|
||||
* ldb now uses options.force_consistency_checks = true by default and "--disable_consistency_checks" is added to disable it.
|
||||
* DB::OpenForReadOnly no longer creates files or directories if the named DB does not exist, unless create_if_missing is set to true.
|
||||
* The consistency checks that validate LSM state changes (table file additions/deletions during flushes and compactions) are now stricter, more efficient, and no longer optional, i.e. they are performed even if `force_consistency_checks` is `false`.
|
||||
* Disable delete triggered compaction (NewCompactOnDeletionCollectorFactory) in universal compaction mode and num_levels = 1 in order to avoid a corruption bug.
|
||||
* `pin_l0_filter_and_index_blocks_in_cache` no longer applies to L0 files larger than `1.5 * write_buffer_size` to give more predictable memory usage. Such L0 files may exist due to intra-L0 compaction, external file ingestion, or user dynamically changing `write_buffer_size` (note, however, that files that are already pinned will continue being pinned, even after such a dynamic change).
|
||||
* In point-in-time wal recovery mode, fail database recovery in case of IOError while reading the WAL to avoid data loss.
|
||||
* A new method `Env::LowerThreadPoolCPUPriority(Priority, CpuPriority)` is added to `Env` to be able to lower to a specific priority such as `CpuPriority::kIdle`.
|
||||
|
||||
### New Features
|
||||
* sst_dump to add a new --readahead_size argument. Users can specify read size when scanning the data. Sst_dump also tries to prefetch tail part of the SST files so usually some number of I/Os are saved there too.
|
||||
* Generate file checksum in SstFileWriter if Options.file_checksum_gen_factory is set. The checksum and checksum function name are stored in ExternalSstFileInfo after the sst file write is finished.
|
||||
* Add a value_size_soft_limit in read options which limits the cumulative value size of keys read in batches in MultiGet. Once the cumulative value size of found keys exceeds read_options.value_size_soft_limit, all the remaining keys are returned with status Abort without further finding their values. By default the value_size_soft_limit is std::numeric_limits<uint64_t>::max().
|
||||
* Enable SST file ingestion with file checksum information when calling IngestExternalFiles(const std::vector<IngestExternalFileArg>& args). Added files_checksums and files_checksum_func_names to IngestExternalFileArg such that user can ingest the sst files with their file checksum information. Added verify_file_checksum to IngestExternalFileOptions (default is True). To be backward compatible, if DB does not enable file checksum or user does not provide checksum information (vectors of files_checksums and files_checksum_func_names are both empty), verification of file checksum is always sucessful. If DB enables file checksum, DB will always generate the checksum for each ingested SST file during Prepare stage of ingestion and store the checksum in Manifest, unless verify_file_checksum is False and checksum information is provided by the application. In this case, we only verify the checksum function name and directly store the ingested checksum in Manifest. If verify_file_checksum is set to True, DB will verify the ingested checksum and function name with the genrated ones. Any mismatch will fail the ingestion. Note that, if IngestExternalFileOptions::write_global_seqno is True, the seqno will be changed in the ingested file. Therefore, the checksum of the file will be changed. In this case, a new checksum will be generated after the seqno is updated and be stored in the Manifest.
|
||||
|
||||
### Performance Improvements
|
||||
* Eliminate redundant key comparisons during random access in block-based tables.
|
||||
|
||||
## 6.10 (5/2/2020)
|
||||
### Bug Fixes
|
||||
* Fix wrong result being read from ingested file. May happen when a key in the file happen to be prefix of another key also in the file. The issue can further cause more data corruption. The issue exists with rocksdb >= 5.0.0 since DB::IngestExternalFile() was introduced.
|
||||
* Finish implementation of BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey. It's now ready for use. Significantly reduces read amplification in some setups, especially for iterator seeks.
|
||||
* Fix a bug by updating CURRENT file so that it points to the correct MANIFEST file after best-efforts recovery.
|
||||
* Fixed a bug where ColumnFamilyHandle objects were not cleaned up in case an error happened during BlobDB's open after the base DB had been opened.
|
||||
* Fix a potential undefined behavior caused by trying to dereference nullable pointer (timestamp argument) in DB::MultiGet.
|
||||
* Fix a bug caused by not including user timestamp in MultiGet LookupKey construction. This can lead to wrong query result since the trailing bytes of a user key, if not shorter than timestamp, will be mistaken for user timestamp.
|
||||
* Fix a bug caused by using wrong compare function when sorting the input keys of MultiGet with timestamps.
|
||||
* Upgraded version of bzip library (1.0.6 -> 1.0.8) used with RocksJava to address potential vulnerabilities if an attacker can manipulate compressed data saved and loaded by RocksDB (not normal). See issue #6703.
|
||||
|
||||
### Public API Change
|
||||
* Add a ConfigOptions argument to the APIs dealing with converting options to and from strings and files. The ConfigOptions is meant to replace some of the options (such as input_strings_escaped and ignore_unknown_options) and allow for more parameters to be passed in the future without changing the function signature.
|
||||
* Add NewFileChecksumGenCrc32cFactory to the file checksum public API, such that the builtin Crc32c based file checksum generator factory can be used by applications.
|
||||
* Add IsDirectory to Env and FS to indicate if a path is a directory.
|
||||
|
||||
### New Features
|
||||
* Added support for pipelined & parallel compression optimization for `BlockBasedTableBuilder`. This optimization makes block building, block compression and block appending a pipeline, and uses multiple threads to accelerate block compression. Users can set `CompressionOptions::parallel_threads` greater than 1 to enable compression parallelism. This feature is experimental for now.
|
||||
* Provide an allocator for memkind to be used with block cache. This is to work with memory technologies (Intel DCPMM is one such technology currently available) that require different libraries for allocation and management (such as PMDK and memkind). The high capacities available make it possible to provision large caches (up to several TBs in size) beyond what is achievable with DRAM.
|
||||
* Option `max_background_flushes` can be set dynamically using DB::SetDBOptions().
|
||||
* Added functionality in sst_dump tool to check the compressed file size for different compression levels and print the time spent on compressing files with each compression type. Added arguments `--compression_level_from` and `--compression_level_to` to report size of all compression levels and one compression_type must be specified with it so that it will report compressed sizes of one compression type with different levels.
|
||||
* Added statistics for redundant insertions into block cache: rocksdb.block.cache.*add.redundant. (There is currently no coordination to ensure that only one thread loads a table block when many threads are trying to access that same table block.)
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug when making options.bottommost_compression, options.compression_opts and options.bottommost_compression_opts dynamically changeable: the modified values are not written to option files or returned back to users when being queried.
|
||||
* Fix a bug where index key comparisons were unaccounted in `PerfContext::user_key_comparison_count` for lookups in files written with `format_version >= 3`.
|
||||
* Fix many bloom.filter statistics not being updated in batch MultiGet.
|
||||
|
||||
### Performance Improvements
|
||||
* Improve performance of batch MultiGet with partitioned filters, by sharing block cache lookups to applicable filter blocks.
|
||||
* Reduced memory copies when fetching and uncompressing compressed blocks from sst files.
|
||||
|
||||
## 6.9.0 (03/29/2020)
|
||||
### Behavior changes
|
||||
* Since RocksDB 6.8, ttl-based FIFO compaction can drop a file whose oldest key becomes older than options.ttl while others have not. This fix reverts this and makes ttl-based FIFO compaction use the file's flush time as the criterion. This fix also requires that max_open_files = -1 and compaction_options_fifo.allow_compaction = false to function properly.
|
||||
|
||||
### Public API Change
|
||||
* Fix spelling so that API now has correctly spelled transaction state name `COMMITTED`, while the old misspelled `COMMITED` is still available as an alias.
|
||||
* Updated default format_version in BlockBasedTableOptions from 2 to 4. SST files generated with the new default can be read by RocksDB versions 5.16 and newer, and use more efficient encoding of keys in index blocks.
|
||||
* A new parameter `CreateBackupOptions` is added to both `BackupEngine::CreateNewBackup` and `BackupEngine::CreateNewBackupWithMetadata`, you can decrease CPU priority of `BackupEngine`'s background threads by setting `decrease_background_thread_cpu_priority` and `background_thread_cpu_priority` in `CreateBackupOptions`.
|
||||
* Updated the public API of SST file checksum. Introduce the FileChecksumGenFactory to create the FileChecksumGenerator for each SST file, such that the FileChecksumGenerator is not shared and it can be more general for checksum implementations. Changed the FileChecksumGenerator interface from Value, Extend, and GetChecksum to Update, Finalize, and GetChecksum. Finalize should be only called once after all data is processed to generate the final checksum. Temproal data should be maintained by the FileChecksumGenerator object itself and finally it can return the checksum string.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug where range tombstone blocks in ingested files were cached incorrectly during ingestion. If range tombstones were read from those incorrectly cached blocks, the keys they covered would be exposed.
|
||||
* Fix a data race that might cause crash when calling DB::GetCreationTimeOfOldestFile() by a small chance. The bug was introduced in 6.6 Release.
|
||||
* Fix a bug where a boolean value optimize_filters_for_hits was for max threads when calling load table handles after a flush or compaction. The value is correct to 1. The bug should not cause user visible problems.
|
||||
* Fix a bug which might crash the service when write buffer manager fails to insert the dummy handle to the block cache.
|
||||
|
||||
### Performance Improvements
|
||||
* In CompactRange, for levels starting from 0, if the level does not have any file with any key falling in the specified range, the level is skipped. So instead of always compacting from level 0, the compaction starts from the first level with keys in the specified range until the last such level.
|
||||
* Reduced memory copy when reading sst footer and blobdb in direct IO mode.
|
||||
* When restarting a database with large numbers of sst files, large amount of CPU time is spent on getting logical block size of the sst files, which slows down the starting progress, this inefficiency is optimized away with an internal cache for the logical block sizes.
|
||||
|
||||
### New Features
|
||||
* Basic support for user timestamp in iterator. Seek/SeekToFirst/Next and lower/upper bounds are supported. Reverse iteration is not supported. Merge is not considered.
|
||||
* When file lock failure when the lock is held by the current process, return acquiring time and thread ID in the error message.
|
||||
* Added a new option, best_efforts_recovery (default: false), to allow database to open in a db dir with missing table files. During best efforts recovery, missing table files are ignored, and database recovers to the most recent state without missing table file. Cross-column-family consistency is not guaranteed even if WAL is enabled.
|
||||
* options.bottommost_compression, options.compression_opts and options.bottommost_compression_opts are now dynamically changeable.
|
||||
|
||||
## 6.8.0 (02/24/2020)
|
||||
### Java API Changes
|
||||
* Major breaking changes to Java comparators, toward standardizing on ByteBuffer for performant, locale-neutral operations on keys (#6252).
|
||||
* Added overloads of common API methods using direct ByteBuffers for keys and values (#2283).
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
BASH_EXISTS := $(shell which bash)
|
||||
SHELL := $(shell which bash)
|
||||
# Default to python3. Some distros like CentOS 8 do not have `python`.
|
||||
PYTHON?=$(shell which python3 || which python || echo python3)
|
||||
export PYTHON
|
||||
|
||||
CLEAN_FILES = # deliberately empty, so we can append below.
|
||||
CFLAGS += ${EXTRA_CFLAGS}
|
||||
@@ -165,6 +168,12 @@ else
|
||||
CXXFLAGS += -fno-rtti
|
||||
endif
|
||||
|
||||
ifdef ASSERT_STATUS_CHECKED
|
||||
ifeq ($(filter -DROCKSDB_ASSERT_STATUS_CHECKED,$(OPT)),)
|
||||
OPT += -DROCKSDB_ASSERT_STATUS_CHECKED
|
||||
endif
|
||||
endif
|
||||
|
||||
$(warning Warning: Compiling in debug mode. Don't use the resulting binary in production)
|
||||
endif
|
||||
|
||||
@@ -186,12 +195,16 @@ AM_V_CC = $(am__v_CC_$(V))
|
||||
am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_CC_0 = @echo " CC " $@;
|
||||
am__v_CC_1 =
|
||||
CCLD = $(CC)
|
||||
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
|
||||
AM_V_CCLD = $(am__v_CCLD_$(V))
|
||||
am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
|
||||
ifneq ($(SKIP_LINK), 1)
|
||||
am__v_CCLD_0 = @echo " CCLD " $@;
|
||||
am__v_CCLD_1 =
|
||||
else
|
||||
am__v_CCLD_0 = @echo " !CCLD " $@; true skip
|
||||
am__v_CCLD_1 = true skip
|
||||
endif
|
||||
AM_V_AR = $(am__v_AR_$(V))
|
||||
am__v_AR_ = $(am__v_AR_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_AR_0 = @echo " AR " $@;
|
||||
@@ -203,10 +216,21 @@ LDFLAGS += -lrados
|
||||
endif
|
||||
|
||||
AM_LINK = $(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
# detect what platform we're building on
|
||||
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; export PORTABLE="$(PORTABLE)"; "$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
|
||||
# Detect what platform we're building on.
|
||||
# Export some common variables that might have been passed as Make variables
|
||||
# instead of environment variables.
|
||||
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
|
||||
export COMPILE_WITH_ASAN="$(COMPILE_WITH_ASAN)"; \
|
||||
export COMPILE_WITH_TSAN="$(COMPILE_WITH_TSAN)"; \
|
||||
export COMPILE_WITH_UBSAN="$(COMPILE_WITH_UBSAN)"; \
|
||||
export PORTABLE="$(PORTABLE)"; \
|
||||
export ROCKSDB_NO_FBCODE="$(ROCKSDB_NO_FBCODE)"; \
|
||||
export USE_CLANG="$(USE_CLANG)"; \
|
||||
"$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
# this file is generated by the previous line to set build flags and sources
|
||||
include make_config.mk
|
||||
export JAVAC_ARGS
|
||||
CLEAN_FILES += make_config.mk
|
||||
|
||||
missing_make_config_paths := $(shell \
|
||||
@@ -216,7 +240,7 @@ missing_make_config_paths := $(shell \
|
||||
done | sort | uniq)
|
||||
|
||||
$(foreach path, $(missing_make_config_paths), \
|
||||
$(warning Warning: $(path) dont exist))
|
||||
$(warning Warning: $(path) does not exist))
|
||||
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
# no debug info
|
||||
@@ -310,7 +334,7 @@ endif
|
||||
|
||||
export GTEST_THROW_ON_FAILURE=1
|
||||
export GTEST_HAS_EXCEPTIONS=1
|
||||
GTEST_DIR = ./third-party/gtest-1.8.1/fused-src
|
||||
GTEST_DIR = third-party/gtest-1.8.1/fused-src
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PLATFORM_CCFLAGS += -I$(GTEST_DIR)
|
||||
@@ -439,12 +463,18 @@ BENCHTOOLOBJECTS = $(BENCH_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
ANALYZETOOLOBJECTS = $(ANALYZER_LIB_SOURCES:.cc=.o)
|
||||
|
||||
ifeq ($(DEBUG_LEVEL),0)
|
||||
STRESSTOOLOBJECTS = $(STRESS_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
else
|
||||
STRESSTOOLOBJECTS = $(STRESS_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL) \
|
||||
$(TESTHARNESS)
|
||||
endif
|
||||
|
||||
EXPOBJECTS = $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
TESTS = \
|
||||
db_basic_test \
|
||||
db_with_timestamp_basic_test \
|
||||
db_encryption_test \
|
||||
db_test2 \
|
||||
external_sst_file_basic_test \
|
||||
@@ -459,15 +489,18 @@ TESTS = \
|
||||
env_basic_test \
|
||||
env_test \
|
||||
env_logger_test \
|
||||
io_posix_test \
|
||||
hash_test \
|
||||
random_test \
|
||||
thread_local_test \
|
||||
work_queue_test \
|
||||
rate_limiter_test \
|
||||
perf_context_test \
|
||||
iostats_context_test \
|
||||
db_wal_test \
|
||||
db_block_cache_test \
|
||||
db_test \
|
||||
db_logical_block_size_cache_test \
|
||||
db_blob_index_test \
|
||||
db_iter_test \
|
||||
db_iter_stress_test \
|
||||
@@ -492,13 +525,14 @@ TESTS = \
|
||||
db_table_properties_test \
|
||||
db_statistics_test \
|
||||
db_write_test \
|
||||
error_handler_test \
|
||||
error_handler_fs_test \
|
||||
autovector_test \
|
||||
blob_db_test \
|
||||
cleanable_test \
|
||||
column_family_test \
|
||||
table_properties_collector_test \
|
||||
arena_test \
|
||||
memkind_kmem_allocator_test \
|
||||
block_test \
|
||||
data_block_hash_index_test \
|
||||
cache_test \
|
||||
@@ -509,8 +543,10 @@ TESTS = \
|
||||
fault_injection_test \
|
||||
filelock_test \
|
||||
filename_test \
|
||||
random_access_file_reader_test \
|
||||
file_reader_writer_test \
|
||||
block_based_filter_block_test \
|
||||
block_based_table_reader_test \
|
||||
full_filter_block_test \
|
||||
partitioned_filter_block_test \
|
||||
hash_table_test \
|
||||
@@ -553,6 +589,7 @@ TESTS = \
|
||||
deletefile_test \
|
||||
obsolete_files_test \
|
||||
table_test \
|
||||
block_fetcher_test \
|
||||
delete_scheduler_test \
|
||||
options_test \
|
||||
options_settable_test \
|
||||
@@ -577,6 +614,7 @@ TESTS = \
|
||||
compaction_job_stats_test \
|
||||
option_change_migration_test \
|
||||
transaction_test \
|
||||
transaction_lock_mgr_test \
|
||||
ldb_cmd_test \
|
||||
persistent_cache_test \
|
||||
statistics_test \
|
||||
@@ -597,10 +635,12 @@ TESTS = \
|
||||
block_cache_tracer_test \
|
||||
block_cache_trace_analyzer_test \
|
||||
defer_test \
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
TESTS += folly_synchronization_distributed_mutex_test
|
||||
endif
|
||||
blob_file_addition_test \
|
||||
blob_file_garbage_test \
|
||||
timer_test \
|
||||
db_with_timestamp_compaction_test \
|
||||
testutil_test \
|
||||
io_tracer_test \
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
@@ -621,13 +661,71 @@ PARALLEL_TEST = \
|
||||
persistent_cache_test \
|
||||
table_test \
|
||||
transaction_test \
|
||||
transaction_lock_mgr_test \
|
||||
write_prepared_transaction_test \
|
||||
write_unprepared_transaction_test \
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
TESTS += folly_synchronization_distributed_mutex_test
|
||||
PARALLEL_TEST += folly_synchronization_distributed_mutex_test
|
||||
endif
|
||||
|
||||
# options_settable_test doesn't pass with UBSAN as we use hack in the test
|
||||
ifdef COMPILE_WITH_UBSAN
|
||||
TESTS := $(shell echo $(TESTS) | sed 's/\boptions_settable_test\b//g')
|
||||
endif
|
||||
ifdef ASSERT_STATUS_CHECKED
|
||||
# This is a new check for which we will add support incrementally. This
|
||||
# list can be removed once support is fully added.
|
||||
TESTS_PASSING_ASC = \
|
||||
arena_test \
|
||||
autovector_test \
|
||||
blob_file_addition_test \
|
||||
blob_file_garbage_test \
|
||||
bloom_test \
|
||||
cassandra_format_test \
|
||||
cassandra_row_merge_test \
|
||||
cassandra_serialize_test \
|
||||
cleanable_test \
|
||||
coding_test \
|
||||
crc32c_test \
|
||||
dbformat_test \
|
||||
defer_test \
|
||||
dynamic_bloom_test \
|
||||
event_logger_test \
|
||||
file_indexer_test \
|
||||
folly_synchronization_distributed_mutex_test \
|
||||
hash_table_test \
|
||||
hash_test \
|
||||
heap_test \
|
||||
histogram_test \
|
||||
inlineskiplist_test \
|
||||
io_posix_test \
|
||||
iostats_context_test \
|
||||
memkind_kmem_allocator_test \
|
||||
merger_test \
|
||||
mock_env_test \
|
||||
object_registry_test \
|
||||
options_settable_test \
|
||||
options_test \
|
||||
random_test \
|
||||
range_del_aggregator_test \
|
||||
range_tombstone_fragmenter_test \
|
||||
repeatable_thread_test \
|
||||
skiplist_test \
|
||||
slice_test \
|
||||
statistics_test \
|
||||
thread_local_test \
|
||||
timer_queue_test \
|
||||
timer_test \
|
||||
util_merge_operators_test \
|
||||
version_edit_test \
|
||||
work_queue_test \
|
||||
write_controller_test \
|
||||
|
||||
TESTS := $(filter $(TESTS_PASSING_ASC),$(TESTS))
|
||||
PARALLEL_TEST := $(filter $(TESTS_PASSING_ASC),$(PARALLEL_TEST))
|
||||
endif
|
||||
SUBSET := $(TESTS)
|
||||
ifdef ROCKSDBTESTS_START
|
||||
SUBSET := $(shell echo $(SUBSET) | sed 's/^.*$(ROCKSDBTESTS_START)/$(ROCKSDBTESTS_START)/')
|
||||
@@ -747,7 +845,8 @@ endif # PLATFORM_SHARED_EXT
|
||||
dbg rocksdbjavastatic rocksdbjava install install-static install-shared uninstall \
|
||||
analyze tools tools_lib \
|
||||
blackbox_crash_test_with_atomic_flush whitebox_crash_test_with_atomic_flush \
|
||||
blackbox_crash_test_with_txn whitebox_crash_test_with_txn
|
||||
blackbox_crash_test_with_txn whitebox_crash_test_with_txn \
|
||||
blackbox_crash_test_with_best_efforts_recovery
|
||||
|
||||
|
||||
all: $(LIBRARY) $(BENCHMARKS) tools tools_lib test_libs $(TESTS)
|
||||
@@ -879,6 +978,12 @@ J ?= 100%
|
||||
# Use this regexp to select the subset of tests whose names match.
|
||||
tests-regexp = .
|
||||
|
||||
ifeq ($(PRINT_PARALLEL_OUTPUTS), 1)
|
||||
parallel_com = '{}'
|
||||
else
|
||||
parallel_com = '{} >& t/log-{/}'
|
||||
endif
|
||||
|
||||
.PHONY: check_0
|
||||
check_0:
|
||||
$(AM_V_GEN)export TEST_TMPDIR=$(TMPD); \
|
||||
@@ -892,9 +997,12 @@ check_0:
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG $$eta --gnu '{} >& t/log-{/}'
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG $$eta --gnu $(parallel_com) ; \
|
||||
parallel_retcode=$$? ; \
|
||||
awk '{ if ($$7 != 0 || $$8 != 0) { if ($$7 == "Exitval") { h = $$0; } else { if (!f) print h; print; f = 1 } } } END { if(f) exit 1; }' < LOG ; \
|
||||
if [ $$parallel_retcode -ne 0 ] ; then exit 1 ; fi
|
||||
|
||||
valgrind-blacklist-regexp = InlineSkipTest.ConcurrentInsert|TransactionStressTest.DeadlockStress|DBCompactionTest.SuggestCompactRangeNoTwoLevel0Compactions|BackupableDBTest.RateLimiting|DBTest.CloseSpeedup|DBTest.ThreadStatusFlush|DBTest.RateLimitingTest|DBTest.EncodeDecompressedBlockSizeTest|FaultInjectionTest.UninstalledCompaction|HarnessTest.Randomized|ExternalSSTFileTest.CompactDuringAddFileRandom|ExternalSSTFileTest.IngestFileWithGlobalSeqnoRandomized|MySQLStyleTransactionTest.TransactionStressTest
|
||||
valgrind-exclude-regexp = InlineSkipTest.ConcurrentInsert|TransactionStressTest.DeadlockStress|DBCompactionTest.SuggestCompactRangeNoTwoLevel0Compactions|BackupableDBTest.RateLimiting|DBTest.CloseSpeedup|DBTest.ThreadStatusFlush|DBTest.RateLimitingTest|DBTest.EncodeDecompressedBlockSizeTest|FaultInjectionTest.UninstalledCompaction|HarnessTest.Randomized|ExternalSSTFileTest.CompactDuringAddFileRandom|ExternalSSTFileTest.IngestFileWithGlobalSeqnoRandomized|MySQLStyleTransactionTest.TransactionStressTest
|
||||
|
||||
.PHONY: valgrind_check_0
|
||||
valgrind_check_0:
|
||||
@@ -909,7 +1017,7 @@ valgrind_check_0:
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| grep -E -v '$(valgrind-blacklist-regexp)' \
|
||||
| grep -E -v '$(valgrind-exclude-regexp)' \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG $$eta --gnu \
|
||||
'(if [[ "{}" == "./"* ]] ; then $(DRIVER) {}; else {}; fi) ' \
|
||||
'>& t/valgrind_log-{/}'
|
||||
@@ -926,6 +1034,9 @@ CLEAN_FILES += t LOG $(TMPD)
|
||||
watch-log:
|
||||
$(WATCH) --interval=0 'sort -k7,7nr -k4,4gr LOG|$(quoted_perl_command)'
|
||||
|
||||
dump-log:
|
||||
bash -c '$(quoted_perl_command)' < LOG
|
||||
|
||||
# If J != 1 and GNU parallel is installed, run the tests in parallel,
|
||||
# via the check_0 rule above. Otherwise, run them sequentially.
|
||||
check: all
|
||||
@@ -937,24 +1048,30 @@ check: all
|
||||
$(MAKE) T="$$t" TMPD=$(TMPD) check_0; \
|
||||
else \
|
||||
for t in $(TESTS); do \
|
||||
echo "===== Running $$t"; ./$$t || exit 1; done; \
|
||||
echo "===== Running $$t (`date`)"; ./$$t || exit 1; done; \
|
||||
fi
|
||||
rm -rf $(TMPD)
|
||||
ifneq ($(PLATFORM), OS_AIX)
|
||||
python tools/check_all_python.py
|
||||
$(PYTHON) tools/check_all_python.py
|
||||
ifeq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
python tools/ldb_test.py
|
||||
ifndef ASSERT_STATUS_CHECKED # not yet working with these tests
|
||||
$(PYTHON) tools/ldb_test.py
|
||||
sh tools/rocksdb_dump_test.sh
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
ifndef SKIP_FORMAT_BUCK_CHECKS
|
||||
$(MAKE) check-format
|
||||
$(MAKE) check-buck-targets
|
||||
endif
|
||||
|
||||
# TODO add ldb_tests
|
||||
check_some: $(SUBSET)
|
||||
for t in $(SUBSET); do echo "===== Running $$t"; ./$$t || exit 1; done
|
||||
for t in $(SUBSET); do echo "===== Running $$t (`date`)"; ./$$t || exit 1; done
|
||||
|
||||
.PHONY: ldb_tests
|
||||
ldb_tests: ldb
|
||||
python tools/ldb_test.py
|
||||
$(PYTHON) tools/ldb_test.py
|
||||
|
||||
crash_test: whitebox_crash_test blackbox_crash_test
|
||||
|
||||
@@ -962,32 +1079,37 @@ crash_test_with_atomic_flush: whitebox_crash_test_with_atomic_flush blackbox_cra
|
||||
|
||||
crash_test_with_txn: whitebox_crash_test_with_txn blackbox_crash_test_with_txn
|
||||
|
||||
crash_test_with_best_efforts_recovery: blackbox_crash_test_with_best_efforts_recovery
|
||||
|
||||
blackbox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(PYTHON) -u tools/db_crashtest.py --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(PYTHON) -u tools/db_crashtest.py blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_atomic_flush: db_stress
|
||||
python -u tools/db_crashtest.py --cf_consistency blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(PYTHON) -u tools/db_crashtest.py --cf_consistency blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_txn: db_stress
|
||||
python -u tools/db_crashtest.py --txn blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(PYTHON) -u tools/db_crashtest.py --txn blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_best_efforts_recovery: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --test_best_efforts_recovery blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
endif
|
||||
|
||||
whitebox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple whitebox --random_kill_odd \
|
||||
$(PYTHON) -u tools/db_crashtest.py --simple whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py whitebox --random_kill_odd \
|
||||
$(PYTHON) -u tools/db_crashtest.py whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_atomic_flush: db_stress
|
||||
python -u tools/db_crashtest.py --cf_consistency whitebox --random_kill_odd \
|
||||
$(PYTHON) -u tools/db_crashtest.py --cf_consistency whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_txn: db_stress
|
||||
python -u tools/db_crashtest.py --txn whitebox --random_kill_odd \
|
||||
$(PYTHON) -u tools/db_crashtest.py --txn whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
asan_check:
|
||||
@@ -1010,6 +1132,11 @@ asan_crash_test_with_txn:
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_txn
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_best_efforts_recovery:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_best_efforts_recovery
|
||||
$(MAKE) clean
|
||||
|
||||
ubsan_check:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) check -j32
|
||||
@@ -1030,6 +1157,11 @@ ubsan_crash_test_with_txn:
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) crash_test_with_txn
|
||||
$(MAKE) clean
|
||||
|
||||
ubsan_crash_test_with_best_efforts_recovery:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) crash_test_with_best_efforts_recovery
|
||||
$(MAKE) clean
|
||||
|
||||
valgrind_test:
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
|
||||
|
||||
@@ -1056,7 +1188,7 @@ ifneq ($(PAR_TEST),)
|
||||
parloop:
|
||||
ret_bad=0; \
|
||||
for t in $(PAR_TEST); do \
|
||||
echo "===== Running $$t in parallel $(NUM_PAR)";\
|
||||
echo "===== Running $$t in parallel $(NUM_PAR) (`date`)";\
|
||||
if [ $(db_test) -eq 1 ]; then \
|
||||
seq $(J) | v="$$t" build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{}; export TEST_TMPDIR=$$s;' \
|
||||
'timeout 2m ./db_test --gtest_filter=$$v >> $$s/log-{} 2>1'; \
|
||||
@@ -1167,6 +1299,12 @@ tags0:
|
||||
format:
|
||||
build_tools/format-diff.sh
|
||||
|
||||
check-format:
|
||||
build_tools/format-diff.sh -c
|
||||
|
||||
check-buck-targets:
|
||||
buckifier/check_buck_targets.sh
|
||||
|
||||
package:
|
||||
bash build_tools/make_package.sh $(SHARED_MAJOR).$(SHARED_MINOR)
|
||||
|
||||
@@ -1230,6 +1368,9 @@ db_repl_stress: tools/db_repl_stress.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
arena_test: memory/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
memkind_kmem_allocator_test: memory/memkind_kmem_allocator_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
autovector_test: util/autovector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1287,6 +1428,9 @@ histogram_test: monitoring/histogram_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
thread_local_test: util/thread_local_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
work_queue_test: util/work_queue_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
corruption_test: db/corruption_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1302,6 +1446,12 @@ slice_transform_test: util/slice_transform_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
db_basic_test: db/db_basic_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_with_timestamp_basic_test: db/db_with_timestamp_basic_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_with_timestamp_compaction_test: db/db_with_timestamp_compaction_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_encryption_test: db/db_encryption_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1311,7 +1461,10 @@ db_test: db/db_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
db_test2: db/db_test2.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_blob_index_test: db/db_blob_index_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
db_logical_block_size_cache_test: db/db_logical_block_size_cache_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_blob_index_test: db/blob/db_blob_index_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_block_cache_test: db/db_block_cache_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1365,7 +1518,7 @@ db_statistics_test: db/db_statistics_test.o db/db_test_util.o $(LIBOBJECTS) $(TE
|
||||
db_write_test: db/db_write_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
error_handler_test: db/error_handler_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
error_handler_fs_test: db/error_handler_fs_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
external_sst_file_basic_test: db/external_sst_file_basic_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1478,6 +1631,9 @@ env_basic_test: env/env_basic_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
env_test: env/env_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
io_posix_test: env/io_posix_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
fault_injection_test: db/fault_injection_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1490,12 +1646,18 @@ delete_scheduler_test: file/delete_scheduler_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
random_access_file_reader_test: file/random_access_file_reader_test.o $(LIBOBJECTS) $(TESTHARNESS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
file_reader_writer_test: util/file_reader_writer_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_based_filter_block_test: table/block_based/block_based_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_based_table_reader_test: table/block_based/block_based_table_reader_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
full_filter_block_test: table/block_based/full_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1511,6 +1673,9 @@ cleanable_test: table/cleanable_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_fetcher_test: table/block_fetcher_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_test: table/block_based/block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1649,6 +1814,9 @@ write_callback_test: db/write_callback_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
heap_test: util/heap_test.o $(GTEST)
|
||||
$(AM_LINK)
|
||||
|
||||
transaction_lock_mgr_test: utilities/transactions/transaction_lock_mgr_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
transaction_test: utilities/transactions/transaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1718,6 +1886,20 @@ block_cache_trace_analyzer_test: tools/block_cache_analyzer/block_cache_trace_an
|
||||
defer_test: util/defer_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_file_addition_test: db/blob/blob_file_addition_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_file_garbage_test: db/blob/blob_file_garbage_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
timer_test: util/timer_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
testutil_test: test_util/testutil_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
io_tracer_test: trace_replay/io_tracer_test.o trace_replay/io_tracer.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
INSTALL_PATH ?= /usr/local
|
||||
@@ -1797,11 +1979,11 @@ SHA256_CMD = sha256sum
|
||||
ZLIB_VER ?= 1.2.11
|
||||
ZLIB_SHA256 ?= c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.6
|
||||
BZIP2_SHA256 ?= a2848f34fcd5d6cf47def00461fcb528a0484d8edef8208d6d2e2909dc61d9cd
|
||||
BZIP2_DOWNLOAD_BASE ?= https://downloads.sourceforge.net/project/bzip2
|
||||
SNAPPY_VER ?= 1.1.7
|
||||
SNAPPY_SHA256 ?= 3dfa02e873ff51a11ee02b9ca391807f0c8ea0529a4924afa645fbf97163f9d4
|
||||
BZIP2_VER ?= 1.0.8
|
||||
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
|
||||
BZIP2_DOWNLOAD_BASE ?= https://sourceware.org/pub/bzip2
|
||||
SNAPPY_VER ?= 1.1.8
|
||||
SNAPPY_SHA256 ?= 16b677f07832a612b0836178db7f374e414f94657c138e6993cbfc5dcc58651f
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
LZ4_VER ?= 1.9.2
|
||||
LZ4_SHA256 ?= 658ba6191fa44c92280d4aa2c271b0f4fbc0e34d249578dd05e50e76d0e5efcc
|
||||
@@ -2068,7 +2250,7 @@ jtest_run:
|
||||
|
||||
jtest: rocksdbjava
|
||||
cd java;$(MAKE) sample;$(MAKE) test;
|
||||
python tools/check_all_python.py # TODO peterd: find a better place for this check in CI targets
|
||||
$(PYTHON) tools/check_all_python.py # TODO peterd: find a better place for this check in CI targets
|
||||
|
||||
jdb_bench:
|
||||
cd java;$(MAKE) db_bench;
|
||||
@@ -2126,6 +2308,7 @@ endif
|
||||
# Source files dependencies detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# FIXME: nothing checks that entries in MAIN_SOURCES actually exist
|
||||
all_sources = $(LIB_SOURCES) $(MAIN_SOURCES) $(MOCK_LIB_SOURCES) $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(TEST_LIB_SOURCES) $(ANALYZER_LIB_SOURCES) $(STRESS_LIB_SOURCES)
|
||||
DEPFILES = $(all_sources:.cc=.cc.d)
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
## RocksDB: A Persistent Key-Value Store for Flash and RAM Storage
|
||||
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
[](https://circleci.com/gh/facebook/rocksdb)
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
[](http://140.211.168.68:8080/job/Rocksdb)
|
||||
|
||||
RocksDB is developed and maintained by Facebook Database Engineering Team.
|
||||
@@ -24,7 +25,7 @@ The public interface is in `include/`. Callers should not include or
|
||||
rely on the details of any other header files in this package. Those
|
||||
internal APIs may be changed without warning.
|
||||
|
||||
Design discussions are conducted in https://www.facebook.com/groups/rocksdb.dev/
|
||||
Design discussions are conducted in https://www.facebook.com/groups/rocksdb.dev/ and https://rocksdb.slack.com/
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# This file @generated by `python buckifier/buckify_rocksdb.py`
|
||||
# This file @generated by `python3 buckifier/buckify_rocksdb.py`
|
||||
# --> DO NOT EDIT MANUALLY <--
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
@@ -26,7 +26,6 @@ ROCKSDB_EXTERNAL_DEPS = [
|
||||
("lz4", None, "lz4"),
|
||||
("zstd", None),
|
||||
("tbb", None),
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
ROCKSDB_OS_DEPS = [
|
||||
@@ -46,7 +45,9 @@ ROCKSDB_OS_PREPROCESSOR_FLAGS = [
|
||||
"-DROCKSDB_PTHREAD_ADAPTIVE_MUTEX",
|
||||
"-DROCKSDB_RANGESYNC_PRESENT",
|
||||
"-DROCKSDB_SCHED_GETCPU_PRESENT",
|
||||
"-DROCKSDB_IOURING_PRESENT",
|
||||
"-DHAVE_SSE42",
|
||||
"-DLIBURING",
|
||||
"-DNUMA",
|
||||
],
|
||||
),
|
||||
@@ -107,13 +108,25 @@ ROCKSDB_OS_DEPS += ([(
|
||||
["third-party//jemalloc:headers"],
|
||||
)] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_LIB_DEPS = [
|
||||
":rocksdb_lib",
|
||||
":rocksdb_test_lib",
|
||||
] if not is_opt_mode else [":rocksdb_lib"]
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_lib",
|
||||
srcs = [
|
||||
"cache/cache.cc",
|
||||
"cache/clock_cache.cc",
|
||||
"cache/lru_cache.cc",
|
||||
"cache/sharded_cache.cc",
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
"db/blob/blob_file_addition.cc",
|
||||
"db/blob/blob_file_garbage.cc",
|
||||
"db/blob/blob_file_meta.cc",
|
||||
"db/blob/blob_log_format.cc",
|
||||
"db/blob/blob_log_reader.cc",
|
||||
"db/blob/blob_log_writer.cc",
|
||||
"db/builder.cc",
|
||||
"db/c.cc",
|
||||
"db/column_family.cc",
|
||||
@@ -167,6 +180,7 @@ cpp_library(
|
||||
"db/trim_history_scheduler.cc",
|
||||
"db/version_builder.cc",
|
||||
"db/version_edit.cc",
|
||||
"db/version_edit_handler.cc",
|
||||
"db/version_set.cc",
|
||||
"db/wal_manager.cc",
|
||||
"db/write_batch.cc",
|
||||
@@ -198,6 +212,7 @@ cpp_library(
|
||||
"memory/arena.cc",
|
||||
"memory/concurrent_arena.cc",
|
||||
"memory/jemalloc_nodump_allocator.cc",
|
||||
"memory/memkind_kmem_allocator.cc",
|
||||
"memtable/alloc_tracker.cc",
|
||||
"memtable/hash_linklist_rep.cc",
|
||||
"memtable/hash_skiplist_rep.cc",
|
||||
@@ -223,16 +238,18 @@ cpp_library(
|
||||
"options/options.cc",
|
||||
"options/options_helper.cc",
|
||||
"options/options_parser.cc",
|
||||
"options/options_sanity_check.cc",
|
||||
"port/port_posix.cc",
|
||||
"port/stack_trace.cc",
|
||||
"table/adaptive/adaptive_table_factory.cc",
|
||||
"table/block_based/binary_search_index_reader.cc",
|
||||
"table/block_based/block.cc",
|
||||
"table/block_based/block_based_filter_block.cc",
|
||||
"table/block_based/block_based_table_builder.cc",
|
||||
"table/block_based/block_based_table_factory.cc",
|
||||
"table/block_based/block_based_table_iterator.cc",
|
||||
"table/block_based/block_based_table_reader.cc",
|
||||
"table/block_based/block_builder.cc",
|
||||
"table/block_based/block_prefetcher.cc",
|
||||
"table/block_based/block_prefix_index.cc",
|
||||
"table/block_based/data_block_footer.cc",
|
||||
"table/block_based/data_block_hash_index.cc",
|
||||
@@ -240,9 +257,14 @@ cpp_library(
|
||||
"table/block_based/filter_policy.cc",
|
||||
"table/block_based/flush_block_policy.cc",
|
||||
"table/block_based/full_filter_block.cc",
|
||||
"table/block_based/hash_index_reader.cc",
|
||||
"table/block_based/index_builder.cc",
|
||||
"table/block_based/index_reader_common.cc",
|
||||
"table/block_based/parsed_full_filter_block.cc",
|
||||
"table/block_based/partitioned_filter_block.cc",
|
||||
"table/block_based/partitioned_index_iterator.cc",
|
||||
"table/block_based/partitioned_index_reader.cc",
|
||||
"table/block_based/reader_common.cc",
|
||||
"table/block_based/uncompression_dict_reader.cc",
|
||||
"table/block_fetcher.cc",
|
||||
"table/cuckoo/cuckoo_table_builder.cc",
|
||||
@@ -272,6 +294,7 @@ cpp_library(
|
||||
"tools/ldb_tool.cc",
|
||||
"tools/sst_dump_tool.cc",
|
||||
"trace_replay/block_cache_tracer.cc",
|
||||
"trace_replay/io_tracer.cc",
|
||||
"trace_replay/trace_replay.cc",
|
||||
"util/build_version.cc",
|
||||
"util/coding.cc",
|
||||
@@ -299,9 +322,6 @@ cpp_library(
|
||||
"utilities/blob_db/blob_db_impl_filesnapshot.cc",
|
||||
"utilities/blob_db/blob_dump_tool.cc",
|
||||
"utilities/blob_db/blob_file.cc",
|
||||
"utilities/blob_db/blob_log_format.cc",
|
||||
"utilities/blob_db/blob_log_reader.cc",
|
||||
"utilities/blob_db/blob_log_writer.cc",
|
||||
"utilities/cassandra/cassandra_compaction_filter.cc",
|
||||
"utilities/cassandra/format.cc",
|
||||
"utilities/cassandra/merge_operator.cc",
|
||||
@@ -365,6 +385,7 @@ cpp_library(
|
||||
"db/db_test_util.cc",
|
||||
"table/mock_table.cc",
|
||||
"test_util/fault_injection_test_env.cc",
|
||||
"test_util/fault_injection_test_fs.cc",
|
||||
"test_util/testharness.cc",
|
||||
"test_util/testutil.cc",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
|
||||
@@ -378,7 +399,9 @@ cpp_library(
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_lib"],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
("googletest", None, "gtest"),
|
||||
],
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
@@ -421,7 +444,7 @@ cpp_library(
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_lib"],
|
||||
deps = ROCKSDB_LIB_DEPS,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
@@ -475,6 +498,20 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"blob_file_addition_test",
|
||||
"db/blob/blob_file_addition_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"blob_file_garbage_test",
|
||||
"db/blob/blob_file_garbage_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"block_based_filter_block_test",
|
||||
"table/block_based/block_based_filter_block_test.cc",
|
||||
@@ -482,6 +519,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"block_based_table_reader_test",
|
||||
"table/block_based/block_based_table_reader_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"block_cache_trace_analyzer_test",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer_test.cc",
|
||||
@@ -496,6 +540,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"block_fetcher_test",
|
||||
"table/block_fetcher_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"block_test",
|
||||
"table/block_based/block_test.cc",
|
||||
@@ -687,7 +738,7 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"db_blob_index_test",
|
||||
"db/db_blob_index_test.cc",
|
||||
"db/blob/db_blob_index_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
@@ -783,6 +834,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_logical_block_size_cache_test",
|
||||
"db/db_logical_block_size_cache_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_memtable_test",
|
||||
"db/db_memtable_test.cc",
|
||||
@@ -888,6 +946,20 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_with_timestamp_basic_test",
|
||||
"db/db_with_timestamp_basic_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_with_timestamp_compaction_test",
|
||||
"db/db_with_timestamp_compaction_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_write_test",
|
||||
"db/db_write_test.cc",
|
||||
@@ -959,8 +1031,8 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
],
|
||||
[
|
||||
"error_handler_test",
|
||||
"db/error_handler_test.cc",
|
||||
"error_handler_fs_test",
|
||||
"db/error_handler_fs_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
@@ -1077,6 +1149,20 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"io_posix_test",
|
||||
"env/io_posix_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"io_tracer_test",
|
||||
"trace_replay/io_tracer_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"iostats_context_test",
|
||||
"monitoring/iostats_context_test.cc",
|
||||
@@ -1119,6 +1205,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"memkind_kmem_allocator_test",
|
||||
"memory/memkind_kmem_allocator_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"memory_test",
|
||||
"utilities/memory/memory_test.cc",
|
||||
@@ -1252,6 +1345,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"random_access_file_reader_test",
|
||||
"file/random_access_file_reader_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"random_test",
|
||||
"util/random_test.cc",
|
||||
@@ -1378,6 +1478,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"testutil_test",
|
||||
"test_util/testutil_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"thread_list_test",
|
||||
"util/thread_list_test.cc",
|
||||
@@ -1399,6 +1506,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"timer_test",
|
||||
"util/timer_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"trace_analyzer_test",
|
||||
"tools/trace_analyzer_test.cc",
|
||||
@@ -1406,6 +1520,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"transaction_lock_mgr_test",
|
||||
"utilities/transactions/transaction_lock_mgr_test.cc",
|
||||
"parallel",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"transaction_test",
|
||||
"utilities/transactions/transaction_test.cc",
|
||||
@@ -1455,6 +1576,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"work_queue_test",
|
||||
"util/work_queue_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"write_batch_test",
|
||||
"db/write_batch_test.cc",
|
||||
@@ -1510,18 +1638,17 @@ ROCKS_TESTS = [
|
||||
# Do not build the tests in opt mode, since SyncPoint and other test code
|
||||
# will not be included.
|
||||
[
|
||||
test_binary(
|
||||
extra_compiler_flags = extra_compiler_flags,
|
||||
extra_deps = extra_deps,
|
||||
parallelism = parallelism,
|
||||
rocksdb_arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
rocksdb_compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
rocksdb_external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
rocksdb_os_deps = ROCKSDB_OS_DEPS,
|
||||
rocksdb_os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
rocksdb_preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
test_cc = test_cc,
|
||||
test_name = test_name,
|
||||
cpp_unittest(
|
||||
name = test_name,
|
||||
srcs = [test_cc],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"] + extra_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
("googletest", None, "gtest"),
|
||||
],
|
||||
)
|
||||
for test_name, test_cc, parallelism, extra_deps, extra_compiler_flags in ROCKS_TESTS
|
||||
if not is_opt_mode
|
||||
|
||||
@@ -106,3 +106,6 @@ LzLabs is using RocksDB as a storage engine in their multi-database distributed
|
||||
## Nebula Graph
|
||||
|
||||
[Nebula Graph](https://github.com/vesoft-inc/nebula) is a distributed, scalable, lightning-fast, open source graph database capable of hosting super large scale graphs with dozens of billions of vertices (nodes) and trillions of edges, with milliseconds of latency.
|
||||
|
||||
## YugabyteDB
|
||||
[YugabyteDB](https://www.yugabyte.com/) is an open source, high performance, distributed SQL database that uses RocksDB as its storage layer. For more information, please see https://github.com/yugabyte/yugabyte-db/.
|
||||
|
||||
+15
-4
@@ -1,6 +1,6 @@
|
||||
version: 1.0.{build}
|
||||
|
||||
image: Visual Studio 2017
|
||||
image: Visual Studio 2019
|
||||
|
||||
environment:
|
||||
JAVA_HOME: C:\Program Files\Java\jdk1.8.0
|
||||
@@ -24,6 +24,15 @@ environment:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
CMAKE_GENERATOR: Visual Studio 15 Win64
|
||||
DEV_ENV: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.com
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
CMAKE_GENERATOR: Visual Studio 16
|
||||
CMAKE_PLATEFORM_NAME: x64
|
||||
DEV_ENV: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\devenv.com
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
CMAKE_GENERATOR: Visual Studio 16
|
||||
CMAKE_PLATEFORM_NAME: x64
|
||||
CMAKE_OPT: -DCMAKE_CXX_STANDARD=20
|
||||
DEV_ENV: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\devenv.com
|
||||
|
||||
install:
|
||||
- md %THIRDPARTY_HOME%
|
||||
@@ -34,7 +43,8 @@ install:
|
||||
- cd snappy-1.1.7
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake -G "%CMAKE_GENERATOR%" ..
|
||||
- if DEFINED CMAKE_PLATEFORM_NAME (set "PLATEFORM_OPT=-A %CMAKE_PLATEFORM_NAME%")
|
||||
- cmake .. -G "%CMAKE_GENERATOR%" %PLATEFORM_OPT%
|
||||
- msbuild Snappy.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild Snappy.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building LZ4 dependency..."
|
||||
@@ -57,7 +67,8 @@ install:
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "%CMAKE_GENERATOR%" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DLZ4=1 -DZSTD=1 -DXPRESS=1 -DJNI=1 ..
|
||||
- if DEFINED CMAKE_PLATEFORM_NAME (set "PLATEFORM_OPT=-A %CMAKE_PLATEFORM_NAME%")
|
||||
- cmake .. -G "%CMAKE_GENERATOR%" %PLATEFORM_OPT% %CMAKE_OPT% -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DLZ4=1 -DZSTD=1 -DXPRESS=1 -DJNI=1 -DWITH_ALL_TESTS=0
|
||||
- cd ..
|
||||
|
||||
build:
|
||||
@@ -68,7 +79,7 @@ build:
|
||||
test:
|
||||
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test,db_merge_operand_test -Concurrency 8
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,env_basic_test -Concurrency 8
|
||||
|
||||
on_failure:
|
||||
- cmd: 7z a build-failed.zip %APPVEYOR_BUILD_FOLDER%\build\ && appveyor PushArtifact build-failed.zip
|
||||
|
||||
@@ -20,10 +20,10 @@ from util import ColorString
|
||||
# User can pass extra dependencies as a JSON object via command line, and this
|
||||
# script can include these dependencies in the generate TARGETS file.
|
||||
# Usage:
|
||||
# $python buckifier/buckify_rocksdb.py
|
||||
# $python3 buckifier/buckify_rocksdb.py
|
||||
# (This generates a TARGET file without user-specified dependency for unit
|
||||
# tests.)
|
||||
# $python buckifier/buckify_rocksdb.py \
|
||||
# $python3 buckifier/buckify_rocksdb.py \
|
||||
# '{"fake": { \
|
||||
# "extra_deps": [":test_dep", "//fakes/module:mock1"], \
|
||||
# "extra_compiler_flags": ["-DROCKSDB_LITE", "-Os"], \
|
||||
@@ -108,7 +108,6 @@ def get_tests(repo_path):
|
||||
|
||||
return tests
|
||||
|
||||
|
||||
# Parse extra dependencies passed by user from command line
|
||||
def get_dependencies():
|
||||
deps_map = {
|
||||
@@ -147,6 +146,7 @@ def generate_targets(repo_path, deps_map):
|
||||
return False
|
||||
|
||||
TARGETS = TARGETSBuilder("%s/TARGETS" % repo_path)
|
||||
|
||||
# rocksdb_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_lib",
|
||||
@@ -159,7 +159,10 @@ def generate_targets(repo_path, deps_map):
|
||||
src_mk.get("TEST_LIB_SOURCES", []) +
|
||||
src_mk.get("EXP_LIB_SOURCES", []) +
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", []),
|
||||
[":rocksdb_lib"])
|
||||
[":rocksdb_lib"],
|
||||
extra_external_deps=""" + [
|
||||
("googletest", None, "gtest"),
|
||||
]""")
|
||||
# rocksdb_tools_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_tools_lib",
|
||||
@@ -168,14 +171,13 @@ def generate_targets(repo_path, deps_map):
|
||||
["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"])
|
||||
# rocksdb_stress_lib
|
||||
TARGETS.add_library(
|
||||
TARGETS.add_rocksdb_library(
|
||||
"rocksdb_stress_lib",
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
+ src_mk.get('STRESS_LIB_SOURCES', [])
|
||||
+ ["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"])
|
||||
+ ["test_util/testutil.cc"])
|
||||
|
||||
print("Extra dependencies:\n{0}".format(str(deps_map)))
|
||||
print("Extra dependencies:\n{0}".format(json.dumps(deps_map)))
|
||||
# test for every test we found in the Makefile
|
||||
for target_alias, deps in deps_map.items():
|
||||
for test in sorted(tests):
|
||||
@@ -196,8 +198,8 @@ def generate_targets(repo_path, deps_map):
|
||||
test_target_name,
|
||||
match_src[0],
|
||||
is_parallel,
|
||||
deps['extra_deps'],
|
||||
deps['extra_compiler_flags'])
|
||||
json.dumps(deps['extra_deps']),
|
||||
json.dumps(deps['extra_compiler_flags']))
|
||||
|
||||
if test in _EXPORTED_TEST_LIBS:
|
||||
test_library = "%s_lib" % test_target_name
|
||||
@@ -220,6 +222,7 @@ def get_rocksdb_path():
|
||||
|
||||
return rocksdb_path
|
||||
|
||||
|
||||
def exit_with_error(msg):
|
||||
print(ColorString.error(msg))
|
||||
sys.exit(1)
|
||||
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# If clang_format_diff.py command is not specfied, we assume we are able to
|
||||
# access directly without any path.
|
||||
|
||||
TGT_DIFF=`git diff TARGETS | head -n 1`
|
||||
|
||||
if [ ! -z "$TGT_DIFF" ]
|
||||
then
|
||||
echo "TARGETS file has uncommitted changes. Skip this check."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo Backup original TARGETS file.
|
||||
|
||||
cp TARGETS TARGETS.bkp
|
||||
|
||||
${PYTHON:-python3} buckifier/buckify_rocksdb.py
|
||||
|
||||
TGT_DIFF=`git diff TARGETS | head -n 1`
|
||||
|
||||
if [ -z "$TGT_DIFF" ]
|
||||
then
|
||||
mv TARGETS.bkp TARGETS
|
||||
exit 0
|
||||
else
|
||||
echo "Please run '${PYTHON:-python3} buckifier/buckify_rocksdb.py' to update TARGETS file."
|
||||
echo "Do not manually update TARGETS file."
|
||||
${PYTHON:-python3} --version
|
||||
mv TARGETS.bkp TARGETS
|
||||
exit 1
|
||||
fi
|
||||
@@ -28,7 +28,8 @@ class TARGETSBuilder(object):
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.targets_file = open(path, 'w')
|
||||
self.targets_file.write(targets_cfg.rocksdb_target_header)
|
||||
header = targets_cfg.rocksdb_target_header_template
|
||||
self.targets_file.write(header)
|
||||
self.total_lib = 0
|
||||
self.total_bin = 0
|
||||
self.total_test = 0
|
||||
@@ -37,17 +38,35 @@ class TARGETSBuilder(object):
|
||||
def __del__(self):
|
||||
self.targets_file.close()
|
||||
|
||||
def add_library(self, name, srcs, deps=None, headers=None):
|
||||
def add_library(self, name, srcs, deps=None, headers=None,
|
||||
extra_external_deps=""):
|
||||
headers_attr_prefix = ""
|
||||
if headers is None:
|
||||
headers_attr_prefix = "auto_"
|
||||
headers = "AutoHeaders.RECURSIVE_GLOB"
|
||||
else:
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
self.targets_file.write(targets_cfg.library_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
headers_attr_prefix=headers_attr_prefix,
|
||||
headers=headers,
|
||||
deps=pretty_list(deps)))
|
||||
deps=pretty_list(deps),
|
||||
extra_external_deps=extra_external_deps))
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_rocksdb_library(self, name, srcs, headers=None):
|
||||
headers_attr_prefix = ""
|
||||
if headers is None:
|
||||
headers_attr_prefix = "auto_"
|
||||
headers = "AutoHeaders.RECURSIVE_GLOB"
|
||||
else:
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
self.targets_file.write(targets_cfg.rocksdb_library_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
headers_attr_prefix=headers_attr_prefix,
|
||||
headers=headers))
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_binary(self, name, srcs, deps=None):
|
||||
|
||||
+35
-14
@@ -4,7 +4,8 @@ from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
rocksdb_target_header = """# This file \100generated by `python buckifier/buckify_rocksdb.py`
|
||||
rocksdb_target_header_template = \
|
||||
"""# This file \100generated by `python3 buckifier/buckify_rocksdb.py`
|
||||
# --> DO NOT EDIT MANUALLY <--
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
@@ -32,7 +33,6 @@ ROCKSDB_EXTERNAL_DEPS = [
|
||||
("lz4", None, "lz4"),
|
||||
("zstd", None),
|
||||
("tbb", None),
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
ROCKSDB_OS_DEPS = [
|
||||
@@ -52,7 +52,9 @@ ROCKSDB_OS_PREPROCESSOR_FLAGS = [
|
||||
"-DROCKSDB_PTHREAD_ADAPTIVE_MUTEX",
|
||||
"-DROCKSDB_RANGESYNC_PRESENT",
|
||||
"-DROCKSDB_SCHED_GETCPU_PRESENT",
|
||||
"-DROCKSDB_IOURING_PRESENT",
|
||||
"-DHAVE_SSE42",
|
||||
"-DLIBURING",
|
||||
"-DNUMA",
|
||||
],
|
||||
),
|
||||
@@ -112,6 +114,11 @@ ROCKSDB_OS_DEPS += ([(
|
||||
"linux",
|
||||
["third-party//jemalloc:headers"],
|
||||
)] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_LIB_DEPS = [
|
||||
":rocksdb_lib",
|
||||
":rocksdb_test_lib",
|
||||
] if not is_opt_mode else [":rocksdb_lib"]
|
||||
"""
|
||||
|
||||
|
||||
@@ -126,6 +133,21 @@ cpp_library(
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [{deps}],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS{extra_external_deps},
|
||||
)
|
||||
"""
|
||||
|
||||
rocksdb_library_template = """
|
||||
cpp_library(
|
||||
name = "{name}",
|
||||
srcs = [{srcs}],
|
||||
{headers_attr_prefix}headers = {headers},
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = ROCKSDB_LIB_DEPS,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
"""
|
||||
@@ -160,18 +182,17 @@ ROCKS_TESTS = [
|
||||
# Do not build the tests in opt mode, since SyncPoint and other test code
|
||||
# will not be included.
|
||||
[
|
||||
test_binary(
|
||||
extra_compiler_flags = extra_compiler_flags,
|
||||
extra_deps = extra_deps,
|
||||
parallelism = parallelism,
|
||||
rocksdb_arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
rocksdb_compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
rocksdb_external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
rocksdb_os_deps = ROCKSDB_OS_DEPS,
|
||||
rocksdb_os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
rocksdb_preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
test_cc = test_cc,
|
||||
test_name = test_name,
|
||||
cpp_unittest(
|
||||
name = test_name,
|
||||
srcs = [test_cc],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"] + extra_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
("googletest", None, "gtest"),
|
||||
],
|
||||
)
|
||||
for test_name, test_cc, parallelism, extra_deps, extra_compiler_flags in ROCKS_TESTS
|
||||
if not is_opt_mode
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
# PLATFORM_LDFLAGS Linker flags
|
||||
# JAVA_LDFLAGS Linker flags for RocksDBJava
|
||||
# JAVA_STATIC_LDFLAGS Linker flags for RocksDBJava static build
|
||||
# JAVAC_ARGS Arguments for javac
|
||||
# PLATFORM_SHARED_EXT Extension for shared libraries
|
||||
# PLATFORM_SHARED_LDFLAGS Flags for building shared library
|
||||
# PLATFORM_SHARED_CFLAGS Flags for compiling objects for shared library
|
||||
@@ -27,6 +28,7 @@
|
||||
# -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:
|
||||
# Our project depends on gflags, which requires users to take some extra steps
|
||||
@@ -190,6 +192,17 @@ EOF
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread"
|
||||
# PORT_FILES=port/freebsd/freebsd_specific.cc
|
||||
;;
|
||||
GNU/kFreeBSD)
|
||||
PLATFORM=OS_GNU_KFREEBSD
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DOS_GNU_KFREEBSD"
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp"
|
||||
else
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt"
|
||||
# PORT_FILES=port/gnu_kfreebsd/gnu_kfreebsd_specific.cc
|
||||
;;
|
||||
NetBSD)
|
||||
PLATFORM=OS_NETBSD
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_NETBSD"
|
||||
@@ -239,10 +252,15 @@ esac
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS ${CXXFLAGS}"
|
||||
JAVA_LDFLAGS="$PLATFORM_LDFLAGS"
|
||||
JAVA_STATIC_LDFLAGS="$PLATFORM_LDFLAGS"
|
||||
JAVAC_ARGS="-source 7"
|
||||
|
||||
if [ "$CROSS_COMPILE" = "true" -o "$FBCODE_BUILD" = "true" ]; then
|
||||
# Cross-compiling; do not try any compilation tests.
|
||||
# Also don't need any compilation tests if compiling on fbcode
|
||||
if [ "$FBCODE_BUILD" = "true" ]; then
|
||||
# Enable backtrace on fbcode since the necessary libraries are present
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_BACKTRACE"
|
||||
fi
|
||||
true
|
||||
else
|
||||
if ! test $ROCKSDB_DISABLE_FALLOCATE; then
|
||||
@@ -423,7 +441,23 @@ EOF
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_MALLOC_USABLE_SIZE"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_MEMKIND; then
|
||||
# Test whether memkind library is installed
|
||||
$CXX $CFLAGS $COMMON_FLAGS -lmemkind -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <memkind.h>
|
||||
int main() {
|
||||
memkind_malloc(MEMKIND_DAX_KMEM, 1024);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DMEMKIND"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lmemkind"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lmemkind"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_PTHREAD_MUTEX_ADAPTIVE_NP; then
|
||||
# Test whether PTHREAD_MUTEX_ADAPTIVE_NP mutex type is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
@@ -508,6 +542,20 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_AUXV_GETAUXVAL; then
|
||||
# Test whether getauxval is supported
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <sys/auxv.h>
|
||||
int main() {
|
||||
uint64_t auxv = getauxval(AT_HWCAP);
|
||||
(void)auxv;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_AUXV_GETAUXVAL_PRESENT"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ALIGNED_NEW; then
|
||||
# Test whether c++17 aligned-new is supported
|
||||
$CXX $PLATFORM_CXXFLAGS -faligned-new -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
@@ -668,6 +716,7 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ "$FBCODE_BUILD" != "true" -a "$PLATFORM" = OS_LINUX ]; then
|
||||
$CXX $COMMON_FLAGS $PLATFORM_SHARED_CFLAGS -x c++ -c - -o test_dl.o 2>/dev/null <<EOF
|
||||
void dummy_func() {}
|
||||
@@ -696,6 +745,7 @@ echo "PLATFORM=$PLATFORM" >> "$OUTPUT"
|
||||
echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> "$OUTPUT"
|
||||
echo "JAVA_LDFLAGS=$JAVA_LDFLAGS" >> "$OUTPUT"
|
||||
echo "JAVA_STATIC_LDFLAGS=$JAVA_STATIC_LDFLAGS" >> "$OUTPUT"
|
||||
echo "JAVAC_ARGS=$JAVAC_ARGS" >> "$OUTPUT"
|
||||
echo "VALGRIND_VER=$VALGRIND_VER" >> "$OUTPUT"
|
||||
echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> "$OUTPUT"
|
||||
echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> "$OUTPUT"
|
||||
|
||||
+99
-33
@@ -2,41 +2,99 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# If clang_format_diff.py command is not specfied, we assume we are able to
|
||||
# access directly without any path.
|
||||
if [ -z $CLANG_FORMAT_DIFF ]
|
||||
then
|
||||
CLANG_FORMAT_DIFF="clang-format-diff.py"
|
||||
fi
|
||||
|
||||
# Check clang-format-diff.py
|
||||
if ! which $CLANG_FORMAT_DIFF &> /dev/null
|
||||
then
|
||||
echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!"
|
||||
echo "You can download clang-format-diff.py by running: "
|
||||
echo " curl --location http://goo.gl/iUW1u2 -o ${CLANG_FORMAT_DIFF}"
|
||||
echo "You can download clang-format by running:"
|
||||
echo " brew install clang-format"
|
||||
echo " Or"
|
||||
echo " apt install clang-format"
|
||||
echo " This might work too:"
|
||||
echo " yum install git-clang-format"
|
||||
echo "Then, move both files (i.e. ${CLANG_FORMAT_DIFF} and clang-format) to some directory within PATH=${PATH}"
|
||||
echo "and make sure ${CLANG_FORMAT_DIFF} is executable."
|
||||
exit 128
|
||||
fi
|
||||
print_usage () {
|
||||
echo "Usage:"
|
||||
echo "format-diff.sh [OPTIONS]"
|
||||
echo "-c: check only."
|
||||
echo "-h: print this message."
|
||||
}
|
||||
|
||||
# Check argparse, a library that clang-format-diff.py requires.
|
||||
python 2>/dev/null << EOF
|
||||
import argparse
|
||||
EOF
|
||||
while getopts ':ch' OPTION; do
|
||||
case "$OPTION" in
|
||||
c)
|
||||
CHECK_ONLY=1
|
||||
;;
|
||||
h)
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
?)
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$?" != 0 ]
|
||||
then
|
||||
echo "To run clang-format-diff.py, we'll need the library "argparse" to be"
|
||||
echo "installed. You can try either of the follow ways to install it:"
|
||||
echo " 1. Manually download argparse: https://pypi.python.org/pypi/argparse"
|
||||
echo " 2. easy_install argparse (if you have easy_install)"
|
||||
echo " 3. pip install argparse (if you have pip)"
|
||||
exit 129
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||
|
||||
if [ "$CLANG_FORMAT_DIFF" ]; then
|
||||
echo "Note: CLANG_FORMAT_DIFF='$CLANG_FORMAT_DIFF'"
|
||||
# Dry run to confirm dependencies like argparse
|
||||
if $CLANG_FORMAT_DIFF --help >/dev/null < /dev/null; then
|
||||
true #Good
|
||||
else
|
||||
exit 128
|
||||
fi
|
||||
else
|
||||
# First try directly executing the possibilities
|
||||
if clang-format-diff.py --help &> /dev/null < /dev/null; then
|
||||
CLANG_FORMAT_DIFF=clang-format-diff.py
|
||||
elif $REPO_ROOT/clang-format-diff.py --help &> /dev/null < /dev/null; then
|
||||
CLANG_FORMAT_DIFF=$REPO_ROOT/clang-format-diff.py
|
||||
else
|
||||
# This probably means we need to directly invoke the interpreter.
|
||||
# But first find clang-format-diff.py
|
||||
if [ -f "$REPO_ROOT/clang-format-diff.py" ]; then
|
||||
CFD_PATH="$REPO_ROOT/clang-format-diff.py"
|
||||
elif which clang-format-diff.py &> /dev/null; then
|
||||
CFD_PATH="$(which clang-format-diff.py)"
|
||||
else
|
||||
echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!"
|
||||
echo "You can download clang-format-diff.py by running: "
|
||||
echo " curl --location http://goo.gl/iUW1u2 -o ${CLANG_FORMAT_DIFF}"
|
||||
echo "You can download clang-format by running:"
|
||||
echo " brew install clang-format"
|
||||
echo " Or"
|
||||
echo " apt install clang-format"
|
||||
echo " This might work too:"
|
||||
echo " yum install git-clang-format"
|
||||
echo "Then, move both files (i.e. ${CLANG_FORMAT_DIFF} and clang-format) to some directory within PATH=${PATH}"
|
||||
echo "and make sure ${CLANG_FORMAT_DIFF} is executable."
|
||||
exit 128
|
||||
fi
|
||||
# Check argparse pre-req on interpreter, or it will fail
|
||||
if echo import argparse | ${PYTHON:-python3}; then
|
||||
true # Good
|
||||
else
|
||||
echo "To run clang-format-diff.py, we'll need the library "argparse" to be"
|
||||
echo "installed. You can try either of the follow ways to install it:"
|
||||
echo " 1. Manually download argparse: https://pypi.python.org/pypi/argparse"
|
||||
echo " 2. easy_install argparse (if you have easy_install)"
|
||||
echo " 3. pip install argparse (if you have pip)"
|
||||
exit 129
|
||||
fi
|
||||
# Unfortunately, some machines have a Python2 clang-format-diff.py
|
||||
# installed but only a Python3 interpreter installed. Rather than trying
|
||||
# different Python versions that might be installed, we can try migrating
|
||||
# the code to Python3 if it looks like Python2
|
||||
if grep -q "print '" "$CFD_PATH" && \
|
||||
${PYTHON:-python3} --version | grep -q 'ython 3'; then
|
||||
if [ ! -f "$REPO_ROOT/.py3/clang-format-diff.py" ]; then
|
||||
echo "Migrating $CFD_PATH to Python3 in a hidden file"
|
||||
mkdir -p "$REPO_ROOT/.py3"
|
||||
${PYTHON:-python3} -m lib2to3 -w -n -o "$REPO_ROOT/.py3" "$CFD_PATH" > /dev/null || exit 128
|
||||
fi
|
||||
CFD_PATH="$REPO_ROOT/.py3/clang-format-diff.py"
|
||||
fi
|
||||
CLANG_FORMAT_DIFF="${PYTHON:-python3} $CFD_PATH"
|
||||
# This had better work after all those checks
|
||||
if $CLANG_FORMAT_DIFF --help >/dev/null < /dev/null; then
|
||||
true #Good
|
||||
else
|
||||
exit 128
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# TODO(kailiu) following work is not complete since we still need to figure
|
||||
@@ -87,6 +145,14 @@ if [ -z "$diffs" ]
|
||||
then
|
||||
echo "Nothing needs to be reformatted!"
|
||||
exit 0
|
||||
elif [ $CHECK_ONLY ]
|
||||
then
|
||||
echo "Your change has unformatted code. Please run make format!"
|
||||
if [ $VERBOSE_CHECK ]; then
|
||||
clang-format --version
|
||||
echo "$diffs"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Highlight the insertion/deletion from the clang-format-diff.py's output
|
||||
@@ -121,7 +187,7 @@ if [ -z "$uncommitted_code" ]
|
||||
then
|
||||
git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -i -p 1
|
||||
else
|
||||
git diff -U0 HEAD^ | $CLANG_FORMAT_DIFF -i -p 1
|
||||
git diff -U0 HEAD | $CLANG_FORMAT_DIFF -i -p 1
|
||||
fi
|
||||
echo "Files reformatted!"
|
||||
|
||||
|
||||
@@ -5801,7 +5801,7 @@ sub workdir {
|
||||
. "-" . $self->seq();
|
||||
} else {
|
||||
$workdir = $opt::workdir;
|
||||
# Rsync treats /./ special. We dont want that
|
||||
# Rsync treats /./ special. We don't want that
|
||||
$workdir =~ s:/\./:/:g; # Remove /./
|
||||
$workdir =~ s:/+$::; # Remove ending / if any
|
||||
$workdir =~ s:^\./::g; # Remove starting ./ if any
|
||||
|
||||
@@ -818,6 +818,8 @@ run_format_compatible()
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
|
||||
export https_proxy="fwdproxy:8080"
|
||||
|
||||
tools/check_format_compatible.sh
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
set -e
|
||||
set -ex
|
||||
|
||||
ROCKSDB_VERSION="5.10.3"
|
||||
ZSTD_VERSION="1.1.3"
|
||||
ROCKSDB_VERSION="6.7.3"
|
||||
ZSTD_VERSION="1.4.4"
|
||||
|
||||
echo "This script configures CentOS with everything needed to build and run RocksDB"
|
||||
|
||||
@@ -40,5 +40,6 @@ cd /usr/local/rocksdb
|
||||
chown -R vagrant:vagrant /usr/local/rocksdb/
|
||||
sudo -u vagrant make static_lib
|
||||
cd examples/
|
||||
sudo -u vagrant make all
|
||||
sudo -u vagrant ./c_simple_example
|
||||
sudo -u vagrant LD_LIBRARY_PATH=/usr/local/lib/ make all
|
||||
sudo -u vagrant LD_LIBRARY_PATH=/usr/local/lib/ ./c_simple_example
|
||||
|
||||
|
||||
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
|
||||
#include "cache/lru_cache.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
#ifndef ROCKSDB_LITE
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
lru_cache_options_type_info = {
|
||||
{"capacity",
|
||||
{offsetof(struct LRUCacheOptions, capacity), OptionType::kSizeT,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, capacity)}},
|
||||
{"num_shard_bits",
|
||||
{offsetof(struct LRUCacheOptions, num_shard_bits), OptionType::kInt,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, num_shard_bits)}},
|
||||
{"strict_capacity_limit",
|
||||
{offsetof(struct LRUCacheOptions, strict_capacity_limit),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, strict_capacity_limit)}},
|
||||
{"high_pri_pool_ratio",
|
||||
{offsetof(struct LRUCacheOptions, high_pri_pool_ratio),
|
||||
OptionType::kDouble, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, high_pri_pool_ratio)}}};
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status Cache::CreateFromString(const ConfigOptions& config_options,
|
||||
const std::string& value,
|
||||
std::shared_ptr<Cache>* result) {
|
||||
Status status;
|
||||
std::shared_ptr<Cache> cache;
|
||||
if (value.find('=') == std::string::npos) {
|
||||
cache = NewLRUCache(ParseSizeT(value));
|
||||
} else {
|
||||
#ifndef ROCKSDB_LITE
|
||||
LRUCacheOptions cache_opts;
|
||||
status = OptionTypeInfo::ParseStruct(
|
||||
config_options, "", &lru_cache_options_type_info, "", value,
|
||||
reinterpret_cast<char*>(&cache_opts));
|
||||
if (status.ok()) {
|
||||
cache = NewLRUCache(cache_opts);
|
||||
}
|
||||
#else
|
||||
(void)config_options;
|
||||
status = Status::NotSupported("Cannot load cache in LITE mode ", value);
|
||||
#endif //! ROCKSDB_LITE
|
||||
}
|
||||
if (status.ok()) {
|
||||
result->swap(cache);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+163
-63
@@ -14,34 +14,47 @@ int main() {
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/gflags_compat.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/random.h"
|
||||
|
||||
using GFLAGS_NAMESPACE::ParseCommandLineFlags;
|
||||
|
||||
static const uint32_t KB = 1024;
|
||||
static constexpr uint32_t KiB = uint32_t{1} << 10;
|
||||
static constexpr uint32_t MiB = KiB << 10;
|
||||
static constexpr uint64_t GiB = MiB << 10;
|
||||
|
||||
DEFINE_int32(threads, 16, "Number of concurrent threads to run.");
|
||||
DEFINE_int64(cache_size, 8 * KB * KB,
|
||||
"Number of bytes to use as a cache of uncompressed data.");
|
||||
DEFINE_int32(num_shard_bits, 4, "shard_bits.");
|
||||
DEFINE_uint32(threads, 16, "Number of concurrent threads to run.");
|
||||
DEFINE_uint64(cache_size, 1 * GiB,
|
||||
"Number of bytes to use as a cache of uncompressed data.");
|
||||
DEFINE_uint32(num_shard_bits, 6, "shard_bits.");
|
||||
|
||||
DEFINE_int64(max_key, 1 * KB * KB * KB, "Max number of key to place in cache");
|
||||
DEFINE_uint64(ops_per_thread, 1200000, "Number of operations per thread.");
|
||||
DEFINE_double(resident_ratio, 0.25,
|
||||
"Ratio of keys fitting in cache to keyspace.");
|
||||
DEFINE_uint64(ops_per_thread, 0,
|
||||
"Number of operations per thread. (Default: 5 * keyspace size)");
|
||||
DEFINE_uint32(value_bytes, 8 * KiB, "Size of each value added.");
|
||||
|
||||
DEFINE_bool(populate_cache, false, "Populate cache before operations");
|
||||
DEFINE_int32(insert_percent, 40,
|
||||
"Ratio of insert to total workload (expressed as a percentage)");
|
||||
DEFINE_int32(lookup_percent, 50,
|
||||
"Ratio of lookup to total workload (expressed as a percentage)");
|
||||
DEFINE_int32(erase_percent, 10,
|
||||
"Ratio of erase to total workload (expressed as a percentage)");
|
||||
DEFINE_uint32(skew, 5, "Degree of skew in key selection");
|
||||
DEFINE_bool(populate_cache, true, "Populate cache before operations");
|
||||
|
||||
DEFINE_uint32(lookup_insert_percent, 87,
|
||||
"Ratio of lookup (+ insert on not found) to total workload "
|
||||
"(expressed as a percentage)");
|
||||
DEFINE_uint32(insert_percent, 2,
|
||||
"Ratio of insert to total workload (expressed as a percentage)");
|
||||
DEFINE_uint32(lookup_percent, 10,
|
||||
"Ratio of lookup to total workload (expressed as a percentage)");
|
||||
DEFINE_uint32(erase_percent, 1,
|
||||
"Ratio of erase to total workload (expressed as a percentage)");
|
||||
|
||||
DEFINE_bool(use_clock_cache, false, "");
|
||||
|
||||
@@ -49,21 +62,15 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class CacheBench;
|
||||
namespace {
|
||||
void deleter(const Slice& /*key*/, void* value) {
|
||||
delete reinterpret_cast<char *>(value);
|
||||
}
|
||||
|
||||
// State shared by all concurrent executions of the same benchmark.
|
||||
class SharedState {
|
||||
public:
|
||||
explicit SharedState(CacheBench* cache_bench)
|
||||
: cv_(&mu_),
|
||||
num_threads_(FLAGS_threads),
|
||||
num_initialized_(0),
|
||||
start_(false),
|
||||
num_done_(0),
|
||||
cache_bench_(cache_bench) {
|
||||
}
|
||||
cache_bench_(cache_bench) {}
|
||||
|
||||
~SharedState() {}
|
||||
|
||||
@@ -87,13 +94,9 @@ class SharedState {
|
||||
num_done_++;
|
||||
}
|
||||
|
||||
bool AllInitialized() const {
|
||||
return num_initialized_ >= num_threads_;
|
||||
}
|
||||
bool AllInitialized() const { return num_initialized_ >= FLAGS_threads; }
|
||||
|
||||
bool AllDone() const {
|
||||
return num_done_ >= num_threads_;
|
||||
}
|
||||
bool AllDone() const { return num_done_ >= FLAGS_threads; }
|
||||
|
||||
void SetStart() {
|
||||
start_ = true;
|
||||
@@ -107,7 +110,6 @@ class SharedState {
|
||||
port::Mutex mu_;
|
||||
port::CondVar cv_;
|
||||
|
||||
const uint64_t num_threads_;
|
||||
uint64_t num_initialized_;
|
||||
bool start_;
|
||||
uint64_t num_done_;
|
||||
@@ -118,17 +120,69 @@ class SharedState {
|
||||
// Per-thread state for concurrent executions of the same benchmark.
|
||||
struct ThreadState {
|
||||
uint32_t tid;
|
||||
Random rnd;
|
||||
Random64 rnd;
|
||||
SharedState* shared;
|
||||
|
||||
ThreadState(uint32_t index, SharedState* _shared)
|
||||
: tid(index), rnd(1000 + index), shared(_shared) {}
|
||||
};
|
||||
|
||||
struct KeyGen {
|
||||
char key_data[27];
|
||||
|
||||
Slice GetRand(Random64& rnd, uint64_t max_key) {
|
||||
uint64_t raw = rnd.Next();
|
||||
// Skew according to setting
|
||||
for (uint32_t i = 0; i < FLAGS_skew; ++i) {
|
||||
raw = std::min(raw, rnd.Next());
|
||||
}
|
||||
uint64_t key = fastrange64(raw, max_key);
|
||||
// Variable size and alignment
|
||||
size_t off = key % 8;
|
||||
key_data[0] = char{42};
|
||||
EncodeFixed64(key_data + 1, key);
|
||||
key_data[9] = char{11};
|
||||
EncodeFixed64(key_data + 10, key);
|
||||
key_data[18] = char{4};
|
||||
EncodeFixed64(key_data + 19, key);
|
||||
return Slice(&key_data[off], sizeof(key_data) - off);
|
||||
}
|
||||
};
|
||||
|
||||
char* createValue(Random64& rnd) {
|
||||
char* rv = new char[FLAGS_value_bytes];
|
||||
// Fill with some filler data, and take some CPU time
|
||||
for (uint32_t i = 0; i < FLAGS_value_bytes; i += 8) {
|
||||
EncodeFixed64(rv + i, rnd.Next());
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void deleter(const Slice& /*key*/, void* value) {
|
||||
delete[] static_cast<char*>(value);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class CacheBench {
|
||||
static constexpr uint64_t kHundredthUint64 =
|
||||
std::numeric_limits<uint64_t>::max() / 100U;
|
||||
|
||||
public:
|
||||
CacheBench() : num_threads_(FLAGS_threads) {
|
||||
CacheBench()
|
||||
: max_key_(static_cast<uint64_t>(FLAGS_cache_size / FLAGS_resident_ratio /
|
||||
FLAGS_value_bytes)),
|
||||
lookup_insert_threshold_(kHundredthUint64 *
|
||||
FLAGS_lookup_insert_percent),
|
||||
insert_threshold_(lookup_insert_threshold_ +
|
||||
kHundredthUint64 * FLAGS_insert_percent),
|
||||
lookup_threshold_(insert_threshold_ +
|
||||
kHundredthUint64 * FLAGS_lookup_percent),
|
||||
erase_threshold_(lookup_threshold_ +
|
||||
kHundredthUint64 * FLAGS_erase_percent) {
|
||||
if (erase_threshold_ != 100U * kHundredthUint64) {
|
||||
fprintf(stderr, "Percentages must add to 100.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (FLAGS_use_clock_cache) {
|
||||
cache_ = NewClockCache(FLAGS_cache_size, FLAGS_num_shard_bits);
|
||||
if (!cache_) {
|
||||
@@ -138,18 +192,19 @@ class CacheBench {
|
||||
} else {
|
||||
cache_ = NewLRUCache(FLAGS_cache_size, FLAGS_num_shard_bits);
|
||||
}
|
||||
if (FLAGS_ops_per_thread == 0) {
|
||||
FLAGS_ops_per_thread = 5 * max_key_;
|
||||
}
|
||||
}
|
||||
|
||||
~CacheBench() {}
|
||||
|
||||
void PopulateCache() {
|
||||
Random rnd(1);
|
||||
for (int64_t i = 0; i < FLAGS_cache_size; i++) {
|
||||
uint64_t rand_key = rnd.Next() % FLAGS_max_key;
|
||||
// Cast uint64* to be char*, data would be copied to cache
|
||||
Slice key(reinterpret_cast<char*>(&rand_key), 8);
|
||||
// do insert
|
||||
cache_->Insert(key, new char[10], 1, &deleter);
|
||||
Random64 rnd(1);
|
||||
KeyGen keygen;
|
||||
for (uint64_t i = 0; i < 2 * FLAGS_cache_size; i += FLAGS_value_bytes) {
|
||||
cache_->Insert(keygen.GetRand(rnd, max_key_), createValue(rnd),
|
||||
FLAGS_value_bytes, &deleter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,10 +213,10 @@ class CacheBench {
|
||||
|
||||
PrintEnv();
|
||||
SharedState shared(this);
|
||||
std::vector<ThreadState*> threads(num_threads_);
|
||||
for (uint32_t i = 0; i < num_threads_; i++) {
|
||||
threads[i] = new ThreadState(i, &shared);
|
||||
env->StartThread(ThreadBody, threads[i]);
|
||||
std::vector<std::unique_ptr<ThreadState> > threads(FLAGS_threads);
|
||||
for (uint32_t i = 0; i < FLAGS_threads; i++) {
|
||||
threads[i].reset(new ThreadState(i, &shared));
|
||||
env->StartThread(ThreadBody, threads[i].get());
|
||||
}
|
||||
{
|
||||
MutexLock l(shared.GetMutex());
|
||||
@@ -192,10 +247,15 @@ class CacheBench {
|
||||
|
||||
private:
|
||||
std::shared_ptr<Cache> cache_;
|
||||
uint32_t num_threads_;
|
||||
const uint64_t max_key_;
|
||||
// Cumulative thresholds in the space of a random uint64_t
|
||||
const uint64_t lookup_insert_threshold_;
|
||||
const uint64_t insert_threshold_;
|
||||
const uint64_t lookup_threshold_;
|
||||
const uint64_t erase_threshold_;
|
||||
|
||||
static void ThreadBody(void* v) {
|
||||
ThreadState* thread = reinterpret_cast<ThreadState*>(v);
|
||||
ThreadState* thread = static_cast<ThreadState*>(v);
|
||||
SharedState* shared = thread->shared;
|
||||
|
||||
{
|
||||
@@ -220,40 +280,78 @@ class CacheBench {
|
||||
}
|
||||
|
||||
void OperateCache(ThreadState* thread) {
|
||||
// To use looked-up values
|
||||
uint64_t result = 0;
|
||||
// To hold handles for a non-trivial amount of time
|
||||
Cache::Handle* handle = nullptr;
|
||||
KeyGen gen;
|
||||
for (uint64_t i = 0; i < FLAGS_ops_per_thread; i++) {
|
||||
uint64_t rand_key = thread->rnd.Next() % FLAGS_max_key;
|
||||
// Cast uint64* to be char*, data would be copied to cache
|
||||
Slice key(reinterpret_cast<char*>(&rand_key), 8);
|
||||
int32_t prob_op = thread->rnd.Uniform(100);
|
||||
if (prob_op >= 0 && prob_op < FLAGS_insert_percent) {
|
||||
// do insert
|
||||
cache_->Insert(key, new char[10], 1, &deleter);
|
||||
} else if (prob_op -= FLAGS_insert_percent &&
|
||||
prob_op < FLAGS_lookup_percent) {
|
||||
// do lookup
|
||||
auto handle = cache_->Lookup(key);
|
||||
Slice key = gen.GetRand(thread->rnd, max_key_);
|
||||
uint64_t random_op = thread->rnd.Next();
|
||||
if (random_op < lookup_insert_threshold_) {
|
||||
if (handle) {
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
} else if (prob_op -= FLAGS_lookup_percent &&
|
||||
prob_op < FLAGS_erase_percent) {
|
||||
// do lookup
|
||||
handle = cache_->Lookup(key);
|
||||
if (handle) {
|
||||
// do something with the data
|
||||
result += NPHash64(static_cast<char*>(cache_->Value(handle)),
|
||||
FLAGS_value_bytes);
|
||||
} else {
|
||||
// do insert
|
||||
cache_->Insert(key, createValue(thread->rnd), FLAGS_value_bytes,
|
||||
&deleter, &handle);
|
||||
}
|
||||
} else if (random_op < insert_threshold_) {
|
||||
if (handle) {
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
// do insert
|
||||
cache_->Insert(key, createValue(thread->rnd), FLAGS_value_bytes,
|
||||
&deleter, &handle);
|
||||
} else if (random_op < lookup_threshold_) {
|
||||
if (handle) {
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
// do lookup
|
||||
handle = cache_->Lookup(key);
|
||||
if (handle) {
|
||||
// do something with the data
|
||||
result += NPHash64(static_cast<char*>(cache_->Value(handle)),
|
||||
FLAGS_value_bytes);
|
||||
}
|
||||
} else if (random_op < erase_threshold_) {
|
||||
// do erase
|
||||
cache_->Erase(key);
|
||||
} else {
|
||||
// Should be extremely unlikely (noop)
|
||||
assert(random_op >= kHundredthUint64 * 100U);
|
||||
}
|
||||
}
|
||||
if (handle) {
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintEnv() const {
|
||||
printf("RocksDB version : %d.%d\n", kMajorVersion, kMinorVersion);
|
||||
printf("Number of threads : %d\n", FLAGS_threads);
|
||||
printf("Number of threads : %u\n", FLAGS_threads);
|
||||
printf("Ops per thread : %" PRIu64 "\n", FLAGS_ops_per_thread);
|
||||
printf("Cache size : %" PRIu64 "\n", FLAGS_cache_size);
|
||||
printf("Num shard bits : %d\n", FLAGS_num_shard_bits);
|
||||
printf("Max key : %" PRIu64 "\n", FLAGS_max_key);
|
||||
printf("Populate cache : %d\n", FLAGS_populate_cache);
|
||||
printf("Insert percentage : %d%%\n", FLAGS_insert_percent);
|
||||
printf("Lookup percentage : %d%%\n", FLAGS_lookup_percent);
|
||||
printf("Erase percentage : %d%%\n", FLAGS_erase_percent);
|
||||
printf("Num shard bits : %u\n", FLAGS_num_shard_bits);
|
||||
printf("Max key : %" PRIu64 "\n", max_key_);
|
||||
printf("Resident ratio : %g\n", FLAGS_resident_ratio);
|
||||
printf("Skew degree : %u\n", FLAGS_skew);
|
||||
printf("Populate cache : %d\n", int{FLAGS_populate_cache});
|
||||
printf("Lookup+Insert pct : %u%%\n", FLAGS_lookup_insert_percent);
|
||||
printf("Insert percentage : %u%%\n", FLAGS_insert_percent);
|
||||
printf("Lookup percentage : %u%%\n", FLAGS_lookup_percent);
|
||||
printf("Erase percentage : %u%%\n", FLAGS_erase_percent);
|
||||
printf("----------------------------\n");
|
||||
}
|
||||
};
|
||||
@@ -270,6 +368,8 @@ int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::CacheBench bench;
|
||||
if (FLAGS_populate_cache) {
|
||||
bench.PopulateCache();
|
||||
printf("Population complete\n");
|
||||
printf("----------------------------\n");
|
||||
}
|
||||
if (bench.Run()) {
|
||||
return 0;
|
||||
|
||||
Vendored
+12
-4
@@ -182,7 +182,7 @@ struct CacheHandle {
|
||||
void (*deleter)(const Slice&, void* value);
|
||||
|
||||
// Flags and counters associated with the cache handle:
|
||||
// lowest bit: n-cache bit
|
||||
// lowest bit: in-cache bit
|
||||
// second lowest bit: usage bit
|
||||
// the rest bits: reference count
|
||||
// The handle is unused when flags equals to 0. The thread decreases the count
|
||||
@@ -341,7 +341,8 @@ class ClockCacheShard final : public CacheShard {
|
||||
CacheHandle* Insert(const Slice& key, uint32_t hash, void* value,
|
||||
size_t change,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
bool hold_reference, CleanupContext* context);
|
||||
bool hold_reference, CleanupContext* context,
|
||||
bool* overwritten);
|
||||
|
||||
// Guards list_, head_, and recycle_. In addition, updating table_ also has
|
||||
// to hold the mutex, to avoid the cache being in inconsistent state.
|
||||
@@ -564,7 +565,8 @@ void ClockCacheShard::SetStrictCapacityLimit(bool strict_capacity_limit) {
|
||||
CacheHandle* ClockCacheShard::Insert(
|
||||
const Slice& key, uint32_t hash, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value), bool hold_reference,
|
||||
CleanupContext* context) {
|
||||
CleanupContext* context, bool* overwritten) {
|
||||
assert(overwritten != nullptr && *overwritten == false);
|
||||
size_t total_charge =
|
||||
CacheHandle::CalcTotalCharge(key, charge, metadata_charge_policy_);
|
||||
MutexLock l(&mutex_);
|
||||
@@ -597,6 +599,7 @@ CacheHandle* ClockCacheShard::Insert(
|
||||
handle->flags.store(flags, std::memory_order_relaxed);
|
||||
HashTable::accessor accessor;
|
||||
if (table_.find(accessor, CacheKey(key, hash))) {
|
||||
*overwritten = true;
|
||||
CacheHandle* existing_handle = accessor->second;
|
||||
table_.erase(accessor);
|
||||
UnsetInCache(existing_handle, context);
|
||||
@@ -619,8 +622,9 @@ Status ClockCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
char* key_data = new char[key.size()];
|
||||
memcpy(key_data, key.data(), key.size());
|
||||
Slice key_copy(key_data, key.size());
|
||||
bool overwritten = false;
|
||||
CacheHandle* handle = Insert(key_copy, hash, value, charge, deleter,
|
||||
out_handle != nullptr, &context);
|
||||
out_handle != nullptr, &context, &overwritten);
|
||||
Status s;
|
||||
if (out_handle != nullptr) {
|
||||
if (handle == nullptr) {
|
||||
@@ -629,6 +633,10 @@ Status ClockCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
*out_handle = reinterpret_cast<Cache::Handle*>(handle);
|
||||
}
|
||||
}
|
||||
if (overwritten) {
|
||||
assert(s.ok());
|
||||
s = Status::OkOverwritten();
|
||||
}
|
||||
Cleanup(context);
|
||||
return s;
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -386,6 +386,7 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
LRUHandle* old = table_.Insert(e);
|
||||
usage_ += total_charge;
|
||||
if (old != nullptr) {
|
||||
s = Status::OkOverwritten();
|
||||
assert(old->InCache());
|
||||
old->SetInCache(false);
|
||||
if (!old->HasRefs()) {
|
||||
|
||||
@@ -1,3 +1,54 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/modules")
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
set(GFLAGS_USE_TARGET_NAMESPACE @GFLAGS_USE_TARGET_NAMESPACE@)
|
||||
|
||||
if(@WITH_JEMALLOC@)
|
||||
find_dependency(JeMalloc)
|
||||
endif()
|
||||
|
||||
if(@WITH_GFLAGS@)
|
||||
find_dependency(gflags CONFIG)
|
||||
if(NOT gflags_FOUND)
|
||||
find_dependency(gflags)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(@WITH_SNAPPY@)
|
||||
find_dependency(Snappy CONFIG)
|
||||
if(NOT Snappy_FOUND)
|
||||
find_dependency(Snappy)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(@WITH_ZLIB@)
|
||||
find_dependency(ZLIB)
|
||||
endif()
|
||||
|
||||
if(@WITH_BZ2@)
|
||||
find_dependency(BZip2)
|
||||
endif()
|
||||
|
||||
if(@WITH_LZ4@)
|
||||
find_dependency(lz4)
|
||||
endif()
|
||||
|
||||
if(@WITH_ZSTD@)
|
||||
find_dependency(zstd)
|
||||
endif()
|
||||
|
||||
if(@WITH_NUMA@)
|
||||
find_dependency(NUMA)
|
||||
endif()
|
||||
|
||||
if(@WITH_TBB@)
|
||||
find_dependency(TBB)
|
||||
endif()
|
||||
|
||||
find_dependency(Threads)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/RocksDBTargets.cmake")
|
||||
check_required_components(RocksDB)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
macro(get_cxx_std_flags FLAGS_VARIABLE)
|
||||
if( CMAKE_CXX_STANDARD_REQUIRED )
|
||||
set(${FLAGS_VARIABLE} ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION})
|
||||
else()
|
||||
set(${FLAGS_VARIABLE} ${CMAKE_CXX${CMAKE_CXX_STANDARD}_EXTENSION_COMPILE_OPTION})
|
||||
endif()
|
||||
endmacro()
|
||||
@@ -0,0 +1,29 @@
|
||||
# - Find Snappy
|
||||
# Find the snappy compression library and includes
|
||||
#
|
||||
# Snappy_INCLUDE_DIRS - where to find snappy.h, etc.
|
||||
# Snappy_LIBRARIES - List of libraries when using snappy.
|
||||
# Snappy_FOUND - True if snappy found.
|
||||
|
||||
find_path(Snappy_INCLUDE_DIRS
|
||||
NAMES snappy.h
|
||||
HINTS ${snappy_ROOT_DIR}/include)
|
||||
|
||||
find_library(Snappy_LIBRARIES
|
||||
NAMES snappy
|
||||
HINTS ${snappy_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Snappy DEFAULT_MSG Snappy_LIBRARIES Snappy_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
Snappy_LIBRARIES
|
||||
Snappy_INCLUDE_DIRS)
|
||||
|
||||
if(Snappy_FOUND AND NOT (TARGET Snappy::snappy))
|
||||
add_library (Snappy::snappy UNKNOWN IMPORTED)
|
||||
set_target_properties(Snappy::snappy
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${Snappy_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${Snappy_INCLUDE_DIRS})
|
||||
endif()
|
||||
@@ -1,8 +1,8 @@
|
||||
# - Find gflags library
|
||||
# Find the gflags includes and library
|
||||
#
|
||||
# gflags_INCLUDE_DIR - where to find gflags.h.
|
||||
# gflags_LIBRARIES - List of libraries when using gflags.
|
||||
# GFLAGS_INCLUDE_DIR - where to find gflags.h.
|
||||
# GFLAGS_LIBRARIES - List of libraries when using gflags.
|
||||
# gflags_FOUND - True if gflags found.
|
||||
|
||||
find_path(GFLAGS_INCLUDE_DIR
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# - Find Snappy
|
||||
# Find the snappy compression library and includes
|
||||
#
|
||||
# snappy_INCLUDE_DIRS - where to find snappy.h, etc.
|
||||
# snappy_LIBRARIES - List of libraries when using snappy.
|
||||
# snappy_FOUND - True if snappy found.
|
||||
|
||||
find_path(snappy_INCLUDE_DIRS
|
||||
NAMES snappy.h
|
||||
HINTS ${snappy_ROOT_DIR}/include)
|
||||
|
||||
find_library(snappy_LIBRARIES
|
||||
NAMES snappy
|
||||
HINTS ${snappy_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(snappy DEFAULT_MSG snappy_LIBRARIES snappy_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
snappy_LIBRARIES
|
||||
snappy_INCLUDE_DIRS)
|
||||
|
||||
if(snappy_FOUND AND NOT (TARGET snappy::snappy))
|
||||
add_library (snappy::snappy UNKNOWN IMPORTED)
|
||||
set_target_properties(snappy::snappy
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${snappy_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${snappy_INCLUDE_DIRS})
|
||||
endif()
|
||||
@@ -12,21 +12,24 @@ fi
|
||||
ROOT=".."
|
||||
# Fetch right version of gcov
|
||||
if [ -d /mnt/gvfs/third-party -a -z "$CXX" ]; then
|
||||
source $ROOT/build_tools/fbcode_config.sh
|
||||
source $ROOT/build_tools/fbcode_config_platform007.sh
|
||||
GCOV=$GCC_BASE/bin/gcov
|
||||
else
|
||||
GCOV=$(which gcov)
|
||||
fi
|
||||
echo -e "Using $GCOV"
|
||||
|
||||
COVERAGE_DIR="$PWD/COVERAGE_REPORT"
|
||||
mkdir -p $COVERAGE_DIR
|
||||
|
||||
# Find all gcno files to generate the coverage report
|
||||
|
||||
PYTHON=${1:-`which python`}
|
||||
echo -e "Using $PYTHON"
|
||||
GCNO_FILES=`find $ROOT -name "*.gcno"`
|
||||
$GCOV --preserve-paths --relative-only --no-output $GCNO_FILES 2>/dev/null |
|
||||
# Parse the raw gcov report to more human readable form.
|
||||
python $ROOT/coverage/parse_gcov_output.py |
|
||||
$PYTHON $ROOT/coverage/parse_gcov_output.py |
|
||||
# Write the output to both stdout and report file.
|
||||
tee $COVERAGE_DIR/coverage_report_all.txt &&
|
||||
echo -e "Generated coverage report for all files: $COVERAGE_DIR/coverage_report_all.txt\n"
|
||||
@@ -41,7 +44,7 @@ RECENT_REPORT=$COVERAGE_DIR/coverage_report_recent.txt
|
||||
|
||||
echo -e "Recently updated files: $LATEST_FILES\n" > $RECENT_REPORT
|
||||
$GCOV --preserve-paths --relative-only --no-output $GCNO_FILES 2>/dev/null |
|
||||
python $ROOT/coverage/parse_gcov_output.py -interested-files $LATEST_FILES |
|
||||
$PYTHON $ROOT/coverage/parse_gcov_output.py -interested-files $LATEST_FILES |
|
||||
tee -a $RECENT_REPORT &&
|
||||
echo -e "Generated coverage report for recently updated files: $RECENT_REPORT\n"
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import optparse
|
||||
import re
|
||||
import sys
|
||||
|
||||
from optparse import OptionParser
|
||||
|
||||
# the gcov report follows certain pattern. Each file will have two lines
|
||||
# of report, from which we can extract the file name, total lines and coverage
|
||||
# percentage.
|
||||
@@ -48,7 +50,7 @@ def parse_gcov_report(gcov_input):
|
||||
def get_option_parser():
|
||||
usage = "Parse the gcov output and generate more human-readable code " +\
|
||||
"coverage report."
|
||||
parser = OptionParser(usage)
|
||||
parser = optparse.OptionParser(usage)
|
||||
|
||||
parser.add_option(
|
||||
"--interested-files", "-i",
|
||||
@@ -73,8 +75,8 @@ def display_file_coverage(per_file_coverage, total_coverage):
|
||||
header_template = \
|
||||
"%" + str(max_file_name_length) + "s\t%s\t%s"
|
||||
separator = "-" * (max_file_name_length + 10 + 20)
|
||||
print header_template % ("Filename", "Coverage", "Lines") # noqa: E999 T25377293 Grandfathered in
|
||||
print separator
|
||||
print(header_template % ("Filename", "Coverage", "Lines")) # noqa: E999 T25377293 Grandfathered in
|
||||
print(separator)
|
||||
|
||||
# -- Print body
|
||||
# template for printing coverage report for each file.
|
||||
@@ -82,12 +84,12 @@ def display_file_coverage(per_file_coverage, total_coverage):
|
||||
|
||||
for fname, coverage_info in per_file_coverage.items():
|
||||
coverage, lines = coverage_info
|
||||
print record_template % (fname, coverage, lines)
|
||||
print(record_template % (fname, coverage, lines))
|
||||
|
||||
# -- Print footer
|
||||
if total_coverage:
|
||||
print separator
|
||||
print record_template % ("Total", total_coverage[0], total_coverage[1])
|
||||
print(separator)
|
||||
print(record_template % ("Total", total_coverage[0], total_coverage[1]))
|
||||
|
||||
def report_coverage():
|
||||
parser = get_option_parser()
|
||||
@@ -111,7 +113,7 @@ def report_coverage():
|
||||
total_coverage = None
|
||||
|
||||
if not len(per_file_coverage):
|
||||
print >> sys.stderr, "Cannot find coverage info for the given files."
|
||||
print("Cannot find coverage info for the given files.", file=sys.stderr)
|
||||
return
|
||||
display_file_coverage(per_file_coverage, total_coverage)
|
||||
|
||||
|
||||
@@ -56,8 +56,9 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
// TODO(yiwu): For last_seq_same_as_publish_seq_==false, this is not the
|
||||
// correct behavior. Will be corrected automatically when we take a snapshot
|
||||
// here for the case of WritePreparedTxnDB.
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
uint64_t cur_sv_number = cfd_->GetSuperVersionNumber();
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:1");
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:2");
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
@@ -65,6 +66,7 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
new (&arena_) Arena();
|
||||
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(latest_seq);
|
||||
}
|
||||
@@ -75,10 +77,10 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator(),
|
||||
latest_seq);
|
||||
latest_seq, /* allow_unprepared_value */ true);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
} else {
|
||||
db_iter_->set_sequence(latest_seq);
|
||||
db_iter_->set_sequence(db_impl_->GetLatestSequenceNumber());
|
||||
db_iter_->set_valid(false);
|
||||
}
|
||||
return Status::OK();
|
||||
|
||||
@@ -51,6 +51,8 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
bool Valid() const override { return db_iter_->Valid(); }
|
||||
void SeekToFirst() override { db_iter_->SeekToFirst(); }
|
||||
void SeekToLast() override { db_iter_->SeekToLast(); }
|
||||
// 'target' does not contain timestamp, even if user timestamp feature is
|
||||
// enabled.
|
||||
void Seek(const Slice& target) override { db_iter_->Seek(target); }
|
||||
void SeekForPrev(const Slice& target) override {
|
||||
db_iter_->SeekForPrev(target);
|
||||
@@ -60,6 +62,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
Slice key() const override { return db_iter_->key(); }
|
||||
Slice value() const override { return db_iter_->value(); }
|
||||
Status status() const override { return db_iter_->status(); }
|
||||
Slice timestamp() const override { return db_iter_->timestamp(); }
|
||||
bool IsBlob() const { return db_iter_->IsBlob(); }
|
||||
|
||||
Status GetProperty(std::string prop_name, std::string* prop) override;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
constexpr uint64_t kInvalidBlobFileNumber = 0;
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_file_addition.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "logging/event_logger.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Tags for custom fields. Note that these get persisted in the manifest,
|
||||
// so existing tags should not be modified.
|
||||
enum BlobFileAddition::CustomFieldTags : uint32_t {
|
||||
kEndMarker,
|
||||
|
||||
// Add forward compatible fields here
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
kForwardIncompatibleMask = 1 << 6,
|
||||
|
||||
// Add forward incompatible fields here
|
||||
};
|
||||
|
||||
void BlobFileAddition::EncodeTo(std::string* output) const {
|
||||
PutVarint64(output, blob_file_number_);
|
||||
PutVarint64(output, total_blob_count_);
|
||||
PutVarint64(output, total_blob_bytes_);
|
||||
PutLengthPrefixedSlice(output, checksum_method_);
|
||||
PutLengthPrefixedSlice(output, checksum_value_);
|
||||
|
||||
// Encode any custom fields here. The format to use is a Varint32 tag (see
|
||||
// CustomFieldTags above) followed by a length prefixed slice. Unknown custom
|
||||
// fields will be ignored during decoding unless they're in the forward
|
||||
// incompatible range.
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("BlobFileAddition::EncodeTo::CustomFields", output);
|
||||
|
||||
PutVarint32(output, kEndMarker);
|
||||
}
|
||||
|
||||
Status BlobFileAddition::DecodeFrom(Slice* input) {
|
||||
constexpr char class_name[] = "BlobFileAddition";
|
||||
|
||||
if (!GetVarint64(input, &blob_file_number_)) {
|
||||
return Status::Corruption(class_name, "Error decoding blob file number");
|
||||
}
|
||||
|
||||
if (!GetVarint64(input, &total_blob_count_)) {
|
||||
return Status::Corruption(class_name, "Error decoding total blob count");
|
||||
}
|
||||
|
||||
if (!GetVarint64(input, &total_blob_bytes_)) {
|
||||
return Status::Corruption(class_name, "Error decoding total blob bytes");
|
||||
}
|
||||
|
||||
Slice checksum_method;
|
||||
if (!GetLengthPrefixedSlice(input, &checksum_method)) {
|
||||
return Status::Corruption(class_name, "Error decoding checksum method");
|
||||
}
|
||||
checksum_method_ = checksum_method.ToString();
|
||||
|
||||
Slice checksum_value;
|
||||
if (!GetLengthPrefixedSlice(input, &checksum_value)) {
|
||||
return Status::Corruption(class_name, "Error decoding checksum value");
|
||||
}
|
||||
checksum_value_ = checksum_value.ToString();
|
||||
|
||||
while (true) {
|
||||
uint32_t custom_field_tag = 0;
|
||||
if (!GetVarint32(input, &custom_field_tag)) {
|
||||
return Status::Corruption(class_name, "Error decoding custom field tag");
|
||||
}
|
||||
|
||||
if (custom_field_tag == kEndMarker) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (custom_field_tag & kForwardIncompatibleMask) {
|
||||
return Status::Corruption(
|
||||
class_name, "Forward incompatible custom field encountered");
|
||||
}
|
||||
|
||||
Slice custom_field_value;
|
||||
if (!GetLengthPrefixedSlice(input, &custom_field_value)) {
|
||||
return Status::Corruption(class_name,
|
||||
"Error decoding custom field value");
|
||||
}
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::string BlobFileAddition::DebugString() const {
|
||||
std::ostringstream oss;
|
||||
|
||||
oss << *this;
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string BlobFileAddition::DebugJSON() const {
|
||||
JSONWriter jw;
|
||||
|
||||
jw << *this;
|
||||
|
||||
jw.EndObject();
|
||||
|
||||
return jw.Get();
|
||||
}
|
||||
|
||||
bool operator==(const BlobFileAddition& lhs, const BlobFileAddition& rhs) {
|
||||
return lhs.GetBlobFileNumber() == rhs.GetBlobFileNumber() &&
|
||||
lhs.GetTotalBlobCount() == rhs.GetTotalBlobCount() &&
|
||||
lhs.GetTotalBlobBytes() == rhs.GetTotalBlobBytes() &&
|
||||
lhs.GetChecksumMethod() == rhs.GetChecksumMethod() &&
|
||||
lhs.GetChecksumValue() == rhs.GetChecksumValue();
|
||||
}
|
||||
|
||||
bool operator!=(const BlobFileAddition& lhs, const BlobFileAddition& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const BlobFileAddition& blob_file_addition) {
|
||||
os << "blob_file_number: " << blob_file_addition.GetBlobFileNumber()
|
||||
<< " total_blob_count: " << blob_file_addition.GetTotalBlobCount()
|
||||
<< " total_blob_bytes: " << blob_file_addition.GetTotalBlobBytes()
|
||||
<< " checksum_method: " << blob_file_addition.GetChecksumMethod()
|
||||
<< " checksum_value: " << blob_file_addition.GetChecksumValue();
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
JSONWriter& operator<<(JSONWriter& jw,
|
||||
const BlobFileAddition& blob_file_addition) {
|
||||
jw << "BlobFileNumber" << blob_file_addition.GetBlobFileNumber()
|
||||
<< "TotalBlobCount" << blob_file_addition.GetTotalBlobCount()
|
||||
<< "TotalBlobBytes" << blob_file_addition.GetTotalBlobBytes()
|
||||
<< "ChecksumMethod" << blob_file_addition.GetChecksumMethod()
|
||||
<< "ChecksumValue" << blob_file_addition.GetChecksumValue();
|
||||
|
||||
return jw;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_constants.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class JSONWriter;
|
||||
class Slice;
|
||||
class Status;
|
||||
|
||||
class BlobFileAddition {
|
||||
public:
|
||||
BlobFileAddition() = default;
|
||||
|
||||
BlobFileAddition(uint64_t blob_file_number, uint64_t total_blob_count,
|
||||
uint64_t total_blob_bytes, std::string checksum_method,
|
||||
std::string checksum_value)
|
||||
: blob_file_number_(blob_file_number),
|
||||
total_blob_count_(total_blob_count),
|
||||
total_blob_bytes_(total_blob_bytes),
|
||||
checksum_method_(std::move(checksum_method)),
|
||||
checksum_value_(std::move(checksum_value)) {
|
||||
assert(checksum_method_.empty() == checksum_value_.empty());
|
||||
}
|
||||
|
||||
uint64_t GetBlobFileNumber() const { return blob_file_number_; }
|
||||
uint64_t GetTotalBlobCount() const { return total_blob_count_; }
|
||||
uint64_t GetTotalBlobBytes() const { return total_blob_bytes_; }
|
||||
const std::string& GetChecksumMethod() const { return checksum_method_; }
|
||||
const std::string& GetChecksumValue() const { return checksum_value_; }
|
||||
|
||||
void EncodeTo(std::string* output) const;
|
||||
Status DecodeFrom(Slice* input);
|
||||
|
||||
std::string DebugString() const;
|
||||
std::string DebugJSON() const;
|
||||
|
||||
private:
|
||||
enum CustomFieldTags : uint32_t;
|
||||
|
||||
uint64_t blob_file_number_ = kInvalidBlobFileNumber;
|
||||
uint64_t total_blob_count_ = 0;
|
||||
uint64_t total_blob_bytes_ = 0;
|
||||
std::string checksum_method_;
|
||||
std::string checksum_value_;
|
||||
};
|
||||
|
||||
bool operator==(const BlobFileAddition& lhs, const BlobFileAddition& rhs);
|
||||
bool operator!=(const BlobFileAddition& lhs, const BlobFileAddition& rhs);
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const BlobFileAddition& blob_file_addition);
|
||||
JSONWriter& operator<<(JSONWriter& jw,
|
||||
const BlobFileAddition& blob_file_addition);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,206 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_file_addition.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class BlobFileAdditionTest : public testing::Test {
|
||||
public:
|
||||
static void TestEncodeDecode(const BlobFileAddition& blob_file_addition) {
|
||||
std::string encoded;
|
||||
blob_file_addition.EncodeTo(&encoded);
|
||||
|
||||
BlobFileAddition decoded;
|
||||
Slice input(encoded);
|
||||
ASSERT_OK(decoded.DecodeFrom(&input));
|
||||
|
||||
ASSERT_EQ(blob_file_addition, decoded);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(BlobFileAdditionTest, Empty) {
|
||||
BlobFileAddition blob_file_addition;
|
||||
|
||||
ASSERT_EQ(blob_file_addition.GetBlobFileNumber(), kInvalidBlobFileNumber);
|
||||
ASSERT_EQ(blob_file_addition.GetTotalBlobCount(), 0);
|
||||
ASSERT_EQ(blob_file_addition.GetTotalBlobBytes(), 0);
|
||||
ASSERT_TRUE(blob_file_addition.GetChecksumMethod().empty());
|
||||
ASSERT_TRUE(blob_file_addition.GetChecksumValue().empty());
|
||||
|
||||
TestEncodeDecode(blob_file_addition);
|
||||
}
|
||||
|
||||
TEST_F(BlobFileAdditionTest, NonEmpty) {
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
constexpr uint64_t total_blob_count = 2;
|
||||
constexpr uint64_t total_blob_bytes = 123456;
|
||||
const std::string checksum_method("SHA1");
|
||||
const std::string checksum_value("bdb7f34a59dfa1592ce7f52e99f98c570c525cbd");
|
||||
|
||||
BlobFileAddition blob_file_addition(blob_file_number, total_blob_count,
|
||||
total_blob_bytes, checksum_method,
|
||||
checksum_value);
|
||||
|
||||
ASSERT_EQ(blob_file_addition.GetBlobFileNumber(), blob_file_number);
|
||||
ASSERT_EQ(blob_file_addition.GetTotalBlobCount(), total_blob_count);
|
||||
ASSERT_EQ(blob_file_addition.GetTotalBlobBytes(), total_blob_bytes);
|
||||
ASSERT_EQ(blob_file_addition.GetChecksumMethod(), checksum_method);
|
||||
ASSERT_EQ(blob_file_addition.GetChecksumValue(), checksum_value);
|
||||
|
||||
TestEncodeDecode(blob_file_addition);
|
||||
}
|
||||
|
||||
TEST_F(BlobFileAdditionTest, DecodeErrors) {
|
||||
std::string str;
|
||||
Slice slice(str);
|
||||
|
||||
BlobFileAddition blob_file_addition;
|
||||
|
||||
{
|
||||
const Status s = blob_file_addition.DecodeFrom(&slice);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "blob file number"));
|
||||
}
|
||||
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
PutVarint64(&str, blob_file_number);
|
||||
slice = str;
|
||||
|
||||
{
|
||||
const Status s = blob_file_addition.DecodeFrom(&slice);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "total blob count"));
|
||||
}
|
||||
|
||||
constexpr uint64_t total_blob_count = 4567;
|
||||
PutVarint64(&str, total_blob_count);
|
||||
slice = str;
|
||||
|
||||
{
|
||||
const Status s = blob_file_addition.DecodeFrom(&slice);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "total blob bytes"));
|
||||
}
|
||||
|
||||
constexpr uint64_t total_blob_bytes = 12345678;
|
||||
PutVarint64(&str, total_blob_bytes);
|
||||
slice = str;
|
||||
|
||||
{
|
||||
const Status s = blob_file_addition.DecodeFrom(&slice);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "checksum method"));
|
||||
}
|
||||
|
||||
constexpr char checksum_method[] = "SHA1";
|
||||
PutLengthPrefixedSlice(&str, checksum_method);
|
||||
slice = str;
|
||||
|
||||
{
|
||||
const Status s = blob_file_addition.DecodeFrom(&slice);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "checksum value"));
|
||||
}
|
||||
|
||||
constexpr char checksum_value[] = "bdb7f34a59dfa1592ce7f52e99f98c570c525cbd";
|
||||
PutLengthPrefixedSlice(&str, checksum_value);
|
||||
slice = str;
|
||||
|
||||
{
|
||||
const Status s = blob_file_addition.DecodeFrom(&slice);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "custom field tag"));
|
||||
}
|
||||
|
||||
constexpr uint32_t custom_tag = 2;
|
||||
PutVarint32(&str, custom_tag);
|
||||
slice = str;
|
||||
|
||||
{
|
||||
const Status s = blob_file_addition.DecodeFrom(&slice);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "custom field value"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BlobFileAdditionTest, ForwardCompatibleCustomField) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileAddition::EncodeTo::CustomFields", [&](void* arg) {
|
||||
std::string* output = static_cast<std::string*>(arg);
|
||||
|
||||
constexpr uint32_t forward_compatible_tag = 2;
|
||||
PutVarint32(output, forward_compatible_tag);
|
||||
|
||||
PutLengthPrefixedSlice(output, "deadbeef");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr uint64_t blob_file_number = 678;
|
||||
constexpr uint64_t total_blob_count = 9999;
|
||||
constexpr uint64_t total_blob_bytes = 100000000;
|
||||
const std::string checksum_method("CRC32");
|
||||
const std::string checksum_value("3d87ff57");
|
||||
|
||||
BlobFileAddition blob_file_addition(blob_file_number, total_blob_count,
|
||||
total_blob_bytes, checksum_method,
|
||||
checksum_value);
|
||||
|
||||
TestEncodeDecode(blob_file_addition);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(BlobFileAdditionTest, ForwardIncompatibleCustomField) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileAddition::EncodeTo::CustomFields", [&](void* arg) {
|
||||
std::string* output = static_cast<std::string*>(arg);
|
||||
|
||||
constexpr uint32_t forward_incompatible_tag = (1 << 6) + 1;
|
||||
PutVarint32(output, forward_incompatible_tag);
|
||||
|
||||
PutLengthPrefixedSlice(output, "foobar");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr uint64_t blob_file_number = 456;
|
||||
constexpr uint64_t total_blob_count = 100;
|
||||
constexpr uint64_t total_blob_bytes = 2000000;
|
||||
const std::string checksum_method("CRC32B");
|
||||
const std::string checksum_value("6dbdf23a");
|
||||
|
||||
BlobFileAddition blob_file_addition(blob_file_number, total_blob_count,
|
||||
total_blob_bytes, checksum_method,
|
||||
checksum_value);
|
||||
|
||||
std::string encoded;
|
||||
blob_file_addition.EncodeTo(&encoded);
|
||||
|
||||
BlobFileAddition decoded_blob_file_addition;
|
||||
Slice input(encoded);
|
||||
const Status s = decoded_blob_file_addition.DecodeFrom(&input);
|
||||
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "Forward incompatible"));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_file_garbage.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "logging/event_logger.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Tags for custom fields. Note that these get persisted in the manifest,
|
||||
// so existing tags should not be modified.
|
||||
enum BlobFileGarbage::CustomFieldTags : uint32_t {
|
||||
kEndMarker,
|
||||
|
||||
// Add forward compatible fields here
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
kForwardIncompatibleMask = 1 << 6,
|
||||
|
||||
// Add forward incompatible fields here
|
||||
};
|
||||
|
||||
void BlobFileGarbage::EncodeTo(std::string* output) const {
|
||||
PutVarint64(output, blob_file_number_);
|
||||
PutVarint64(output, garbage_blob_count_);
|
||||
PutVarint64(output, garbage_blob_bytes_);
|
||||
|
||||
// Encode any custom fields here. The format to use is a Varint32 tag (see
|
||||
// CustomFieldTags above) followed by a length prefixed slice. Unknown custom
|
||||
// fields will be ignored during decoding unless they're in the forward
|
||||
// incompatible range.
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("BlobFileGarbage::EncodeTo::CustomFields", output);
|
||||
|
||||
PutVarint32(output, kEndMarker);
|
||||
}
|
||||
|
||||
Status BlobFileGarbage::DecodeFrom(Slice* input) {
|
||||
constexpr char class_name[] = "BlobFileGarbage";
|
||||
|
||||
if (!GetVarint64(input, &blob_file_number_)) {
|
||||
return Status::Corruption(class_name, "Error decoding blob file number");
|
||||
}
|
||||
|
||||
if (!GetVarint64(input, &garbage_blob_count_)) {
|
||||
return Status::Corruption(class_name, "Error decoding garbage blob count");
|
||||
}
|
||||
|
||||
if (!GetVarint64(input, &garbage_blob_bytes_)) {
|
||||
return Status::Corruption(class_name, "Error decoding garbage blob bytes");
|
||||
}
|
||||
|
||||
while (true) {
|
||||
uint32_t custom_field_tag = 0;
|
||||
if (!GetVarint32(input, &custom_field_tag)) {
|
||||
return Status::Corruption(class_name, "Error decoding custom field tag");
|
||||
}
|
||||
|
||||
if (custom_field_tag == kEndMarker) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (custom_field_tag & kForwardIncompatibleMask) {
|
||||
return Status::Corruption(
|
||||
class_name, "Forward incompatible custom field encountered");
|
||||
}
|
||||
|
||||
Slice custom_field_value;
|
||||
if (!GetLengthPrefixedSlice(input, &custom_field_value)) {
|
||||
return Status::Corruption(class_name,
|
||||
"Error decoding custom field value");
|
||||
}
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::string BlobFileGarbage::DebugString() const {
|
||||
std::ostringstream oss;
|
||||
|
||||
oss << *this;
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string BlobFileGarbage::DebugJSON() const {
|
||||
JSONWriter jw;
|
||||
|
||||
jw << *this;
|
||||
|
||||
jw.EndObject();
|
||||
|
||||
return jw.Get();
|
||||
}
|
||||
|
||||
bool operator==(const BlobFileGarbage& lhs, const BlobFileGarbage& rhs) {
|
||||
return lhs.GetBlobFileNumber() == rhs.GetBlobFileNumber() &&
|
||||
lhs.GetGarbageBlobCount() == rhs.GetGarbageBlobCount() &&
|
||||
lhs.GetGarbageBlobBytes() == rhs.GetGarbageBlobBytes();
|
||||
}
|
||||
|
||||
bool operator!=(const BlobFileGarbage& lhs, const BlobFileGarbage& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const BlobFileGarbage& blob_file_garbage) {
|
||||
os << "blob_file_number: " << blob_file_garbage.GetBlobFileNumber()
|
||||
<< " garbage_blob_count: " << blob_file_garbage.GetGarbageBlobCount()
|
||||
<< " garbage_blob_bytes: " << blob_file_garbage.GetGarbageBlobBytes();
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
JSONWriter& operator<<(JSONWriter& jw,
|
||||
const BlobFileGarbage& blob_file_garbage) {
|
||||
jw << "BlobFileNumber" << blob_file_garbage.GetBlobFileNumber()
|
||||
<< "GarbageBlobCount" << blob_file_garbage.GetGarbageBlobCount()
|
||||
<< "GarbageBlobBytes" << blob_file_garbage.GetGarbageBlobBytes();
|
||||
|
||||
return jw;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_constants.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class JSONWriter;
|
||||
class Slice;
|
||||
class Status;
|
||||
|
||||
class BlobFileGarbage {
|
||||
public:
|
||||
BlobFileGarbage() = default;
|
||||
|
||||
BlobFileGarbage(uint64_t blob_file_number, uint64_t garbage_blob_count,
|
||||
uint64_t garbage_blob_bytes)
|
||||
: blob_file_number_(blob_file_number),
|
||||
garbage_blob_count_(garbage_blob_count),
|
||||
garbage_blob_bytes_(garbage_blob_bytes) {}
|
||||
|
||||
uint64_t GetBlobFileNumber() const { return blob_file_number_; }
|
||||
uint64_t GetGarbageBlobCount() const { return garbage_blob_count_; }
|
||||
uint64_t GetGarbageBlobBytes() const { return garbage_blob_bytes_; }
|
||||
|
||||
void EncodeTo(std::string* output) const;
|
||||
Status DecodeFrom(Slice* input);
|
||||
|
||||
std::string DebugString() const;
|
||||
std::string DebugJSON() const;
|
||||
|
||||
private:
|
||||
enum CustomFieldTags : uint32_t;
|
||||
|
||||
uint64_t blob_file_number_ = kInvalidBlobFileNumber;
|
||||
uint64_t garbage_blob_count_ = 0;
|
||||
uint64_t garbage_blob_bytes_ = 0;
|
||||
};
|
||||
|
||||
bool operator==(const BlobFileGarbage& lhs, const BlobFileGarbage& rhs);
|
||||
bool operator!=(const BlobFileGarbage& lhs, const BlobFileGarbage& rhs);
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const BlobFileGarbage& blob_file_garbage);
|
||||
JSONWriter& operator<<(JSONWriter& jw,
|
||||
const BlobFileGarbage& blob_file_garbage);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,173 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_file_garbage.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class BlobFileGarbageTest : public testing::Test {
|
||||
public:
|
||||
static void TestEncodeDecode(const BlobFileGarbage& blob_file_garbage) {
|
||||
std::string encoded;
|
||||
blob_file_garbage.EncodeTo(&encoded);
|
||||
|
||||
BlobFileGarbage decoded;
|
||||
Slice input(encoded);
|
||||
ASSERT_OK(decoded.DecodeFrom(&input));
|
||||
|
||||
ASSERT_EQ(blob_file_garbage, decoded);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(BlobFileGarbageTest, Empty) {
|
||||
BlobFileGarbage blob_file_garbage;
|
||||
|
||||
ASSERT_EQ(blob_file_garbage.GetBlobFileNumber(), kInvalidBlobFileNumber);
|
||||
ASSERT_EQ(blob_file_garbage.GetGarbageBlobCount(), 0);
|
||||
ASSERT_EQ(blob_file_garbage.GetGarbageBlobBytes(), 0);
|
||||
|
||||
TestEncodeDecode(blob_file_garbage);
|
||||
}
|
||||
|
||||
TEST_F(BlobFileGarbageTest, NonEmpty) {
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
constexpr uint64_t garbage_blob_count = 1;
|
||||
constexpr uint64_t garbage_blob_bytes = 9876;
|
||||
|
||||
BlobFileGarbage blob_file_garbage(blob_file_number, garbage_blob_count,
|
||||
garbage_blob_bytes);
|
||||
|
||||
ASSERT_EQ(blob_file_garbage.GetBlobFileNumber(), blob_file_number);
|
||||
ASSERT_EQ(blob_file_garbage.GetGarbageBlobCount(), garbage_blob_count);
|
||||
ASSERT_EQ(blob_file_garbage.GetGarbageBlobBytes(), garbage_blob_bytes);
|
||||
|
||||
TestEncodeDecode(blob_file_garbage);
|
||||
}
|
||||
|
||||
TEST_F(BlobFileGarbageTest, DecodeErrors) {
|
||||
std::string str;
|
||||
Slice slice(str);
|
||||
|
||||
BlobFileGarbage blob_file_garbage;
|
||||
|
||||
{
|
||||
const Status s = blob_file_garbage.DecodeFrom(&slice);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "blob file number"));
|
||||
}
|
||||
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
PutVarint64(&str, blob_file_number);
|
||||
slice = str;
|
||||
|
||||
{
|
||||
const Status s = blob_file_garbage.DecodeFrom(&slice);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "garbage blob count"));
|
||||
}
|
||||
|
||||
constexpr uint64_t garbage_blob_count = 4567;
|
||||
PutVarint64(&str, garbage_blob_count);
|
||||
slice = str;
|
||||
|
||||
{
|
||||
const Status s = blob_file_garbage.DecodeFrom(&slice);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "garbage blob bytes"));
|
||||
}
|
||||
|
||||
constexpr uint64_t garbage_blob_bytes = 12345678;
|
||||
PutVarint64(&str, garbage_blob_bytes);
|
||||
slice = str;
|
||||
|
||||
{
|
||||
const Status s = blob_file_garbage.DecodeFrom(&slice);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "custom field tag"));
|
||||
}
|
||||
|
||||
constexpr uint32_t custom_tag = 2;
|
||||
PutVarint32(&str, custom_tag);
|
||||
slice = str;
|
||||
|
||||
{
|
||||
const Status s = blob_file_garbage.DecodeFrom(&slice);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "custom field value"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BlobFileGarbageTest, ForwardCompatibleCustomField) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileGarbage::EncodeTo::CustomFields", [&](void* arg) {
|
||||
std::string* output = static_cast<std::string*>(arg);
|
||||
|
||||
constexpr uint32_t forward_compatible_tag = 2;
|
||||
PutVarint32(output, forward_compatible_tag);
|
||||
|
||||
PutLengthPrefixedSlice(output, "deadbeef");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr uint64_t blob_file_number = 678;
|
||||
constexpr uint64_t garbage_blob_count = 9999;
|
||||
constexpr uint64_t garbage_blob_bytes = 100000000;
|
||||
|
||||
BlobFileGarbage blob_file_garbage(blob_file_number, garbage_blob_count,
|
||||
garbage_blob_bytes);
|
||||
|
||||
TestEncodeDecode(blob_file_garbage);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(BlobFileGarbageTest, ForwardIncompatibleCustomField) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileGarbage::EncodeTo::CustomFields", [&](void* arg) {
|
||||
std::string* output = static_cast<std::string*>(arg);
|
||||
|
||||
constexpr uint32_t forward_incompatible_tag = (1 << 6) + 1;
|
||||
PutVarint32(output, forward_incompatible_tag);
|
||||
|
||||
PutLengthPrefixedSlice(output, "foobar");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr uint64_t blob_file_number = 456;
|
||||
constexpr uint64_t garbage_blob_count = 100;
|
||||
constexpr uint64_t garbage_blob_bytes = 2000000;
|
||||
|
||||
BlobFileGarbage blob_file_garbage(blob_file_number, garbage_blob_count,
|
||||
garbage_blob_bytes);
|
||||
|
||||
std::string encoded;
|
||||
blob_file_garbage.EncodeTo(&encoded);
|
||||
|
||||
BlobFileGarbage decoded_blob_file_addition;
|
||||
Slice input(encoded);
|
||||
const Status s = decoded_blob_file_addition.DecodeFrom(&input);
|
||||
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "Forward incompatible"));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_file_meta.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
std::string SharedBlobFileMetaData::DebugString() const {
|
||||
std::ostringstream oss;
|
||||
oss << (*this);
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const SharedBlobFileMetaData& shared_meta) {
|
||||
os << "blob_file_number: " << shared_meta.GetBlobFileNumber()
|
||||
<< " total_blob_count: " << shared_meta.GetTotalBlobCount()
|
||||
<< " total_blob_bytes: " << shared_meta.GetTotalBlobBytes()
|
||||
<< " checksum_method: " << shared_meta.GetChecksumMethod()
|
||||
<< " checksum_value: " << shared_meta.GetChecksumValue();
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string BlobFileMetaData::DebugString() const {
|
||||
std::ostringstream oss;
|
||||
oss << (*this);
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const BlobFileMetaData& meta) {
|
||||
const auto& shared_meta = meta.GetSharedMeta();
|
||||
assert(shared_meta);
|
||||
os << (*shared_meta);
|
||||
|
||||
os << " linked_ssts: {";
|
||||
for (uint64_t file_number : meta.GetLinkedSsts()) {
|
||||
os << ' ' << file_number;
|
||||
}
|
||||
os << " }";
|
||||
|
||||
os << " garbage_blob_count: " << meta.GetGarbageBlobCount()
|
||||
<< " garbage_blob_bytes: " << meta.GetGarbageBlobBytes();
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// SharedBlobFileMetaData represents the immutable part of blob files' metadata,
|
||||
// like the blob file number, total number and size of blobs, or checksum
|
||||
// method and value. There is supposed to be one object of this class per blob
|
||||
// file (shared across all versions that include the blob file in question);
|
||||
// hence, the type is neither copyable nor movable. A blob file can be marked
|
||||
// obsolete when the corresponding SharedBlobFileMetaData object is destroyed.
|
||||
|
||||
class SharedBlobFileMetaData {
|
||||
public:
|
||||
static std::shared_ptr<SharedBlobFileMetaData> Create(
|
||||
uint64_t blob_file_number, uint64_t total_blob_count,
|
||||
uint64_t total_blob_bytes, std::string checksum_method,
|
||||
std::string checksum_value) {
|
||||
return std::shared_ptr<SharedBlobFileMetaData>(new SharedBlobFileMetaData(
|
||||
blob_file_number, total_blob_count, total_blob_bytes,
|
||||
std::move(checksum_method), std::move(checksum_value)));
|
||||
}
|
||||
|
||||
template <typename Deleter>
|
||||
static std::shared_ptr<SharedBlobFileMetaData> Create(
|
||||
uint64_t blob_file_number, uint64_t total_blob_count,
|
||||
uint64_t total_blob_bytes, std::string checksum_method,
|
||||
std::string checksum_value, Deleter deleter) {
|
||||
return std::shared_ptr<SharedBlobFileMetaData>(
|
||||
new SharedBlobFileMetaData(blob_file_number, total_blob_count,
|
||||
total_blob_bytes, std::move(checksum_method),
|
||||
std::move(checksum_value)),
|
||||
deleter);
|
||||
}
|
||||
|
||||
SharedBlobFileMetaData(const SharedBlobFileMetaData&) = delete;
|
||||
SharedBlobFileMetaData& operator=(const SharedBlobFileMetaData&) = delete;
|
||||
|
||||
SharedBlobFileMetaData(SharedBlobFileMetaData&&) = delete;
|
||||
SharedBlobFileMetaData& operator=(SharedBlobFileMetaData&&) = delete;
|
||||
|
||||
uint64_t GetBlobFileNumber() const { return blob_file_number_; }
|
||||
uint64_t GetTotalBlobCount() const { return total_blob_count_; }
|
||||
uint64_t GetTotalBlobBytes() const { return total_blob_bytes_; }
|
||||
const std::string& GetChecksumMethod() const { return checksum_method_; }
|
||||
const std::string& GetChecksumValue() const { return checksum_value_; }
|
||||
|
||||
std::string DebugString() const;
|
||||
|
||||
private:
|
||||
SharedBlobFileMetaData(uint64_t blob_file_number, uint64_t total_blob_count,
|
||||
uint64_t total_blob_bytes, std::string checksum_method,
|
||||
std::string checksum_value)
|
||||
: blob_file_number_(blob_file_number),
|
||||
total_blob_count_(total_blob_count),
|
||||
total_blob_bytes_(total_blob_bytes),
|
||||
checksum_method_(std::move(checksum_method)),
|
||||
checksum_value_(std::move(checksum_value)) {
|
||||
assert(checksum_method_.empty() == checksum_value_.empty());
|
||||
}
|
||||
|
||||
uint64_t blob_file_number_;
|
||||
uint64_t total_blob_count_;
|
||||
uint64_t total_blob_bytes_;
|
||||
std::string checksum_method_;
|
||||
std::string checksum_value_;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const SharedBlobFileMetaData& shared_meta);
|
||||
|
||||
// BlobFileMetaData contains the part of the metadata for blob files that can
|
||||
// vary across versions, like the amount of garbage in the blob file. In
|
||||
// addition, BlobFileMetaData objects point to and share the ownership of the
|
||||
// SharedBlobFileMetaData object for the corresponding blob file. Similarly to
|
||||
// SharedBlobFileMetaData, BlobFileMetaData are not copyable or movable. They
|
||||
// are meant to be jointly owned by the versions in which the blob file has the
|
||||
// same (immutable *and* mutable) state.
|
||||
|
||||
class BlobFileMetaData {
|
||||
public:
|
||||
using LinkedSsts = std::unordered_set<uint64_t>;
|
||||
|
||||
static std::shared_ptr<BlobFileMetaData> Create(
|
||||
std::shared_ptr<SharedBlobFileMetaData> shared_meta,
|
||||
LinkedSsts linked_ssts, uint64_t garbage_blob_count,
|
||||
uint64_t garbage_blob_bytes) {
|
||||
return std::shared_ptr<BlobFileMetaData>(
|
||||
new BlobFileMetaData(std::move(shared_meta), std::move(linked_ssts),
|
||||
garbage_blob_count, garbage_blob_bytes));
|
||||
}
|
||||
|
||||
BlobFileMetaData(const BlobFileMetaData&) = delete;
|
||||
BlobFileMetaData& operator=(const BlobFileMetaData&) = delete;
|
||||
|
||||
BlobFileMetaData(BlobFileMetaData&&) = delete;
|
||||
BlobFileMetaData& operator=(BlobFileMetaData&&) = delete;
|
||||
|
||||
const std::shared_ptr<SharedBlobFileMetaData>& GetSharedMeta() const {
|
||||
return shared_meta_;
|
||||
}
|
||||
|
||||
uint64_t GetBlobFileNumber() const {
|
||||
assert(shared_meta_);
|
||||
return shared_meta_->GetBlobFileNumber();
|
||||
}
|
||||
uint64_t GetTotalBlobCount() const {
|
||||
assert(shared_meta_);
|
||||
return shared_meta_->GetTotalBlobCount();
|
||||
}
|
||||
uint64_t GetTotalBlobBytes() const {
|
||||
assert(shared_meta_);
|
||||
return shared_meta_->GetTotalBlobBytes();
|
||||
}
|
||||
const std::string& GetChecksumMethod() const {
|
||||
assert(shared_meta_);
|
||||
return shared_meta_->GetChecksumMethod();
|
||||
}
|
||||
const std::string& GetChecksumValue() const {
|
||||
assert(shared_meta_);
|
||||
return shared_meta_->GetChecksumValue();
|
||||
}
|
||||
|
||||
const LinkedSsts& GetLinkedSsts() const { return linked_ssts_; }
|
||||
|
||||
uint64_t GetGarbageBlobCount() const { return garbage_blob_count_; }
|
||||
uint64_t GetGarbageBlobBytes() const { return garbage_blob_bytes_; }
|
||||
|
||||
std::string DebugString() const;
|
||||
|
||||
private:
|
||||
BlobFileMetaData(std::shared_ptr<SharedBlobFileMetaData> shared_meta,
|
||||
LinkedSsts linked_ssts, uint64_t garbage_blob_count,
|
||||
uint64_t garbage_blob_bytes)
|
||||
: shared_meta_(std::move(shared_meta)),
|
||||
linked_ssts_(std::move(linked_ssts)),
|
||||
garbage_blob_count_(garbage_blob_count),
|
||||
garbage_blob_bytes_(garbage_blob_bytes) {
|
||||
assert(shared_meta_);
|
||||
assert(garbage_blob_count_ <= shared_meta_->GetTotalBlobCount());
|
||||
assert(garbage_blob_bytes_ <= shared_meta_->GetTotalBlobBytes());
|
||||
}
|
||||
|
||||
std::shared_ptr<SharedBlobFileMetaData> shared_meta_;
|
||||
LinkedSsts linked_ssts_;
|
||||
uint64_t garbage_blob_count_;
|
||||
uint64_t garbage_blob_bytes_;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const BlobFileMetaData& meta);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -5,7 +5,7 @@
|
||||
//
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "utilities/blob_db/blob_log_format.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
|
||||
#include "util/coding.h"
|
||||
#include "util/crc32c.h"
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
@@ -23,7 +22,6 @@ namespace blob_db {
|
||||
|
||||
constexpr uint32_t kMagicNumber = 2395959; // 0x00248f37
|
||||
constexpr uint32_t kVersion1 = 1;
|
||||
constexpr uint64_t kNoExpiration = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
using ExpirationRange = std::pair<uint64_t, uint64_t>;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "utilities/blob_db/blob_log_reader.h"
|
||||
#include "db/blob/blob_log_reader.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -26,7 +26,8 @@ Reader::Reader(std::unique_ptr<RandomAccessFileReader>&& file_reader, Env* env,
|
||||
|
||||
Status Reader::ReadSlice(uint64_t size, Slice* slice, char* buf) {
|
||||
StopWatch read_sw(env_, statistics_, BLOB_DB_BLOB_FILE_READ_MICROS);
|
||||
Status s = file_->Read(next_byte_, static_cast<size_t>(size), slice, buf);
|
||||
Status s = file_->Read(IOOptions(), next_byte_, static_cast<size_t>(size),
|
||||
slice, buf, nullptr);
|
||||
next_byte_ += size;
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -10,12 +10,12 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "file/random_access_file_reader.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "utilities/blob_db/blob_log_format.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "utilities/blob_db/blob_log_writer.h"
|
||||
#include "db/blob/blob_log_writer.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "utilities/blob_db/blob_log_format.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace blob_db {
|
||||
@@ -10,12 +10,12 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "utilities/blob_db/blob_log_format.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
+33
-18
@@ -51,7 +51,8 @@ TableBuilder* NewTableBuilder(
|
||||
uint64_t sample_for_compression, const CompressionOptions& compression_opts,
|
||||
int level, const bool skip_filters, const uint64_t creation_time,
|
||||
const uint64_t oldest_key_time, const uint64_t target_file_size,
|
||||
const uint64_t file_creation_time) {
|
||||
const uint64_t file_creation_time, const std::string& db_id,
|
||||
const std::string& db_session_id) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
@@ -61,7 +62,7 @@ TableBuilder* NewTableBuilder(
|
||||
sample_for_compression, compression_opts,
|
||||
skip_filters, column_family_name, level,
|
||||
creation_time, oldest_key_time, target_file_size,
|
||||
file_creation_time),
|
||||
file_creation_time, db_id, db_session_id),
|
||||
column_family_id, file);
|
||||
}
|
||||
|
||||
@@ -81,10 +82,12 @@ Status BuildTable(
|
||||
SnapshotChecker* snapshot_checker, const CompressionType compression,
|
||||
uint64_t sample_for_compression, const CompressionOptions& compression_opts,
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
TableFileCreationReason reason, EventLogger* event_logger, int job_id,
|
||||
const Env::IOPriority io_priority, TableProperties* table_properties,
|
||||
int level, const uint64_t creation_time, const uint64_t oldest_key_time,
|
||||
Env::WriteLifeTimeHint write_hint, const uint64_t file_creation_time) {
|
||||
TableFileCreationReason reason, IOStatus* io_status,
|
||||
EventLogger* event_logger, int job_id, const Env::IOPriority io_priority,
|
||||
TableProperties* table_properties, int level, const uint64_t creation_time,
|
||||
const uint64_t oldest_key_time, Env::WriteLifeTimeHint write_hint,
|
||||
const uint64_t file_creation_time, const std::string& db_id,
|
||||
const std::string& db_session_id) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
@@ -133,7 +136,7 @@ Status BuildTable(
|
||||
|
||||
file_writer.reset(new WritableFileWriter(
|
||||
std::move(file), fname, file_options, env, ioptions.statistics,
|
||||
ioptions.listeners, ioptions.sst_file_checksum_func));
|
||||
ioptions.listeners, ioptions.file_checksum_gen_factory));
|
||||
|
||||
builder = NewTableBuilder(
|
||||
ioptions, mutable_cf_options, internal_comparator,
|
||||
@@ -141,7 +144,7 @@ Status BuildTable(
|
||||
column_family_name, file_writer.get(), compression,
|
||||
sample_for_compression, compression_opts_for_flush, level,
|
||||
false /* skip_filters */, creation_time, oldest_key_time,
|
||||
0 /*target_file_size*/, file_creation_time);
|
||||
0 /*target_file_size*/, file_creation_time, db_id, db_session_id);
|
||||
}
|
||||
|
||||
MergeHelper merge(env, internal_comparator.user_comparator(),
|
||||
@@ -182,14 +185,15 @@ Status BuildTable(
|
||||
}
|
||||
|
||||
// Finish and check for builder errors
|
||||
tp = builder->GetTableProperties();
|
||||
bool empty = builder->NumEntries() == 0 && tp.num_range_deletions == 0;
|
||||
bool empty = builder->IsEmpty();
|
||||
s = c_iter.status();
|
||||
TEST_SYNC_POINT("BuildTable:BeforeFinishBuildTable");
|
||||
if (!s.ok() || empty) {
|
||||
builder->Abandon();
|
||||
} else {
|
||||
s = builder->Finish();
|
||||
}
|
||||
*io_status = builder->io_status();
|
||||
|
||||
if (s.ok() && !empty) {
|
||||
uint64_t file_size = builder->FileSize();
|
||||
@@ -200,20 +204,28 @@ Status BuildTable(
|
||||
if (table_properties) {
|
||||
*table_properties = tp;
|
||||
}
|
||||
// Add the checksum information to file metadata.
|
||||
meta->file_checksum = builder->GetFileChecksum();
|
||||
meta->file_checksum_func_name = builder->GetFileChecksumFuncName();
|
||||
}
|
||||
delete builder;
|
||||
|
||||
// Finish and check for file errors
|
||||
if (s.ok() && !empty) {
|
||||
StopWatch sw(env, ioptions.statistics, TABLE_SYNC_MICROS);
|
||||
s = file_writer->Sync(ioptions.use_fsync);
|
||||
*io_status = file_writer->Sync(ioptions.use_fsync);
|
||||
}
|
||||
if (s.ok() && !empty) {
|
||||
s = file_writer->Close();
|
||||
if (io_status->ok() && !empty) {
|
||||
*io_status = file_writer->Close();
|
||||
}
|
||||
if (io_status->ok() && !empty) {
|
||||
// Add the checksum information to file metadata.
|
||||
meta->file_checksum = file_writer->GetFileChecksum();
|
||||
meta->file_checksum_func_name = file_writer->GetFileChecksumFuncName();
|
||||
}
|
||||
|
||||
if (!io_status->ok()) {
|
||||
s = *io_status;
|
||||
}
|
||||
|
||||
// TODO Also check the IO status when create the Iterator.
|
||||
|
||||
if (s.ok() && !empty) {
|
||||
// Verify that the table is usable
|
||||
@@ -229,8 +241,11 @@ Status BuildTable(
|
||||
(internal_stats == nullptr) ? nullptr
|
||||
: internal_stats->GetFileReadHist(0),
|
||||
TableReaderCaller::kFlush, /*arena=*/nullptr,
|
||||
/*skip_filter=*/false, level, /*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key*/ nullptr));
|
||||
/*skip_filter=*/false, level,
|
||||
MaxFileSizeForL0MetaPin(mutable_cf_options),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key*/ nullptr,
|
||||
/*allow_unprepared_value*/ false));
|
||||
s = it->status();
|
||||
if (s.ok() && paranoid_file_checks) {
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
|
||||
+5
-3
@@ -51,7 +51,8 @@ TableBuilder* NewTableBuilder(
|
||||
const CompressionOptions& compression_opts, int level,
|
||||
const bool skip_filters = false, const uint64_t creation_time = 0,
|
||||
const uint64_t oldest_key_time = 0, const uint64_t target_file_size = 0,
|
||||
const uint64_t file_creation_time = 0);
|
||||
const uint64_t file_creation_time = 0, const std::string& db_id = "",
|
||||
const std::string& db_session_id = "");
|
||||
|
||||
// Build a Table file from the contents of *iter. The generated file
|
||||
// will be named according to number specified in meta. On success, the rest of
|
||||
@@ -78,11 +79,12 @@ extern Status BuildTable(
|
||||
const uint64_t sample_for_compression,
|
||||
const CompressionOptions& compression_opts, bool paranoid_file_checks,
|
||||
InternalStats* internal_stats, TableFileCreationReason reason,
|
||||
EventLogger* event_logger = nullptr, int job_id = 0,
|
||||
IOStatus* io_status, EventLogger* event_logger = nullptr, int job_id = 0,
|
||||
const Env::IOPriority io_priority = Env::IO_HIGH,
|
||||
TableProperties* table_properties = nullptr, int level = -1,
|
||||
const uint64_t creation_time = 0, const uint64_t oldest_key_time = 0,
|
||||
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET,
|
||||
const uint64_t file_creation_time = 0);
|
||||
const uint64_t file_creation_time = 0, const std::string& db_id = "",
|
||||
const std::string& db_session_id = "");
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -1466,6 +1466,11 @@ void rocksdb_writebatch_delete(
|
||||
b->rep.Delete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_singledelete(rocksdb_writebatch_t* b, const char* key,
|
||||
size_t klen) {
|
||||
b->rep.SingleDelete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_delete_cf(
|
||||
rocksdb_writebatch_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
@@ -1473,6 +1478,12 @@ void rocksdb_writebatch_delete_cf(
|
||||
b->rep.Delete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_singledelete_cf(
|
||||
rocksdb_writebatch_t* b, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen) {
|
||||
b->rep.SingleDelete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_deletev(
|
||||
rocksdb_writebatch_t* b,
|
||||
int num_keys, const char* const* keys_list,
|
||||
@@ -1723,6 +1734,11 @@ void rocksdb_writebatch_wi_delete(
|
||||
b->rep->Delete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_singledelete(rocksdb_writebatch_wi_t* b,
|
||||
const char* key, size_t klen) {
|
||||
b->rep->SingleDelete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_delete_cf(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
@@ -1730,6 +1746,12 @@ void rocksdb_writebatch_wi_delete_cf(
|
||||
b->rep->Delete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_singledelete_cf(
|
||||
rocksdb_writebatch_wi_t* b, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen) {
|
||||
b->rep->SingleDelete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_deletev(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
int num_keys, const char* const* keys_list,
|
||||
@@ -2154,6 +2176,10 @@ void rocksdb_options_destroy(rocksdb_options_t* options) {
|
||||
delete options;
|
||||
}
|
||||
|
||||
rocksdb_options_t* rocksdb_options_create_copy(rocksdb_options_t* options) {
|
||||
return new rocksdb_options_t(*options);
|
||||
}
|
||||
|
||||
void rocksdb_options_increase_parallelism(
|
||||
rocksdb_options_t* opt, int total_threads) {
|
||||
opt->rep.IncreaseParallelism(total_threads);
|
||||
@@ -2179,6 +2205,10 @@ void rocksdb_options_set_allow_ingest_behind(
|
||||
opt->rep.allow_ingest_behind = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_allow_ingest_behind(rocksdb_options_t* opt) {
|
||||
return opt->rep.allow_ingest_behind;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compaction_filter(
|
||||
rocksdb_options_t* opt,
|
||||
rocksdb_compactionfilter_t* filter) {
|
||||
@@ -2196,6 +2226,10 @@ void rocksdb_options_compaction_readahead_size(
|
||||
opt->rep.compaction_readahead_size = s;
|
||||
}
|
||||
|
||||
size_t rocksdb_options_get_compaction_readahead_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.compaction_readahead_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_comparator(
|
||||
rocksdb_options_t* opt,
|
||||
rocksdb_comparator_t* cmp) {
|
||||
@@ -2208,27 +2242,43 @@ void rocksdb_options_set_merge_operator(
|
||||
opt->rep.merge_operator = std::shared_ptr<MergeOperator>(merge_operator);
|
||||
}
|
||||
|
||||
|
||||
void rocksdb_options_set_create_if_missing(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.create_if_missing = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_create_if_missing(rocksdb_options_t* opt) {
|
||||
return opt->rep.create_if_missing;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_create_missing_column_families(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.create_missing_column_families = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_create_missing_column_families(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.create_missing_column_families;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_error_if_exists(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.error_if_exists = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_error_if_exists(rocksdb_options_t* opt) {
|
||||
return opt->rep.error_if_exists;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_paranoid_checks(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.paranoid_checks = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_paranoid_checks(rocksdb_options_t* opt) {
|
||||
return opt->rep.paranoid_checks;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_db_paths(rocksdb_options_t* opt,
|
||||
const rocksdb_dbpath_t** dbpath_values,
|
||||
size_t num_paths) {
|
||||
@@ -2254,52 +2304,98 @@ void rocksdb_options_set_info_log_level(
|
||||
opt->rep.info_log_level = static_cast<InfoLogLevel>(v);
|
||||
}
|
||||
|
||||
int rocksdb_options_get_info_log_level(rocksdb_options_t* opt) {
|
||||
return static_cast<int>(opt->rep.info_log_level);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_db_write_buffer_size(rocksdb_options_t* opt,
|
||||
size_t s) {
|
||||
opt->rep.db_write_buffer_size = s;
|
||||
}
|
||||
|
||||
size_t rocksdb_options_get_db_write_buffer_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.db_write_buffer_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_write_buffer_size(rocksdb_options_t* opt, size_t s) {
|
||||
opt->rep.write_buffer_size = s;
|
||||
}
|
||||
|
||||
size_t rocksdb_options_get_write_buffer_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.write_buffer_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_open_files(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_open_files = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_max_open_files(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_open_files;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_file_opening_threads(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_file_opening_threads = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_max_file_opening_threads(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_file_opening_threads;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_total_wal_size(rocksdb_options_t* opt, uint64_t n) {
|
||||
opt->rep.max_total_wal_size = n;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_max_total_wal_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_total_wal_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_target_file_size_base(
|
||||
rocksdb_options_t* opt, uint64_t n) {
|
||||
opt->rep.target_file_size_base = n;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_target_file_size_base(rocksdb_options_t* opt) {
|
||||
return opt->rep.target_file_size_base;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_target_file_size_multiplier(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.target_file_size_multiplier = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_target_file_size_multiplier(rocksdb_options_t* opt) {
|
||||
return opt->rep.target_file_size_multiplier;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_bytes_for_level_base(
|
||||
rocksdb_options_t* opt, uint64_t n) {
|
||||
opt->rep.max_bytes_for_level_base = n;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_max_bytes_for_level_base(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_bytes_for_level_base;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level_compaction_dynamic_level_bytes(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.level_compaction_dynamic_level_bytes = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_level_compaction_dynamic_level_bytes(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.level_compaction_dynamic_level_bytes;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_bytes_for_level_multiplier(rocksdb_options_t* opt,
|
||||
double n) {
|
||||
opt->rep.max_bytes_for_level_multiplier = n;
|
||||
}
|
||||
|
||||
double rocksdb_options_get_max_bytes_for_level_multiplier(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.max_bytes_for_level_multiplier;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_compaction_bytes(rocksdb_options_t* opt,
|
||||
uint64_t n) {
|
||||
opt->rep.max_compaction_bytes = n;
|
||||
@@ -2322,30 +2418,57 @@ void rocksdb_options_set_skip_stats_update_on_db_open(rocksdb_options_t* opt,
|
||||
opt->rep.skip_stats_update_on_db_open = val;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_skip_stats_update_on_db_open(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.skip_stats_update_on_db_open;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(
|
||||
rocksdb_options_t* opt, unsigned char val) {
|
||||
opt->rep.skip_checking_sst_file_sizes_on_db_open = val;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.skip_checking_sst_file_sizes_on_db_open;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_num_levels(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.num_levels = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_num_levels(rocksdb_options_t* opt) {
|
||||
return opt->rep.num_levels;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level0_file_num_compaction_trigger(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.level0_file_num_compaction_trigger = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_level0_file_num_compaction_trigger(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.level0_file_num_compaction_trigger;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level0_slowdown_writes_trigger(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.level0_slowdown_writes_trigger = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_level0_slowdown_writes_trigger(rocksdb_options_t* opt) {
|
||||
return opt->rep.level0_slowdown_writes_trigger;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level0_stop_writes_trigger(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.level0_stop_writes_trigger = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_level0_stop_writes_trigger(rocksdb_options_t* opt) {
|
||||
return opt->rep.level0_stop_writes_trigger;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_mem_compaction_level(rocksdb_options_t* /*opt*/,
|
||||
int /*n*/) {}
|
||||
|
||||
@@ -2357,6 +2480,10 @@ void rocksdb_options_set_compression(rocksdb_options_t* opt, int t) {
|
||||
opt->rep.compression = static_cast<CompressionType>(t);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_bottommost_compression(rocksdb_options_t* opt, int t) {
|
||||
opt->rep.bottommost_compression = static_cast<CompressionType>(t);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_per_level(rocksdb_options_t* opt,
|
||||
int* level_values,
|
||||
size_t num_levels) {
|
||||
@@ -2371,7 +2498,7 @@ void rocksdb_options_set_bottommost_compression_options(rocksdb_options_t* opt,
|
||||
int w_bits, int level,
|
||||
int strategy,
|
||||
int max_dict_bytes,
|
||||
bool enabled) {
|
||||
unsigned char enabled) {
|
||||
opt->rep.bottommost_compression_opts.window_bits = w_bits;
|
||||
opt->rep.bottommost_compression_opts.level = level;
|
||||
opt->rep.bottommost_compression_opts.strategy = strategy;
|
||||
@@ -2379,6 +2506,13 @@ void rocksdb_options_set_bottommost_compression_options(rocksdb_options_t* opt,
|
||||
opt->rep.bottommost_compression_opts.enabled = enabled;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_bottommost_compression_options_zstd_max_train_bytes(
|
||||
rocksdb_options_t* opt, int zstd_max_train_bytes, unsigned char enabled) {
|
||||
opt->rep.bottommost_compression_opts.zstd_max_train_bytes =
|
||||
zstd_max_train_bytes;
|
||||
opt->rep.bottommost_compression_opts.enabled = enabled;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_options(rocksdb_options_t* opt, int w_bits,
|
||||
int level, int strategy,
|
||||
int max_dict_bytes) {
|
||||
@@ -2388,6 +2522,11 @@ void rocksdb_options_set_compression_options(rocksdb_options_t* opt, int w_bits,
|
||||
opt->rep.compression_opts.max_dict_bytes = max_dict_bytes;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_prefix_extractor(
|
||||
rocksdb_options_t* opt, rocksdb_slicetransform_t* prefix_extractor) {
|
||||
opt->rep.prefix_extractor.reset(prefix_extractor);
|
||||
@@ -2527,35 +2666,67 @@ void rocksdb_options_set_max_write_buffer_number(rocksdb_options_t* opt, int n)
|
||||
opt->rep.max_write_buffer_number = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_max_write_buffer_number(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_write_buffer_number;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_min_write_buffer_number_to_merge(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.min_write_buffer_number_to_merge = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_min_write_buffer_number_to_merge(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.min_write_buffer_number_to_merge;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_write_buffer_number_to_maintain(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_write_buffer_number_to_maintain = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_max_write_buffer_number_to_maintain(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.max_write_buffer_number_to_maintain;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_write_buffer_size_to_maintain(
|
||||
rocksdb_options_t* opt, int64_t n) {
|
||||
opt->rep.max_write_buffer_size_to_maintain = n;
|
||||
}
|
||||
|
||||
int64_t rocksdb_options_get_max_write_buffer_size_to_maintain(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.max_write_buffer_size_to_maintain;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_enable_pipelined_write(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.enable_pipelined_write = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_enable_pipelined_write(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.enable_pipelined_write;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_unordered_write(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.unordered_write = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_unordered_write(rocksdb_options_t* opt) {
|
||||
return opt->rep.unordered_write;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_subcompactions(rocksdb_options_t* opt,
|
||||
uint32_t n) {
|
||||
opt->rep.max_subcompactions = n;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_options_get_max_subcompactions(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_subcompactions;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_background_jobs(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_background_jobs = n;
|
||||
}
|
||||
@@ -4441,11 +4612,25 @@ uint64_t rocksdb_approximate_memory_usage_get_cache_total(
|
||||
return memory_usage->cache_total;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_dump_malloc_stats(rocksdb_options_t* opt,
|
||||
unsigned char val) {
|
||||
opt->rep.dump_malloc_stats = val;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_memtable_whole_key_filtering(rocksdb_options_t* opt,
|
||||
unsigned char val) {
|
||||
opt->rep.memtable_whole_key_filtering = val;
|
||||
}
|
||||
|
||||
// deletes container with memory usage estimates
|
||||
void rocksdb_approximate_memory_usage_destroy(rocksdb_memory_usage_t* usage) {
|
||||
delete usage;
|
||||
}
|
||||
|
||||
void rocksdb_cancel_all_background_work(rocksdb_t* db, unsigned char wait) {
|
||||
CancelAllBackgroundWork(db->rep, wait);
|
||||
}
|
||||
|
||||
} // end extern "C"
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
+291
@@ -1461,6 +1461,294 @@ int main(int argc, char** argv) {
|
||||
rocksdb_cuckoo_options_destroy(cuckoo_options);
|
||||
}
|
||||
|
||||
StartPhase("options");
|
||||
{
|
||||
rocksdb_options_t* o;
|
||||
o = rocksdb_options_create();
|
||||
|
||||
// Set and check options.
|
||||
rocksdb_options_set_allow_ingest_behind(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_allow_ingest_behind(o));
|
||||
|
||||
rocksdb_options_compaction_readahead_size(o, 10);
|
||||
CheckCondition(10 == rocksdb_options_get_compaction_readahead_size(o));
|
||||
|
||||
rocksdb_options_set_create_if_missing(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_create_if_missing(o));
|
||||
|
||||
rocksdb_options_set_create_missing_column_families(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_create_missing_column_families(o));
|
||||
|
||||
rocksdb_options_set_error_if_exists(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_error_if_exists(o));
|
||||
|
||||
rocksdb_options_set_paranoid_checks(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_paranoid_checks(o));
|
||||
|
||||
rocksdb_options_set_info_log_level(o, 3);
|
||||
CheckCondition(3 == rocksdb_options_get_info_log_level(o));
|
||||
|
||||
rocksdb_options_set_write_buffer_size(o, 100);
|
||||
CheckCondition(100 == rocksdb_options_get_write_buffer_size(o));
|
||||
|
||||
rocksdb_options_set_db_write_buffer_size(o, 1000);
|
||||
CheckCondition(1000 == rocksdb_options_get_db_write_buffer_size(o));
|
||||
|
||||
rocksdb_options_set_max_open_files(o, 21);
|
||||
CheckCondition(21 == rocksdb_options_get_max_open_files(o));
|
||||
|
||||
rocksdb_options_set_max_file_opening_threads(o, 5);
|
||||
CheckCondition(5 == rocksdb_options_get_max_file_opening_threads(o));
|
||||
|
||||
rocksdb_options_set_max_total_wal_size(o, 400);
|
||||
CheckCondition(400 == rocksdb_options_get_max_total_wal_size(o));
|
||||
|
||||
rocksdb_options_set_num_levels(o, 7);
|
||||
CheckCondition(7 == rocksdb_options_get_num_levels(o));
|
||||
|
||||
rocksdb_options_set_level0_file_num_compaction_trigger(o, 4);
|
||||
CheckCondition(4 ==
|
||||
rocksdb_options_get_level0_file_num_compaction_trigger(o));
|
||||
|
||||
rocksdb_options_set_level0_slowdown_writes_trigger(o, 6);
|
||||
CheckCondition(6 == rocksdb_options_get_level0_slowdown_writes_trigger(o));
|
||||
|
||||
rocksdb_options_set_level0_stop_writes_trigger(o, 8);
|
||||
CheckCondition(8 == rocksdb_options_get_level0_stop_writes_trigger(o));
|
||||
|
||||
rocksdb_options_set_target_file_size_base(o, 256);
|
||||
CheckCondition(256 == rocksdb_options_get_target_file_size_base(o));
|
||||
|
||||
rocksdb_options_set_target_file_size_multiplier(o, 3);
|
||||
CheckCondition(3 == rocksdb_options_get_target_file_size_multiplier(o));
|
||||
|
||||
rocksdb_options_set_max_bytes_for_level_base(o, 1024);
|
||||
CheckCondition(1024 == rocksdb_options_get_max_bytes_for_level_base(o));
|
||||
|
||||
rocksdb_options_set_level_compaction_dynamic_level_bytes(o, 1);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_options_get_level_compaction_dynamic_level_bytes(o));
|
||||
|
||||
rocksdb_options_set_max_bytes_for_level_multiplier(o, 2.0);
|
||||
CheckCondition(2.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(o));
|
||||
|
||||
rocksdb_options_set_skip_stats_update_on_db_open(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(o, 1);
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number(o, 97);
|
||||
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(o));
|
||||
|
||||
rocksdb_options_set_min_write_buffer_number_to_merge(o, 23);
|
||||
CheckCondition(23 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number_to_maintain(o, 64);
|
||||
CheckCondition(64 ==
|
||||
rocksdb_options_get_max_write_buffer_number_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_size_to_maintain(o, 50000);
|
||||
CheckCondition(50000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_enable_pipelined_write(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_enable_pipelined_write(o));
|
||||
|
||||
rocksdb_options_set_unordered_write(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_unordered_write(o));
|
||||
|
||||
rocksdb_options_set_max_subcompactions(o, 123456);
|
||||
CheckCondition(123456 == rocksdb_options_get_max_subcompactions(o));
|
||||
|
||||
// Create a copy that should be equal to the original.
|
||||
rocksdb_options_t* copy;
|
||||
copy = rocksdb_options_create_copy(o);
|
||||
|
||||
CheckCondition(1 == rocksdb_options_get_allow_ingest_behind(copy));
|
||||
CheckCondition(10 == rocksdb_options_get_compaction_readahead_size(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_create_if_missing(copy));
|
||||
CheckCondition(1 ==
|
||||
rocksdb_options_get_create_missing_column_families(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_error_if_exists(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_paranoid_checks(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_info_log_level(copy));
|
||||
CheckCondition(100 == rocksdb_options_get_write_buffer_size(copy));
|
||||
CheckCondition(1000 == rocksdb_options_get_db_write_buffer_size(copy));
|
||||
CheckCondition(21 == rocksdb_options_get_max_open_files(copy));
|
||||
CheckCondition(5 == rocksdb_options_get_max_file_opening_threads(copy));
|
||||
CheckCondition(400 == rocksdb_options_get_max_total_wal_size(copy));
|
||||
CheckCondition(7 == rocksdb_options_get_num_levels(copy));
|
||||
CheckCondition(
|
||||
4 == rocksdb_options_get_level0_file_num_compaction_trigger(copy));
|
||||
CheckCondition(6 ==
|
||||
rocksdb_options_get_level0_slowdown_writes_trigger(copy));
|
||||
CheckCondition(8 == rocksdb_options_get_level0_stop_writes_trigger(copy));
|
||||
CheckCondition(256 == rocksdb_options_get_target_file_size_base(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_target_file_size_multiplier(copy));
|
||||
CheckCondition(1024 == rocksdb_options_get_max_bytes_for_level_base(copy));
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_level_compaction_dynamic_level_bytes(copy));
|
||||
CheckCondition(2.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(copy));
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(copy));
|
||||
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(copy));
|
||||
CheckCondition(23 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(copy));
|
||||
CheckCondition(
|
||||
64 == rocksdb_options_get_max_write_buffer_number_to_maintain(copy));
|
||||
CheckCondition(50000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_enable_pipelined_write(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_unordered_write(copy));
|
||||
CheckCondition(123456 == rocksdb_options_get_max_subcompactions(copy));
|
||||
|
||||
// Copies should be independent.
|
||||
rocksdb_options_set_allow_ingest_behind(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_allow_ingest_behind(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_allow_ingest_behind(o));
|
||||
|
||||
rocksdb_options_compaction_readahead_size(copy, 20);
|
||||
CheckCondition(20 == rocksdb_options_get_compaction_readahead_size(copy));
|
||||
CheckCondition(10 == rocksdb_options_get_compaction_readahead_size(o));
|
||||
|
||||
rocksdb_options_set_create_if_missing(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_create_if_missing(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_create_if_missing(o));
|
||||
|
||||
rocksdb_options_set_create_missing_column_families(copy, 0);
|
||||
CheckCondition(0 ==
|
||||
rocksdb_options_get_create_missing_column_families(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_create_missing_column_families(o));
|
||||
|
||||
rocksdb_options_set_error_if_exists(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_error_if_exists(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_error_if_exists(o));
|
||||
|
||||
rocksdb_options_set_paranoid_checks(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_paranoid_checks(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_paranoid_checks(o));
|
||||
|
||||
rocksdb_options_set_info_log_level(copy, 2);
|
||||
CheckCondition(2 == rocksdb_options_get_info_log_level(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_info_log_level(o));
|
||||
|
||||
rocksdb_options_set_write_buffer_size(copy, 200);
|
||||
CheckCondition(200 == rocksdb_options_get_write_buffer_size(copy));
|
||||
CheckCondition(100 == rocksdb_options_get_write_buffer_size(o));
|
||||
|
||||
rocksdb_options_set_db_write_buffer_size(copy, 2000);
|
||||
CheckCondition(2000 == rocksdb_options_get_db_write_buffer_size(copy));
|
||||
CheckCondition(1000 == rocksdb_options_get_db_write_buffer_size(o));
|
||||
|
||||
rocksdb_options_set_max_open_files(copy, 42);
|
||||
CheckCondition(42 == rocksdb_options_get_max_open_files(copy));
|
||||
CheckCondition(21 == rocksdb_options_get_max_open_files(o));
|
||||
|
||||
rocksdb_options_set_max_file_opening_threads(copy, 3);
|
||||
CheckCondition(3 == rocksdb_options_get_max_file_opening_threads(copy));
|
||||
CheckCondition(5 == rocksdb_options_get_max_file_opening_threads(o));
|
||||
|
||||
rocksdb_options_set_max_total_wal_size(copy, 4000);
|
||||
CheckCondition(4000 == rocksdb_options_get_max_total_wal_size(copy));
|
||||
CheckCondition(400 == rocksdb_options_get_max_total_wal_size(o));
|
||||
|
||||
rocksdb_options_set_num_levels(copy, 6);
|
||||
CheckCondition(6 == rocksdb_options_get_num_levels(copy));
|
||||
CheckCondition(7 == rocksdb_options_get_num_levels(o));
|
||||
|
||||
rocksdb_options_set_level0_file_num_compaction_trigger(copy, 14);
|
||||
CheckCondition(
|
||||
14 == rocksdb_options_get_level0_file_num_compaction_trigger(copy));
|
||||
CheckCondition(4 ==
|
||||
rocksdb_options_get_level0_file_num_compaction_trigger(o));
|
||||
|
||||
rocksdb_options_set_level0_slowdown_writes_trigger(copy, 61);
|
||||
CheckCondition(61 ==
|
||||
rocksdb_options_get_level0_slowdown_writes_trigger(copy));
|
||||
CheckCondition(6 == rocksdb_options_get_level0_slowdown_writes_trigger(o));
|
||||
|
||||
rocksdb_options_set_level0_stop_writes_trigger(copy, 17);
|
||||
CheckCondition(17 == rocksdb_options_get_level0_stop_writes_trigger(copy));
|
||||
CheckCondition(8 == rocksdb_options_get_level0_stop_writes_trigger(o));
|
||||
|
||||
rocksdb_options_set_target_file_size_base(copy, 128);
|
||||
CheckCondition(128 == rocksdb_options_get_target_file_size_base(copy));
|
||||
CheckCondition(256 == rocksdb_options_get_target_file_size_base(o));
|
||||
|
||||
rocksdb_options_set_target_file_size_multiplier(copy, 13);
|
||||
CheckCondition(13 == rocksdb_options_get_target_file_size_multiplier(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_target_file_size_multiplier(o));
|
||||
|
||||
rocksdb_options_set_max_bytes_for_level_base(copy, 900);
|
||||
CheckCondition(900 == rocksdb_options_get_max_bytes_for_level_base(copy));
|
||||
CheckCondition(1024 == rocksdb_options_get_max_bytes_for_level_base(o));
|
||||
|
||||
rocksdb_options_set_level_compaction_dynamic_level_bytes(copy, 0);
|
||||
CheckCondition(
|
||||
0 == rocksdb_options_get_level_compaction_dynamic_level_bytes(copy));
|
||||
CheckCondition(1 ==
|
||||
rocksdb_options_get_level_compaction_dynamic_level_bytes(o));
|
||||
|
||||
rocksdb_options_set_max_bytes_for_level_multiplier(copy, 8.0);
|
||||
CheckCondition(8.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(copy));
|
||||
CheckCondition(2.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(o));
|
||||
|
||||
rocksdb_options_set_skip_stats_update_on_db_open(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_skip_stats_update_on_db_open(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(copy, 0);
|
||||
CheckCondition(
|
||||
0 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(copy));
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number(copy, 2000);
|
||||
CheckCondition(2000 == rocksdb_options_get_max_write_buffer_number(copy));
|
||||
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(o));
|
||||
|
||||
rocksdb_options_set_min_write_buffer_number_to_merge(copy, 146);
|
||||
CheckCondition(146 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(copy));
|
||||
CheckCondition(23 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number_to_maintain(copy, 128);
|
||||
CheckCondition(
|
||||
128 == rocksdb_options_get_max_write_buffer_number_to_maintain(copy));
|
||||
CheckCondition(64 ==
|
||||
rocksdb_options_get_max_write_buffer_number_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_size_to_maintain(copy, 9000);
|
||||
CheckCondition(9000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(copy));
|
||||
CheckCondition(50000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_enable_pipelined_write(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_enable_pipelined_write(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_enable_pipelined_write(o));
|
||||
|
||||
rocksdb_options_set_unordered_write(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_unordered_write(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_unordered_write(o));
|
||||
|
||||
rocksdb_options_set_max_subcompactions(copy, 90001);
|
||||
CheckCondition(90001 == rocksdb_options_get_max_subcompactions(copy));
|
||||
CheckCondition(123456 == rocksdb_options_get_max_subcompactions(o));
|
||||
|
||||
rocksdb_options_destroy(copy);
|
||||
rocksdb_options_destroy(o);
|
||||
}
|
||||
|
||||
StartPhase("iterate_upper_bound");
|
||||
{
|
||||
// Create new empty database
|
||||
@@ -1840,6 +2128,9 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
StartPhase("cancel_all_background_work");
|
||||
rocksdb_cancel_all_background_work(db, 1);
|
||||
|
||||
StartPhase("cleanup");
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_destroy(options);
|
||||
|
||||
+51
-11
@@ -471,6 +471,17 @@ void SuperVersionUnrefHandle(void* ptr) {
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
std::vector<std::string> ColumnFamilyData::GetDbPaths() const {
|
||||
std::vector<std::string> paths;
|
||||
paths.reserve(ioptions_.cf_paths.size());
|
||||
for (const DbPath& db_path : ioptions_.cf_paths) {
|
||||
paths.emplace_back(db_path.path);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
const uint32_t ColumnFamilyData::kDummyColumnFamilyDataId = port::kMaxUint32;
|
||||
|
||||
ColumnFamilyData::ColumnFamilyData(
|
||||
uint32_t id, const std::string& name, Version* _dummy_versions,
|
||||
Cache* _table_cache, WriteBufferManager* write_buffer_manager,
|
||||
@@ -507,7 +518,23 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
queued_for_compaction_(false),
|
||||
prev_compaction_needed_bytes_(0),
|
||||
allow_2pc_(db_options.allow_2pc),
|
||||
last_memtable_id_(0) {
|
||||
last_memtable_id_(0),
|
||||
db_paths_registered_(false) {
|
||||
if (id_ != kDummyColumnFamilyDataId) {
|
||||
// TODO(cc): RegisterDbPaths can be expensive, considering moving it
|
||||
// outside of this constructor which might be called with db mutex held.
|
||||
// TODO(cc): considering using ioptions_.fs, currently some tests rely on
|
||||
// EnvWrapper, that's the main reason why we use env here.
|
||||
Status s = ioptions_.env->RegisterDbPaths(GetDbPaths());
|
||||
if (s.ok()) {
|
||||
db_paths_registered_ = true;
|
||||
} else {
|
||||
ROCKS_LOG_ERROR(
|
||||
ioptions_.info_log,
|
||||
"Failed to register data paths of column family (id: %d, name: %s)",
|
||||
id_, name_.c_str());
|
||||
}
|
||||
}
|
||||
Ref();
|
||||
|
||||
// Convert user defined table properties collector factories to internal ones.
|
||||
@@ -601,6 +628,18 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
for (MemTable* m : to_delete) {
|
||||
delete m;
|
||||
}
|
||||
|
||||
if (db_paths_registered_) {
|
||||
// TODO(cc): considering using ioptions_.fs, currently some tests rely on
|
||||
// EnvWrapper, that's the main reason why we use env here.
|
||||
Status s = ioptions_.env->UnregisterDbPaths(GetDbPaths());
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(
|
||||
ioptions_.info_log,
|
||||
"Failed to unregister data paths of column family (id: %d, name: %s)",
|
||||
id_, name_.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::UnrefAndTryDelete() {
|
||||
@@ -1321,7 +1360,7 @@ Env::WriteLifeTimeHint ColumnFamilyData::CalculateSSTWriteHint(int level) {
|
||||
}
|
||||
|
||||
Status ColumnFamilyData::AddDirectories(
|
||||
std::map<std::string, std::shared_ptr<Directory>>* created_dirs) {
|
||||
std::map<std::string, std::shared_ptr<FSDirectory>>* created_dirs) {
|
||||
Status s;
|
||||
assert(created_dirs != nullptr);
|
||||
assert(data_dirs_.empty());
|
||||
@@ -1329,8 +1368,8 @@ Status ColumnFamilyData::AddDirectories(
|
||||
auto existing_dir = created_dirs->find(p.path);
|
||||
|
||||
if (existing_dir == created_dirs->end()) {
|
||||
std::unique_ptr<Directory> path_directory;
|
||||
s = DBImpl::CreateAndNewDirectory(ioptions_.env, p.path, &path_directory);
|
||||
std::unique_ptr<FSDirectory> path_directory;
|
||||
s = DBImpl::CreateAndNewDirectory(ioptions_.fs, p.path, &path_directory);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -1345,7 +1384,7 @@ Status ColumnFamilyData::AddDirectories(
|
||||
return s;
|
||||
}
|
||||
|
||||
Directory* ColumnFamilyData::GetDataDir(size_t path_id) const {
|
||||
FSDirectory* ColumnFamilyData::GetDataDir(size_t path_id) const {
|
||||
if (data_dirs_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1358,20 +1397,21 @@ ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
const ImmutableDBOptions* db_options,
|
||||
const FileOptions& file_options,
|
||||
Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
WriteController* write_controller,
|
||||
WriteBufferManager* _write_buffer_manager,
|
||||
WriteController* _write_controller,
|
||||
BlockCacheTracer* const block_cache_tracer)
|
||||
: max_column_family_(0),
|
||||
dummy_cfd_(new ColumnFamilyData(
|
||||
0, "", nullptr, nullptr, nullptr, ColumnFamilyOptions(), *db_options,
|
||||
file_options, nullptr, block_cache_tracer)),
|
||||
ColumnFamilyData::kDummyColumnFamilyDataId, "", nullptr, nullptr,
|
||||
nullptr, ColumnFamilyOptions(), *db_options, file_options, nullptr,
|
||||
block_cache_tracer)),
|
||||
default_cfd_cache_(nullptr),
|
||||
db_name_(dbname),
|
||||
db_options_(db_options),
|
||||
file_options_(file_options),
|
||||
table_cache_(table_cache),
|
||||
write_buffer_manager_(write_buffer_manager),
|
||||
write_controller_(write_controller),
|
||||
write_buffer_manager_(_write_buffer_manager),
|
||||
write_controller_(_write_controller),
|
||||
block_cache_tracer_(block_cache_tracer) {
|
||||
// initialize linked list
|
||||
dummy_cfd_->prev_ = dummy_cfd_;
|
||||
|
||||
+14
-5
@@ -500,14 +500,15 @@ class ColumnFamilyData {
|
||||
// created_dirs remembers directory created, so that we don't need to call
|
||||
// the same data creation operation again.
|
||||
Status AddDirectories(
|
||||
std::map<std::string, std::shared_ptr<Directory>>* created_dirs);
|
||||
std::map<std::string, std::shared_ptr<FSDirectory>>* created_dirs);
|
||||
|
||||
Directory* GetDataDir(size_t path_id) const;
|
||||
FSDirectory* GetDataDir(size_t path_id) const;
|
||||
|
||||
ThreadLocalPtr* TEST_GetLocalSV() { return local_sv_.get(); }
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
static const uint32_t kDummyColumnFamilyDataId;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
Version* dummy_versions, Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
@@ -517,6 +518,8 @@ class ColumnFamilyData {
|
||||
ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer);
|
||||
|
||||
std::vector<std::string> GetDbPaths() const;
|
||||
|
||||
uint32_t id_;
|
||||
const std::string name_;
|
||||
Version* dummy_versions_; // Head of circular doubly-linked list of versions.
|
||||
@@ -592,7 +595,9 @@ class ColumnFamilyData {
|
||||
std::atomic<uint64_t> last_memtable_id_;
|
||||
|
||||
// Directories corresponding to cf_paths.
|
||||
std::vector<std::shared_ptr<Directory>> data_dirs_;
|
||||
std::vector<std::shared_ptr<FSDirectory>> data_dirs_;
|
||||
|
||||
bool db_paths_registered_;
|
||||
};
|
||||
|
||||
// ColumnFamilySet has interesting thread-safety requirements
|
||||
@@ -642,8 +647,8 @@ class ColumnFamilySet {
|
||||
ColumnFamilySet(const std::string& dbname,
|
||||
const ImmutableDBOptions* db_options,
|
||||
const FileOptions& file_options, Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
WriteController* write_controller,
|
||||
WriteBufferManager* _write_buffer_manager,
|
||||
WriteController* _write_controller,
|
||||
BlockCacheTracer* const block_cache_tracer);
|
||||
~ColumnFamilySet();
|
||||
|
||||
@@ -673,6 +678,10 @@ class ColumnFamilySet {
|
||||
|
||||
Cache* get_table_cache() { return table_cache_; }
|
||||
|
||||
WriteBufferManager* write_buffer_manager() { return write_buffer_manager_; }
|
||||
|
||||
WriteController* write_controller() { return write_controller_; }
|
||||
|
||||
private:
|
||||
friend class ColumnFamilyData;
|
||||
// helper function that gets called from cfd destructor
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "memtable/hash_skiplist_rep.h"
|
||||
#include "options/options_parser.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
@@ -287,7 +287,9 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
// Verify the CF options of the returned CF handle.
|
||||
ColumnFamilyDescriptor desc;
|
||||
ASSERT_OK(handles_[cfi]->GetDescriptor(&desc));
|
||||
RocksDBOptionsParser::VerifyCFOptions(desc.options, current_cf_opt);
|
||||
RocksDBOptionsParser::VerifyCFOptions(ConfigOptions(), desc.options,
|
||||
current_cf_opt);
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
cfi++;
|
||||
}
|
||||
@@ -821,7 +823,7 @@ TEST_P(ColumnFamilyTest, BulkAddDrop) {
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, DropTest) {
|
||||
// first iteration - dont reopen DB before dropping
|
||||
// first iteration - don't reopen DB before dropping
|
||||
// second iteration - reopen DB before dropping
|
||||
for (int iter = 0; iter < 2; ++iter) {
|
||||
Open({"default"});
|
||||
|
||||
@@ -37,7 +37,7 @@ Status CompactedDBImpl::Get(const ReadOptions& options, ColumnFamilyHandle*,
|
||||
const Slice& key, PinnableSlice* value) {
|
||||
GetContext get_context(user_comparator_, nullptr, nullptr, nullptr,
|
||||
GetContext::kNotFound, key, value, nullptr, nullptr,
|
||||
true, nullptr, nullptr);
|
||||
nullptr, true, nullptr, nullptr);
|
||||
LookupKey lkey(key, kMaxSequenceNumber);
|
||||
files_.files[FindFile(key)].fd.table_reader->Get(options, lkey.internal_key(),
|
||||
&get_context, nullptr);
|
||||
@@ -70,7 +70,7 @@ std::vector<Status> CompactedDBImpl::MultiGet(const ReadOptions& options,
|
||||
std::string& value = (*values)[idx];
|
||||
GetContext get_context(user_comparator_, nullptr, nullptr, nullptr,
|
||||
GetContext::kNotFound, keys[idx], &pinnable_val,
|
||||
nullptr, nullptr, true, nullptr, nullptr);
|
||||
nullptr, nullptr, nullptr, true, nullptr, nullptr);
|
||||
LookupKey lkey(keys[idx], kMaxSequenceNumber);
|
||||
r->Get(options, lkey.internal_key(), &get_context, nullptr);
|
||||
value.assign(pinnable_val.data(), pinnable_val.size());
|
||||
|
||||
@@ -23,7 +23,7 @@ const uint64_t kRangeTombstoneSentinel =
|
||||
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const InternalKey& b) {
|
||||
auto c = user_cmp->Compare(a.user_key(), b.user_key());
|
||||
auto c = user_cmp->CompareWithoutTimestamp(a.user_key(), b.user_key());
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -298,19 +298,22 @@ CompactionJob::CompactionJob(
|
||||
const FileOptions& file_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_directory, Statistics* stats,
|
||||
FSDirectory* db_directory, FSDirectory* output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, std::shared_ptr<Cache> table_cache,
|
||||
EventLogger* event_logger, bool paranoid_file_checks, bool measure_io_stats,
|
||||
const std::string& dbname, CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri, const std::atomic<bool>* manual_compaction_paused)
|
||||
Env::Priority thread_pri, const std::atomic<bool>* manual_compaction_paused,
|
||||
const std::string& db_id, const std::string& db_session_id)
|
||||
: job_id_(job_id),
|
||||
compact_(new CompactionState(compaction)),
|
||||
compaction_job_stats_(compaction_job_stats),
|
||||
compaction_stats_(compaction->compaction_reason(), 1),
|
||||
dbname_(dbname),
|
||||
db_id_(db_id),
|
||||
db_session_id_(db_session_id),
|
||||
db_options_(db_options),
|
||||
file_options_(file_options),
|
||||
env_(db_options.env),
|
||||
@@ -542,7 +545,7 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
// Greedily add ranges to the subcompaction until the sum of the ranges'
|
||||
// sizes becomes >= the expected mean size of a subcompaction
|
||||
sum = 0;
|
||||
for (size_t i = 0; i < ranges.size() - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < ranges.size(); i++) {
|
||||
sum += ranges[i].size;
|
||||
if (subcompactions == 1) {
|
||||
// If there's only one left to schedule then it goes to the end so no
|
||||
@@ -613,8 +616,13 @@ Status CompactionJob::Run() {
|
||||
}
|
||||
}
|
||||
|
||||
IOStatus io_s;
|
||||
if (status.ok() && output_directory_) {
|
||||
status = output_directory_->Fsync();
|
||||
io_s = output_directory_->Fsync(IOOptions(), nullptr);
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
status = io_s;
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
@@ -649,8 +657,11 @@ Status CompactionJob::Run() {
|
||||
compact_->compaction->output_level()),
|
||||
TableReaderCaller::kCompactionRefill, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, compact_->compaction->output_level(),
|
||||
MaxFileSizeForL0MetaPin(
|
||||
*compact_->compaction->mutable_cf_options()),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr);
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
/*allow_unprepared_value=*/false);
|
||||
auto s = iter->status();
|
||||
|
||||
if (s.ok() && paranoid_file_checks_) {
|
||||
@@ -713,9 +724,13 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
cfd->internal_stats()->AddCompactionStats(
|
||||
compact_->compaction->output_level(), thread_pri_, compaction_stats_);
|
||||
|
||||
versions_->SetIOStatusOK();
|
||||
if (status.ok()) {
|
||||
status = InstallCompactionResults(mutable_cf_options);
|
||||
}
|
||||
if (!versions_->io_status().ok()) {
|
||||
io_status_ = versions_->io_status();
|
||||
}
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
auto vstorage = cfd->current()->storage_info();
|
||||
const auto& stats = compaction_stats_;
|
||||
@@ -928,7 +943,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
assert(sub_compact->builder != nullptr);
|
||||
assert(sub_compact->current_output() != nullptr);
|
||||
sub_compact->builder->Add(key, value);
|
||||
sub_compact->current_output_file_size = sub_compact->builder->FileSize();
|
||||
sub_compact->current_output_file_size =
|
||||
sub_compact->builder->EstimatedFileSize();
|
||||
const ParsedInternalKey& ikey = c_iter->ikey();
|
||||
sub_compact->current_output()->meta.UpdateBoundaries(
|
||||
key, value, ikey.sequence, ikey.type);
|
||||
@@ -1294,25 +1310,35 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
} else {
|
||||
sub_compact->builder->Abandon();
|
||||
}
|
||||
if (!sub_compact->builder->io_status().ok()) {
|
||||
io_status_ = sub_compact->builder->io_status();
|
||||
s = io_status_;
|
||||
}
|
||||
const uint64_t current_bytes = sub_compact->builder->FileSize();
|
||||
if (s.ok()) {
|
||||
// Add the checksum information to file metadata.
|
||||
meta->file_checksum = sub_compact->builder->GetFileChecksum();
|
||||
meta->file_checksum_func_name =
|
||||
sub_compact->builder->GetFileChecksumFuncName();
|
||||
|
||||
meta->fd.file_size = current_bytes;
|
||||
}
|
||||
sub_compact->current_output()->finished = true;
|
||||
sub_compact->total_bytes += current_bytes;
|
||||
|
||||
// Finish and check for file errors
|
||||
IOStatus io_s;
|
||||
if (s.ok()) {
|
||||
StopWatch sw(env_, stats_, COMPACTION_OUTFILE_SYNC_MICROS);
|
||||
s = sub_compact->outfile->Sync(db_options_.use_fsync);
|
||||
io_s = sub_compact->outfile->Sync(db_options_.use_fsync);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = sub_compact->outfile->Close();
|
||||
if (io_s.ok()) {
|
||||
io_s = sub_compact->outfile->Close();
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
// Add the checksum information to file metadata.
|
||||
meta->file_checksum = sub_compact->outfile->GetFileChecksum();
|
||||
meta->file_checksum_func_name =
|
||||
sub_compact->outfile->GetFileChecksumFuncName();
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
s = io_s;
|
||||
}
|
||||
sub_compact->outfile.reset();
|
||||
|
||||
@@ -1514,7 +1540,7 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
sub_compact->outfile.reset(
|
||||
new WritableFileWriter(std::move(writable_file), fname, file_options_,
|
||||
env_, db_options_.statistics.get(), listeners,
|
||||
db_options_.sst_file_checksum_func.get()));
|
||||
db_options_.file_checksum_gen_factory.get()));
|
||||
|
||||
// If the Column family flag is to only optimize filters for hits,
|
||||
// we can skip creating filters if this is the bottommost_level where
|
||||
@@ -1531,7 +1557,8 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
sub_compact->compaction->output_compression_opts(),
|
||||
sub_compact->compaction->output_level(), skip_filters,
|
||||
oldest_ancester_time, 0 /* oldest_key_time */,
|
||||
sub_compact->compaction->max_output_file_size(), current_time));
|
||||
sub_compact->compaction->max_output_file_size(), current_time, db_id_,
|
||||
db_session_id_));
|
||||
LogFlush(db_options_.info_log);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -67,8 +67,8 @@ class CompactionJob {
|
||||
const FileOptions& file_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
LogBuffer* log_buffer, Directory* db_directory,
|
||||
Directory* output_directory, Statistics* stats,
|
||||
LogBuffer* log_buffer, FSDirectory* db_directory,
|
||||
FSDirectory* output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
@@ -78,7 +78,9 @@ class CompactionJob {
|
||||
const std::string& dbname,
|
||||
CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri,
|
||||
const std::atomic<bool>* manual_compaction_paused = nullptr);
|
||||
const std::atomic<bool>* manual_compaction_paused = nullptr,
|
||||
const std::string& db_id = "",
|
||||
const std::string& db_session_id = "");
|
||||
|
||||
~CompactionJob();
|
||||
|
||||
@@ -100,6 +102,9 @@ class CompactionJob {
|
||||
// Add compaction input/output to the current version
|
||||
Status Install(const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Return the IO status
|
||||
IOStatus io_status() const { return io_status_; }
|
||||
|
||||
private:
|
||||
struct SubcompactionState;
|
||||
|
||||
@@ -149,6 +154,8 @@ class CompactionJob {
|
||||
|
||||
// DBImpl state
|
||||
const std::string& dbname_;
|
||||
const std::string db_id_;
|
||||
const std::string db_session_id_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
const FileOptions file_options_;
|
||||
|
||||
@@ -161,8 +168,8 @@ class CompactionJob {
|
||||
const std::atomic<bool>* manual_compaction_paused_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
LogBuffer* log_buffer_;
|
||||
Directory* db_directory_;
|
||||
Directory* output_directory_;
|
||||
FSDirectory* db_directory_;
|
||||
FSDirectory* output_directory_;
|
||||
Statistics* stats_;
|
||||
InstrumentedMutex* db_mutex_;
|
||||
ErrorHandler* db_error_handler_;
|
||||
@@ -193,6 +200,7 @@ class CompactionJob {
|
||||
std::vector<uint64_t> sizes_;
|
||||
Env::WriteLifeTimeHint write_hint_;
|
||||
Env::Priority thread_pri_;
|
||||
IOStatus io_status_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "db/blob_index.h"
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction/compaction_job.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
@@ -281,7 +281,7 @@ class CompactionJobTest : public testing::Test {
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
// Make "CURRENT" file that points to the new manifest file.
|
||||
s = SetCurrentFile(env_, dbname_, 1, nullptr);
|
||||
s = SetCurrentFile(fs_.get(), dbname_, 1, nullptr);
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
cf_options_.table_factory = mock_table_factory_;
|
||||
@@ -314,11 +314,11 @@ class CompactionJobTest : public testing::Test {
|
||||
num_input_files += level_files.size();
|
||||
}
|
||||
|
||||
Compaction compaction(cfd->current()->storage_info(), *cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
compaction_input_files, output_level, 1024 * 1024,
|
||||
10 * 1024 * 1024, 0, kNoCompression,
|
||||
cfd->ioptions()->compression_opts, 0, {}, true);
|
||||
Compaction compaction(
|
||||
cfd->current()->storage_info(), *cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions(), compaction_input_files, output_level,
|
||||
1024 * 1024, 10 * 1024 * 1024, 0, kNoCompression,
|
||||
cfd->GetLatestMutableCFOptions()->compression_opts, 0, {}, true);
|
||||
compaction.SetInputVersion(cfd->current());
|
||||
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
|
||||
|
||||
@@ -110,9 +110,9 @@ CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
|
||||
// If bottommost_compression is set and we are compacting to the
|
||||
// bottommost level then we should use it.
|
||||
if (ioptions.bottommost_compression != kDisableCompressionOption &&
|
||||
if (mutable_cf_options.bottommost_compression != kDisableCompressionOption &&
|
||||
level >= (vstorage->num_non_empty_levels() - 1)) {
|
||||
return ioptions.bottommost_compression;
|
||||
return mutable_cf_options.bottommost_compression;
|
||||
}
|
||||
// If the user has specified a different compression level for each level,
|
||||
// then pick the compression for that level.
|
||||
@@ -132,22 +132,22 @@ CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
}
|
||||
}
|
||||
|
||||
CompressionOptions GetCompressionOptions(const ImmutableCFOptions& ioptions,
|
||||
CompressionOptions GetCompressionOptions(const MutableCFOptions& cf_options,
|
||||
const VersionStorageInfo* vstorage,
|
||||
int level,
|
||||
const bool enable_compression) {
|
||||
if (!enable_compression) {
|
||||
return ioptions.compression_opts;
|
||||
return cf_options.compression_opts;
|
||||
}
|
||||
// If bottommost_compression is set and we are compacting to the
|
||||
// bottommost level then we should use the specified compression options
|
||||
// for the bottmomost_compression.
|
||||
if (ioptions.bottommost_compression != kDisableCompressionOption &&
|
||||
if (cf_options.bottommost_compression != kDisableCompressionOption &&
|
||||
level >= (vstorage->num_non_empty_levels() - 1) &&
|
||||
ioptions.bottommost_compression_opts.enabled) {
|
||||
return ioptions.bottommost_compression_opts;
|
||||
cf_options.bottommost_compression_opts.enabled) {
|
||||
return cf_options.bottommost_compression_opts;
|
||||
}
|
||||
return ioptions.compression_opts;
|
||||
return cf_options.compression_opts;
|
||||
}
|
||||
|
||||
CompactionPicker::CompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
@@ -359,7 +359,7 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
vstorage, ioptions_, mutable_cf_options, input_files, output_level,
|
||||
compact_options.output_file_size_limit,
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id, compression_type,
|
||||
GetCompressionOptions(ioptions_, vstorage, output_level),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
compact_options.max_subcompactions,
|
||||
/* grandparents */ {}, true);
|
||||
RegisterCompaction(c);
|
||||
@@ -634,7 +634,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
compact_range_options.target_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, 1),
|
||||
GetCompressionOptions(ioptions_, vstorage, output_level),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
compact_range_options.max_subcompactions, /* grandparents */ {},
|
||||
/* is manual */ true);
|
||||
RegisterCompaction(c);
|
||||
@@ -787,7 +787,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
compact_range_options.target_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, output_level,
|
||||
vstorage->base_level()),
|
||||
GetCompressionOptions(ioptions_, vstorage, output_level),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
compact_range_options.max_subcompactions, std::move(grandparents),
|
||||
/* is manual compaction */ true);
|
||||
|
||||
@@ -1085,6 +1085,8 @@ void CompactionPicker::PickFilesMarkedForCompaction(
|
||||
Random64 rnd(/* seed */ reinterpret_cast<uint64_t>(vstorage));
|
||||
size_t random_file_index = static_cast<size_t>(rnd.Uniform(
|
||||
static_cast<uint64_t>(vstorage->FilesMarkedForCompaction().size())));
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionPicker::PickFilesMarkedForCompaction",
|
||||
&random_file_index);
|
||||
|
||||
if (continuation(vstorage->FilesMarkedForCompaction()[random_file_index])) {
|
||||
// found the compaction!
|
||||
|
||||
@@ -305,9 +305,9 @@ CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
int level, int base_level,
|
||||
const bool enable_compression = true);
|
||||
|
||||
CompressionOptions GetCompressionOptions(const ImmutableCFOptions& ioptions,
|
||||
const VersionStorageInfo* vstorage,
|
||||
int level,
|
||||
const bool enable_compression = true);
|
||||
CompressionOptions GetCompressionOptions(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const VersionStorageInfo* vstorage, int level,
|
||||
const bool enable_compression = true);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -70,18 +70,18 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
// avoid underflow
|
||||
if (current_time > mutable_cf_options.ttl) {
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
auto f = *ritr;
|
||||
if (f->fd.table_reader != nullptr &&
|
||||
f->fd.table_reader->GetTableProperties() != nullptr) {
|
||||
auto creation_time =
|
||||
FileMetaData* f = *ritr;
|
||||
assert(f);
|
||||
if (f->fd.table_reader && f->fd.table_reader->GetTableProperties()) {
|
||||
uint64_t creation_time =
|
||||
f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
if (creation_time == 0 ||
|
||||
creation_time >= (current_time - mutable_cf_options.ttl)) {
|
||||
break;
|
||||
}
|
||||
total_size -= f->compensated_file_size;
|
||||
inputs[0].files.push_back(f);
|
||||
}
|
||||
total_size -= f->compensated_file_size;
|
||||
inputs[0].files.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,17 +96,22 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
}
|
||||
|
||||
for (const auto& f : inputs[0].files) {
|
||||
uint64_t creation_time = 0;
|
||||
assert(f);
|
||||
if (f->fd.table_reader && f->fd.table_reader->GetTableProperties()) {
|
||||
creation_time = f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
}
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with creation time %" PRIu64 " for deletion",
|
||||
cf_name.c_str(), f->fd.GetNumber(),
|
||||
f->fd.table_reader->GetTableProperties()->creation_time);
|
||||
cf_name.c_str(), f->fd.GetNumber(), creation_time);
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, ioptions_.compression_opts, /* max_subcompactions */ 0,
|
||||
{}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
kNoCompression, mutable_cf_options.compression_opts,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOTtl);
|
||||
return c;
|
||||
}
|
||||
@@ -146,7 +151,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
16 * 1024 * 1024 /* output file size limit */,
|
||||
0 /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID */, mutable_cf_options.compression,
|
||||
ioptions_.compression_opts, 0 /* max_subcompactions */, {},
|
||||
mutable_cf_options.compression_opts, 0 /* max_subcompactions */, {},
|
||||
/* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
CompactionReason::kFIFOReduceNumFiles);
|
||||
@@ -194,8 +199,9 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, ioptions_.compression_opts, /* max_subcompactions */ 0,
|
||||
{}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
kNoCompression, mutable_cf_options.compression_opts,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOMaxSize);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -93,9 +93,13 @@ class LevelCompactionBuilder {
|
||||
// otherwise, returns false.
|
||||
bool PickIntraL0Compaction();
|
||||
|
||||
void PickExpiredTtlFiles();
|
||||
|
||||
void PickFilesMarkedForPeriodicCompaction();
|
||||
// Picks a file from level_files to compact.
|
||||
// level_files is a vector of (level, file metadata) in ascending order of
|
||||
// level. If compact_to_next_level is true, compact the file to the next
|
||||
// level, otherwise, compact to the same level as the input file.
|
||||
void PickFileToCompact(
|
||||
const autovector<std::pair<int, FileMetaData*>>& level_files,
|
||||
bool compact_to_next_level);
|
||||
|
||||
const std::string& cf_name_;
|
||||
VersionStorageInfo* vstorage_;
|
||||
@@ -124,72 +128,34 @@ class LevelCompactionBuilder {
|
||||
static const int kMinFilesForIntraL0Compaction = 4;
|
||||
};
|
||||
|
||||
void LevelCompactionBuilder::PickExpiredTtlFiles() {
|
||||
if (vstorage_->ExpiredTtlFiles().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&](std::pair<int, FileMetaData*> level_file) {
|
||||
void LevelCompactionBuilder::PickFileToCompact(
|
||||
const autovector<std::pair<int, FileMetaData*>>& level_files,
|
||||
bool compact_to_next_level) {
|
||||
for (auto& level_file : level_files) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
start_level_ = level_file.first;
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
|
||||
if ((start_level_ == vstorage_->num_non_empty_levels() - 1) ||
|
||||
if ((compact_to_next_level &&
|
||||
start_level_ == vstorage_->num_non_empty_levels() - 1) ||
|
||||
(start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty())) {
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
if (compact_to_next_level) {
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
} else {
|
||||
output_level_ = start_level_;
|
||||
}
|
||||
|
||||
start_level_inputs_.files = {level_file.second};
|
||||
start_level_inputs_.level = start_level_;
|
||||
return compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
for (auto& level_file : vstorage_->ExpiredTtlFiles()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
if (compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
void LevelCompactionBuilder::PickFilesMarkedForPeriodicCompaction() {
|
||||
if (vstorage_->FilesMarkedForPeriodicCompaction().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&](std::pair<int, FileMetaData*> level_file) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
output_level_ = start_level_ = level_file.first;
|
||||
|
||||
if (start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.files = {level_file.second};
|
||||
start_level_inputs_.level = start_level_;
|
||||
return compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
for (auto& level_file : vstorage_->FilesMarkedForPeriodicCompaction()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
@@ -238,64 +204,46 @@ void LevelCompactionBuilder::SetupInitialFiles() {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Compaction scores are sorted in descending order, no further scores
|
||||
// will be >= 1.
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!start_level_inputs_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we didn't find a compaction, check if there are any files marked for
|
||||
// compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
parent_index_ = base_index_ = -1;
|
||||
parent_index_ = base_index_ = -1;
|
||||
|
||||
compaction_picker_->PickFilesMarkedForCompaction(
|
||||
cf_name_, vstorage_, &start_level_, &output_level_,
|
||||
&start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
is_manual_ = true;
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
}
|
||||
compaction_picker_->PickFilesMarkedForCompaction(
|
||||
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
}
|
||||
|
||||
// Bottommost Files Compaction on deleting tombstones
|
||||
if (start_level_inputs_.empty()) {
|
||||
size_t i;
|
||||
for (i = 0; i < vstorage_->BottommostFilesMarkedForCompaction().size();
|
||||
++i) {
|
||||
auto& level_and_file = vstorage_->BottommostFilesMarkedForCompaction()[i];
|
||||
assert(!level_and_file.second->being_compacted);
|
||||
start_level_inputs_.level = output_level_ = start_level_ =
|
||||
level_and_file.first;
|
||||
start_level_inputs_.files = {level_and_file.second};
|
||||
if (compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == vstorage_->BottommostFilesMarkedForCompaction().size()) {
|
||||
start_level_inputs_.clear();
|
||||
} else {
|
||||
assert(!start_level_inputs_.empty());
|
||||
compaction_reason_ = CompactionReason::kBottommostFiles;
|
||||
return;
|
||||
}
|
||||
PickFileToCompact(vstorage_->BottommostFilesMarkedForCompaction(), false);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kBottommostFiles;
|
||||
return;
|
||||
}
|
||||
|
||||
// TTL Compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
PickExpiredTtlFiles();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kTtl;
|
||||
return;
|
||||
}
|
||||
PickFileToCompact(vstorage_->ExpiredTtlFiles(), true);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kTtl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Periodic Compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
PickFilesMarkedForPeriodicCompaction();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kPeriodicCompaction;
|
||||
return;
|
||||
}
|
||||
PickFileToCompact(vstorage_->FilesMarkedForPeriodicCompaction(), false);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kPeriodicCompaction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +332,7 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
GetPathId(ioptions_, mutable_cf_options_, output_level_),
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level_, vstorage_->base_level()),
|
||||
GetCompressionOptions(ioptions_, vstorage_, output_level_),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level_),
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
start_level_score_, false /* deletion_compaction */, compaction_reason_);
|
||||
|
||||
|
||||
@@ -78,8 +78,17 @@ class CompactionPickerTest : public testing::Test {
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
}
|
||||
|
||||
// Create a new VersionStorageInfo object so we can add mode files and then
|
||||
// merge it with the existing VersionStorageInfo
|
||||
void AddVersionStorage() {
|
||||
temp_vstorage_.reset(new VersionStorageInfo(
|
||||
&icmp_, ucmp_, options_.num_levels, ioptions_.compaction_style,
|
||||
vstorage_.get(), false));
|
||||
}
|
||||
|
||||
void DeleteVersionStorage() {
|
||||
vstorage_.reset();
|
||||
temp_vstorage_.reset();
|
||||
files_.clear();
|
||||
file_map_.clear();
|
||||
input_files_.clear();
|
||||
@@ -88,18 +97,24 @@ class CompactionPickerTest : public testing::Test {
|
||||
void Add(int level, uint32_t file_number, const char* smallest,
|
||||
const char* largest, uint64_t file_size = 1, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
|
||||
size_t compensated_file_size = 0) {
|
||||
assert(level < vstorage_->num_levels());
|
||||
size_t compensated_file_size = 0, bool marked_for_compact = false) {
|
||||
VersionStorageInfo* vstorage;
|
||||
if (temp_vstorage_) {
|
||||
vstorage = temp_vstorage_.get();
|
||||
} else {
|
||||
vstorage = vstorage_.get();
|
||||
}
|
||||
assert(level < vstorage->num_levels());
|
||||
FileMetaData* f = new FileMetaData(
|
||||
file_number, path_id, file_size,
|
||||
InternalKey(smallest, smallest_seq, kTypeValue),
|
||||
InternalKey(largest, largest_seq, kTypeValue), smallest_seq,
|
||||
largest_seq, /* marked_for_compact */ false, kInvalidBlobFileNumber,
|
||||
largest_seq, marked_for_compact, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName);
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
vstorage_->AddFile(level, f);
|
||||
vstorage->AddFile(level, f);
|
||||
files_.emplace_back(f);
|
||||
file_map_.insert({file_number, {f, level}});
|
||||
}
|
||||
@@ -122,6 +137,12 @@ class CompactionPickerTest : public testing::Test {
|
||||
}
|
||||
|
||||
void UpdateVersionStorageInfo() {
|
||||
if (temp_vstorage_) {
|
||||
VersionBuilder builder(FileOptions(), &ioptions_, nullptr,
|
||||
vstorage_.get(), nullptr);
|
||||
builder.SaveTo(temp_vstorage_.get());
|
||||
vstorage_ = std::move(temp_vstorage_);
|
||||
}
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
vstorage_->UpdateFilesByCompactionPri(ioptions_.compaction_pri);
|
||||
vstorage_->UpdateNumNonEmptyLevels();
|
||||
@@ -132,6 +153,28 @@ class CompactionPickerTest : public testing::Test {
|
||||
vstorage_->ComputeFilesMarkedForCompaction();
|
||||
vstorage_->SetFinalized();
|
||||
}
|
||||
void AddFileToVersionStorage(int level, uint32_t file_number,
|
||||
const char* smallest, const char* largest,
|
||||
uint64_t file_size = 1, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100,
|
||||
SequenceNumber largest_seq = 100,
|
||||
size_t compensated_file_size = 0,
|
||||
bool marked_for_compact = false) {
|
||||
VersionStorageInfo* base_vstorage = vstorage_.release();
|
||||
vstorage_.reset(new VersionStorageInfo(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleUniversal,
|
||||
base_vstorage, false));
|
||||
Add(level, file_number, smallest, largest, file_size, path_id, smallest_seq,
|
||||
largest_seq, compensated_file_size, marked_for_compact);
|
||||
|
||||
VersionBuilder builder(FileOptions(), &ioptions_, nullptr, base_vstorage,
|
||||
nullptr);
|
||||
builder.SaveTo(vstorage_.get());
|
||||
UpdateVersionStorageInfo();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<VersionStorageInfo> temp_vstorage_;
|
||||
};
|
||||
|
||||
TEST_F(CompactionPickerTest, Empty) {
|
||||
@@ -371,8 +414,8 @@ TEST_F(CompactionPickerTest, LevelTriggerDynamic4) {
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
NewVersionStorage(num_levels, kCompactionStyleLevel);
|
||||
Add(0, 1U, "150", "200");
|
||||
Add(num_levels - 1, 3U, "200", "250", 300U);
|
||||
Add(num_levels - 1, 4U, "300", "350", 3000U);
|
||||
Add(num_levels - 1, 2U, "200", "250", 300U);
|
||||
Add(num_levels - 1, 3U, "300", "350", 3000U);
|
||||
Add(num_levels - 1, 4U, "400", "450", 3U);
|
||||
Add(num_levels - 2, 5U, "150", "180", 300U);
|
||||
Add(num_levels - 2, 6U, "181", "350", 500U);
|
||||
@@ -739,7 +782,7 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping2) {
|
||||
Add(2, 8U, "201", "300",
|
||||
60000000U); // Overlaps with file 28, 29, total size 521M
|
||||
|
||||
Add(3, 26U, "100", "110", 261000000U);
|
||||
Add(3, 25U, "100", "110", 261000000U);
|
||||
Add(3, 26U, "150", "170", 261000000U);
|
||||
Add(3, 27U, "171", "179", 260000000U);
|
||||
Add(3, 28U, "191", "220", 260000000U);
|
||||
@@ -1255,7 +1298,7 @@ TEST_F(CompactionPickerTest, EstimateCompactionBytesNeeded1) {
|
||||
// Size ratio L4/L3 is 9.9
|
||||
// After merge from L3, L4 size is 1000900
|
||||
Add(4, 11U, "400", "500", 999900);
|
||||
Add(5, 11U, "400", "500", 8007200);
|
||||
Add(5, 12U, "400", "500", 8007200);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
@@ -1568,8 +1611,8 @@ TEST_F(CompactionPickerTest, IsTrivialMoveOn) {
|
||||
|
||||
Add(3, 5U, "120", "130", 7000U);
|
||||
Add(3, 6U, "170", "180", 7000U);
|
||||
Add(3, 5U, "220", "230", 7000U);
|
||||
Add(3, 5U, "270", "280", 7000U);
|
||||
Add(3, 7U, "220", "230", 7000U);
|
||||
Add(3, 8U, "270", "280", 7000U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
@@ -1733,6 +1776,165 @@ TEST_F(CompactionPickerTest, IntraL0ForEarliestSeqno) {
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
// This test covers the case where a "regular" universal compaction is
|
||||
// scheduled first, followed by a delete triggered compaction. The latter
|
||||
// should fail
|
||||
NewVersionStorage(5, kCompactionStyleUniversal);
|
||||
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550);
|
||||
Add(0, 2U, "201", "250", 2 * kFileSize, 0, 401, 450);
|
||||
Add(0, 4U, "260", "300", 4 * kFileSize, 0, 260, 300);
|
||||
Add(3, 5U, "010", "080", 8 * kFileSize, 0, 200, 251);
|
||||
Add(4, 3U, "301", "350", 8 * kFileSize, 0, 101, 150);
|
||||
Add(4, 6U, "501", "750", 8 * kFileSize, 0, 101, 150);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a compaction to reduce sorted runs
|
||||
ASSERT_EQ(CompactionReason::kUniversalSortedRunNum,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
|
||||
AddVersionStorage();
|
||||
// Simulate a flush and mark the file for compaction
|
||||
Add(0, 7U, "150", "200", kFileSize, 0, 551, 600, 0, true);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction2(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_FALSE(compaction2);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap2) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
// This test covers the case where a delete triggered compaction is
|
||||
// scheduled first, followed by a "regular" compaction. The latter
|
||||
// should fail
|
||||
NewVersionStorage(5, kCompactionStyleUniversal);
|
||||
|
||||
// Mark file number 4 for compaction
|
||||
Add(0, 4U, "260", "300", 4 * kFileSize, 0, 260, 300, 0, true);
|
||||
Add(3, 5U, "240", "290", 8 * kFileSize, 0, 201, 250);
|
||||
Add(4, 3U, "301", "350", 8 * kFileSize, 0, 101, 150);
|
||||
Add(4, 6U, "501", "750", 8 * kFileSize, 0, 101, 150);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a delete triggered compaction
|
||||
ASSERT_EQ(CompactionReason::kFilesMarkedForCompaction,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(3, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
|
||||
AddVersionStorage();
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550);
|
||||
Add(0, 2U, "201", "250", 2 * kFileSize, 0, 401, 450);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction2(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_FALSE(compaction2);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedCompactionStartOutputOverlap) {
|
||||
// The case where universal periodic compaction can be picked
|
||||
// with some newer files being compacted.
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
|
||||
bool input_level_overlap = false;
|
||||
bool output_level_overlap = false;
|
||||
// Let's mark 2 files in 2 different levels for compaction. The
|
||||
// compaction picker will randomly pick one, so use the sync point to
|
||||
// ensure a deterministic order. Loop until both cases are covered
|
||||
size_t random_index = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionPicker::PickFilesMarkedForCompaction", [&](void* arg) {
|
||||
size_t* index = static_cast<size_t*>(arg);
|
||||
*index = random_index;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
while (!input_level_overlap || !output_level_overlap) {
|
||||
// Ensure that the L0 file gets picked first
|
||||
random_index = !input_level_overlap ? 0 : 1;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
NewVersionStorage(5, kCompactionStyleUniversal);
|
||||
|
||||
Add(0, 1U, "260", "300", 4 * kFileSize, 0, 260, 300, 0, true);
|
||||
Add(3, 2U, "010", "020", 2 * kFileSize, 0, 201, 248);
|
||||
Add(3, 3U, "250", "270", 2 * kFileSize, 0, 202, 249);
|
||||
Add(3, 4U, "290", "310", 2 * kFileSize, 0, 203, 250);
|
||||
Add(3, 5U, "310", "320", 2 * kFileSize, 0, 204, 251, 0, true);
|
||||
Add(4, 6U, "301", "350", 8 * kFileSize, 0, 101, 150);
|
||||
Add(4, 7U, "501", "750", 8 * kFileSize, 0, 101, 150);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a delete triggered compaction
|
||||
ASSERT_EQ(CompactionReason::kFilesMarkedForCompaction,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_TRUE(compaction->start_level() == 0 ||
|
||||
compaction->start_level() == 3);
|
||||
if (compaction->start_level() == 0) {
|
||||
// The L0 file was picked. The next compaction will detect an
|
||||
// overlap on its input level
|
||||
input_level_overlap = true;
|
||||
ASSERT_EQ(3, compaction->output_level());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(3U, compaction->num_input_files(1));
|
||||
} else {
|
||||
// The level 3 file was picked. The next compaction will pick
|
||||
// the L0 file and will detect overlap when adding output
|
||||
// level inputs
|
||||
output_level_overlap = true;
|
||||
ASSERT_EQ(4, compaction->output_level());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
}
|
||||
|
||||
vstorage_->ComputeCompactionScore(ioptions_, mutable_cf_options_);
|
||||
// After recomputing the compaction score, only one marked file will remain
|
||||
random_index = 0;
|
||||
std::unique_ptr<Compaction> compaction2(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_FALSE(compaction2);
|
||||
DeleteVersionStorage();
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -120,8 +120,7 @@ class UniversalCompactionBuilder {
|
||||
LogBuffer* log_buffer_;
|
||||
|
||||
static std::vector<SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
const VersionStorageInfo& vstorage);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
@@ -325,8 +324,7 @@ void UniversalCompactionBuilder::SortedRun::DumpSizeInfo(
|
||||
|
||||
std::vector<UniversalCompactionBuilder::SortedRun>
|
||||
UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& /*ioptions*/,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
const VersionStorageInfo& vstorage) {
|
||||
std::vector<UniversalCompactionBuilder::SortedRun> ret;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(0)) {
|
||||
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
|
||||
@@ -336,27 +334,16 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
uint64_t total_compensated_size = 0U;
|
||||
uint64_t total_size = 0U;
|
||||
bool being_compacted = false;
|
||||
bool is_first = true;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(level)) {
|
||||
total_compensated_size += f->compensated_file_size;
|
||||
total_size += f->fd.GetFileSize();
|
||||
if (mutable_cf_options.compaction_options_universal.allow_trivial_move ==
|
||||
true) {
|
||||
if (f->being_compacted) {
|
||||
being_compacted = f->being_compacted;
|
||||
}
|
||||
} else {
|
||||
// Compaction always includes all files for a non-zero level, so for a
|
||||
// non-zero level, all the files should share the same being_compacted
|
||||
// value.
|
||||
// This assumption is only valid when
|
||||
// mutable_cf_options.compaction_options_universal.allow_trivial_move
|
||||
// is false
|
||||
assert(is_first || f->being_compacted == being_compacted);
|
||||
}
|
||||
if (is_first) {
|
||||
// Size amp, read amp and periodic compactions always include all files
|
||||
// for a non-zero level. However, a delete triggered compaction and
|
||||
// a trivial move might pick a subset of files in a sorted run. So
|
||||
// always check all files in a sorted run and mark the entire run as
|
||||
// being compacted if one or more files are being compacted
|
||||
if (f->being_compacted) {
|
||||
being_compacted = f->being_compacted;
|
||||
is_first = false;
|
||||
}
|
||||
}
|
||||
if (total_compensated_size > 0) {
|
||||
@@ -372,8 +359,7 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
const int kLevel0 = 0;
|
||||
score_ = vstorage_->CompactionScore(kLevel0);
|
||||
sorted_runs_ =
|
||||
CalculateSortedRuns(*vstorage_, ioptions_, mutable_cf_options_);
|
||||
sorted_runs_ = CalculateSortedRuns(*vstorage_);
|
||||
|
||||
if (sorted_runs_.size() == 0 ||
|
||||
(vstorage_->FilesMarkedForPeriodicCompaction().empty() &&
|
||||
@@ -389,7 +375,7 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
ROCKS_LOG_BUFFER_MAX_SZ(
|
||||
log_buffer_, 3072,
|
||||
"[%s] Universal: sorted runs files(%" ROCKSDB_PRIszt "): %s\n",
|
||||
"[%s] Universal: sorted runs: %" ROCKSDB_PRIszt " files: %s\n",
|
||||
cf_name_.c_str(), sorted_runs_.size(), vstorage_->LevelSummary(&tmp));
|
||||
|
||||
Compaction* c = nullptr;
|
||||
@@ -751,7 +737,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_, start_level,
|
||||
1, enable_compression),
|
||||
GetCompressionOptions(ioptions_, vstorage_, start_level,
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, start_level,
|
||||
enable_compression),
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
score_, false /* deletion_compaction */, compaction_reason);
|
||||
@@ -779,7 +765,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
}
|
||||
|
||||
// Skip files that are already being compacted
|
||||
for (size_t loop = 0; loop < sorted_runs_.size() - 1; loop++) {
|
||||
for (size_t loop = 0; loop + 1 < sorted_runs_.size(); loop++) {
|
||||
sr = &sorted_runs_[loop];
|
||||
if (!sr->being_compacted) {
|
||||
start_index = loop; // Consider this as the first candidate.
|
||||
@@ -807,7 +793,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
}
|
||||
|
||||
// keep adding up all the remaining files
|
||||
for (size_t loop = start_index; loop < sorted_runs_.size() - 1; loop++) {
|
||||
for (size_t loop = start_index; loop + 1 < sorted_runs_.size(); loop++) {
|
||||
sr = &sorted_runs_[loop];
|
||||
if (sr->being_compacted) {
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
@@ -855,6 +841,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
|
||||
if (vstorage_->num_levels() == 1) {
|
||||
#if defined(ENABLE_SINGLE_LEVEL_DTC)
|
||||
// This is single level universal. Since we're basically trying to reclaim
|
||||
// space by processing files marked for compaction due to high tombstone
|
||||
// density, let's do the same thing as compaction to reduce size amp which
|
||||
@@ -877,6 +864,11 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
return nullptr;
|
||||
}
|
||||
inputs.push_back(start_level_inputs);
|
||||
#else
|
||||
// Disable due to a known race condition.
|
||||
// TODO: Reenable once the race condition is fixed
|
||||
return nullptr;
|
||||
#endif // ENABLE_SINGLE_LEVEL_DTC
|
||||
} else {
|
||||
int start_level;
|
||||
|
||||
@@ -959,8 +951,8 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
/* max_grandparent_overlap_bytes */ LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level, 1),
|
||||
GetCompressionOptions(ioptions_, vstorage_, output_level),
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ true,
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
score_, false /* deletion_compaction */,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
}
|
||||
@@ -1029,7 +1021,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToOldest(
|
||||
LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_, start_level,
|
||||
1, true /* enable_compression */),
|
||||
GetCompressionOptions(ioptions_, vstorage_, start_level,
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, start_level,
|
||||
true /* enable_compression */),
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
score_, false /* deletion_compaction */, compaction_reason);
|
||||
|
||||
+4
-3
@@ -14,7 +14,7 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
void CancelAllBackgroundWork(DB* db, bool wait) {
|
||||
(static_cast_with_check<DBImpl, DB>(db->GetRootDB()))
|
||||
(static_cast_with_check<DBImpl>(db->GetRootDB()))
|
||||
->CancelAllBackgroundWork(wait);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ Status DeleteFilesInRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
Status DeleteFilesInRanges(DB* db, ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end) {
|
||||
return (static_cast_with_check<DBImpl, DB>(db->GetRootDB()))
|
||||
return (static_cast_with_check<DBImpl>(db->GetRootDB()))
|
||||
->DeleteFilesInRanges(column_family, ranges, n, include_end);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,8 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
s = ioptions.table_factory->NewTableReader(
|
||||
TableReaderOptions(ioptions, options.prefix_extractor.get(), env_options,
|
||||
internal_comparator, false /* skip_filters */,
|
||||
!kImmortal, -1 /* level */),
|
||||
!kImmortal, false /* force_direct_prefetch */,
|
||||
-1 /* level */),
|
||||
std::move(file_reader), file_size, &table_reader,
|
||||
false /* prefetch_index_and_filter_in_cache */);
|
||||
if (!s.ok()) {
|
||||
|
||||
+3
-40
@@ -157,43 +157,6 @@ class CorruptionTest : public testing::Test {
|
||||
ASSERT_GE(max_expected, correct);
|
||||
}
|
||||
|
||||
void CorruptFile(const std::string& fname, int offset, int bytes_to_corrupt) {
|
||||
struct stat sbuf;
|
||||
if (stat(fname.c_str(), &sbuf) != 0) {
|
||||
const char* msg = strerror(errno);
|
||||
FAIL() << fname << ": " << msg;
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
// Relative to end of file; make it absolute
|
||||
if (-offset > sbuf.st_size) {
|
||||
offset = 0;
|
||||
} else {
|
||||
offset = static_cast<int>(sbuf.st_size + offset);
|
||||
}
|
||||
}
|
||||
if (offset > sbuf.st_size) {
|
||||
offset = static_cast<int>(sbuf.st_size);
|
||||
}
|
||||
if (offset + bytes_to_corrupt > sbuf.st_size) {
|
||||
bytes_to_corrupt = static_cast<int>(sbuf.st_size - offset);
|
||||
}
|
||||
|
||||
// Do it
|
||||
std::string contents;
|
||||
Status s = ReadFileToString(Env::Default(), fname, &contents);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
for (int i = 0; i < bytes_to_corrupt; i++) {
|
||||
contents[i + offset] ^= 0x80;
|
||||
}
|
||||
s = WriteStringToFile(Env::Default(), contents, fname);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
Options options;
|
||||
EnvOptions env_options;
|
||||
options.file_system.reset(new LegacyFileSystemWrapper(options.env));
|
||||
ASSERT_NOK(VerifySstFileChecksum(options, env_options, fname));
|
||||
}
|
||||
|
||||
void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
|
||||
// Pick file to corrupt
|
||||
std::vector<std::string> filenames;
|
||||
@@ -212,7 +175,7 @@ class CorruptionTest : public testing::Test {
|
||||
}
|
||||
ASSERT_TRUE(!fname.empty()) << filetype;
|
||||
|
||||
CorruptFile(fname, offset, bytes_to_corrupt);
|
||||
test::CorruptFile(fname, offset, bytes_to_corrupt);
|
||||
}
|
||||
|
||||
// corrupts exactly one file at level `level`. if no file found at level,
|
||||
@@ -222,7 +185,7 @@ class CorruptionTest : public testing::Test {
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
for (const auto& m : metadata) {
|
||||
if (m.level == level) {
|
||||
CorruptFile(dbname_ + "/" + m.name, offset, bytes_to_corrupt);
|
||||
test::CorruptFile(dbname_ + "/" + m.name, offset, bytes_to_corrupt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -557,7 +520,7 @@ TEST_F(CorruptionTest, RangeDeletionCorrupted) {
|
||||
ImmutableCFOptions(options_), kRangeDelBlock, &range_del_handle));
|
||||
|
||||
ASSERT_OK(TryReopen());
|
||||
CorruptFile(filename, static_cast<int>(range_del_handle.offset()), 1);
|
||||
test::CorruptFile(filename, static_cast<int>(range_del_handle.offset()), 1);
|
||||
ASSERT_TRUE(TryReopen().IsCorruption());
|
||||
}
|
||||
|
||||
|
||||
+1017
-471
File diff suppressed because it is too large
Load Diff
+137
-4
@@ -187,7 +187,7 @@ TEST_F(DBBlockCacheTest, TestWithoutCompressedBlockCache) {
|
||||
Iterator* iter = nullptr;
|
||||
|
||||
// Load blocks into cache.
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(i));
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -209,12 +209,12 @@ TEST_F(DBBlockCacheTest, TestWithoutCompressedBlockCache) {
|
||||
iter = nullptr;
|
||||
|
||||
// Release iterators and access cache again.
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iterators[i].reset();
|
||||
CheckCacheCounters(options, 0, 0, 0, 0);
|
||||
}
|
||||
ASSERT_EQ(0, cache->GetPinnedUsage());
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(i));
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -243,7 +243,7 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
Iterator* iter = nullptr;
|
||||
|
||||
// Load blocks into cache.
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(i));
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -517,6 +517,139 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksCachePriority) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// An LRUCache wrapper that can falsely report "not found" on Lookup.
|
||||
// This allows us to manipulate BlockBasedTableReader into thinking
|
||||
// another thread inserted the data in between Lookup and Insert,
|
||||
// while mostly preserving the LRUCache interface/behavior.
|
||||
class LookupLiarCache : public CacheWrapper {
|
||||
int nth_lookup_not_found_ = 0;
|
||||
|
||||
public:
|
||||
explicit LookupLiarCache(std::shared_ptr<Cache> target)
|
||||
: CacheWrapper(std::move(target)) {}
|
||||
|
||||
Handle* Lookup(const Slice& key, Statistics* stats) override {
|
||||
if (nth_lookup_not_found_ == 1) {
|
||||
nth_lookup_not_found_ = 0;
|
||||
return nullptr;
|
||||
}
|
||||
if (nth_lookup_not_found_ > 1) {
|
||||
--nth_lookup_not_found_;
|
||||
}
|
||||
return CacheWrapper::Lookup(key, stats);
|
||||
}
|
||||
|
||||
// 1 == next lookup, 2 == after next, etc.
|
||||
void SetNthLookupNotFound(int n) { nth_lookup_not_found_ = n; }
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBlockCacheTest, AddRedundantStats) {
|
||||
const size_t capacity = size_t{1} << 25;
|
||||
const int num_shard_bits = 0; // 1 shard
|
||||
int iterations_tested = 0;
|
||||
for (std::shared_ptr<Cache> base_cache :
|
||||
{NewLRUCache(capacity, num_shard_bits),
|
||||
NewClockCache(capacity, num_shard_bits)}) {
|
||||
if (!base_cache) {
|
||||
// Skip clock cache when not supported
|
||||
continue;
|
||||
}
|
||||
++iterations_tested;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
|
||||
std::shared_ptr<LookupLiarCache> cache =
|
||||
std::make_shared<LookupLiarCache>(base_cache);
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(50));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Create a new table.
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
// Normal access filter+index+data.
|
||||
ASSERT_EQ("value", Get("foo"));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Againt access filter+index+data, but force redundant load+insert on index
|
||||
cache->SetNthLookupNotFound(2);
|
||||
ASSERT_EQ("value", Get("bar"));
|
||||
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(4, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just filter (with high probability), and force redundant
|
||||
// load+insert
|
||||
cache->SetNthLookupNotFound(1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("this key was not added"));
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(5, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just data, forcing redundant load+insert
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<Iterator> iter{db_->NewIterator(read_options)};
|
||||
cache->SetNthLookupNotFound(1);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), "bar");
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(6, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
}
|
||||
EXPECT_GE(iterations_tested, 1);
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, ParanoidFileChecks) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
+211
-2
@@ -8,6 +8,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "table/block_based/filter_policy_internal.h"
|
||||
@@ -1029,6 +1030,215 @@ TEST_F(DBBloomFilterTest, MemtablePrefixBloomOutOfDomain) {
|
||||
ASSERT_EQ(kKey, iter->key());
|
||||
}
|
||||
|
||||
class DBBloomFilterTestVaryPrefixAndFormatVer
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, uint32_t>> {
|
||||
protected:
|
||||
bool use_prefix_;
|
||||
uint32_t format_version_;
|
||||
|
||||
public:
|
||||
DBBloomFilterTestVaryPrefixAndFormatVer()
|
||||
: DBTestBase("/db_bloom_filter_tests") {}
|
||||
|
||||
~DBBloomFilterTestVaryPrefixAndFormatVer() override {}
|
||||
|
||||
void SetUp() override {
|
||||
use_prefix_ = std::get<0>(GetParam());
|
||||
format_version_ = std::get<1>(GetParam());
|
||||
}
|
||||
|
||||
static std::string UKey(uint32_t i) { return Key(static_cast<int>(i)); }
|
||||
};
|
||||
|
||||
TEST_P(DBBloomFilterTestVaryPrefixAndFormatVer, PartitionedMultiGet) {
|
||||
Options options = CurrentOptions();
|
||||
if (use_prefix_) {
|
||||
// Entire key from UKey()
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(9));
|
||||
}
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
bbto.partition_filters = true;
|
||||
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
bbto.whole_key_filtering = !use_prefix_;
|
||||
if (use_prefix_) { // (not related to prefix, just alternating between)
|
||||
// Make sure code appropriately deals with metadata block size setting
|
||||
// that is "too small" (smaller than minimum size for filter builder)
|
||||
bbto.metadata_block_size = 63;
|
||||
} else {
|
||||
// Make sure the test will work even on platforms with large minimum
|
||||
// filter size, due to large cache line size.
|
||||
// (Largest cache line size + 10+% overhead.)
|
||||
bbto.metadata_block_size = 290;
|
||||
}
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
ReadOptions ropts;
|
||||
|
||||
constexpr uint32_t N = 12000;
|
||||
// Add N/2 evens
|
||||
for (uint32_t i = 0; i < N; i += 2) {
|
||||
ASSERT_OK(Put(UKey(i), UKey(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ(TotalTableFiles(), 1);
|
||||
#endif
|
||||
|
||||
constexpr uint32_t Q = 29;
|
||||
// MultiGet In
|
||||
std::array<std::string, Q> keys;
|
||||
std::array<Slice, Q> key_slices;
|
||||
std::array<ColumnFamilyHandle*, Q> column_families;
|
||||
// MultiGet Out
|
||||
std::array<Status, Q> statuses;
|
||||
std::array<PinnableSlice, Q> values;
|
||||
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_HIT);
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_USEFUL);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_POSITIVE);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_TRUE_POSITIVE);
|
||||
|
||||
// Check that initial clump of keys only loads one partition filter from
|
||||
// block cache.
|
||||
// And that spread out keys load many partition filters.
|
||||
// In both cases, mix present vs. not present keys.
|
||||
for (uint32_t stride : {uint32_t{1}, (N / Q) | 1}) {
|
||||
for (uint32_t i = 0; i < Q; ++i) {
|
||||
keys[i] = UKey(i * stride);
|
||||
key_slices[i] = Slice(keys[i]);
|
||||
column_families[i] = db_->DefaultColumnFamily();
|
||||
statuses[i] = Status();
|
||||
values[i] = PinnableSlice();
|
||||
}
|
||||
|
||||
db_->MultiGet(ropts, Q, &column_families[0], &key_slices[0], &values[0],
|
||||
/*timestamps=*/nullptr, &statuses[0], true);
|
||||
|
||||
// Confirm correct status results
|
||||
uint32_t number_not_found = 0;
|
||||
for (uint32_t i = 0; i < Q; ++i) {
|
||||
if ((i * stride % 2) == 0) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
} else {
|
||||
ASSERT_TRUE(statuses[i].IsNotFound());
|
||||
++number_not_found;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm correct Bloom stats (no FPs)
|
||||
uint64_t filter_useful = TestGetAndResetTickerCount(
|
||||
options,
|
||||
use_prefix_ ? BLOOM_FILTER_PREFIX_USEFUL : BLOOM_FILTER_USEFUL);
|
||||
uint64_t filter_checked =
|
||||
TestGetAndResetTickerCount(options, use_prefix_
|
||||
? BLOOM_FILTER_PREFIX_CHECKED
|
||||
: BLOOM_FILTER_FULL_POSITIVE) +
|
||||
(use_prefix_ ? 0 : filter_useful);
|
||||
EXPECT_EQ(filter_useful, number_not_found);
|
||||
EXPECT_EQ(filter_checked, Q);
|
||||
if (!use_prefix_) {
|
||||
EXPECT_EQ(
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_TRUE_POSITIVE),
|
||||
Q - number_not_found);
|
||||
}
|
||||
|
||||
// Confirm no duplicate loading same filter partition
|
||||
uint64_t filter_accesses =
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_HIT) +
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
if (stride == 1) {
|
||||
EXPECT_EQ(filter_accesses, 1);
|
||||
} else {
|
||||
// for large stride
|
||||
EXPECT_GE(filter_accesses, Q / 2 + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that a clump of keys (present and not) works when spanning
|
||||
// two partitions
|
||||
int found_spanning = 0;
|
||||
for (uint32_t start = 0; start < N / 2;) {
|
||||
for (uint32_t i = 0; i < Q; ++i) {
|
||||
keys[i] = UKey(start + i);
|
||||
key_slices[i] = Slice(keys[i]);
|
||||
column_families[i] = db_->DefaultColumnFamily();
|
||||
statuses[i] = Status();
|
||||
values[i] = PinnableSlice();
|
||||
}
|
||||
|
||||
db_->MultiGet(ropts, Q, &column_families[0], &key_slices[0], &values[0],
|
||||
/*timestamps=*/nullptr, &statuses[0], true);
|
||||
|
||||
// Confirm correct status results
|
||||
uint32_t number_not_found = 0;
|
||||
for (uint32_t i = 0; i < Q; ++i) {
|
||||
if (((start + i) % 2) == 0) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
} else {
|
||||
ASSERT_TRUE(statuses[i].IsNotFound());
|
||||
++number_not_found;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm correct Bloom stats (might see some FPs)
|
||||
uint64_t filter_useful = TestGetAndResetTickerCount(
|
||||
options,
|
||||
use_prefix_ ? BLOOM_FILTER_PREFIX_USEFUL : BLOOM_FILTER_USEFUL);
|
||||
uint64_t filter_checked =
|
||||
TestGetAndResetTickerCount(options, use_prefix_
|
||||
? BLOOM_FILTER_PREFIX_CHECKED
|
||||
: BLOOM_FILTER_FULL_POSITIVE) +
|
||||
(use_prefix_ ? 0 : filter_useful);
|
||||
EXPECT_GE(filter_useful, number_not_found - 2); // possible FP
|
||||
EXPECT_EQ(filter_checked, Q);
|
||||
if (!use_prefix_) {
|
||||
EXPECT_EQ(
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_TRUE_POSITIVE),
|
||||
Q - number_not_found);
|
||||
}
|
||||
|
||||
// Confirm no duplicate loading of same filter partition
|
||||
uint64_t filter_accesses =
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_HIT) +
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
if (filter_accesses == 2) {
|
||||
// Spanned across partitions.
|
||||
++found_spanning;
|
||||
if (found_spanning >= 2) {
|
||||
break;
|
||||
} else {
|
||||
// Ensure that at least once we have at least one present and
|
||||
// one non-present key on both sides of partition boundary.
|
||||
start += 2;
|
||||
}
|
||||
} else {
|
||||
EXPECT_EQ(filter_accesses, 1);
|
||||
// See explanation at "start += 2"
|
||||
start += Q - 4;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(found_spanning >= 2);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBBloomFilterTestVaryPrefixAndFormatVer,
|
||||
DBBloomFilterTestVaryPrefixAndFormatVer,
|
||||
::testing::Values(
|
||||
// (use_prefix, format_version)
|
||||
std::make_tuple(false, 2),
|
||||
std::make_tuple(false, 3),
|
||||
std::make_tuple(false, 4),
|
||||
std::make_tuple(false, 5),
|
||||
std::make_tuple(true, 2),
|
||||
std::make_tuple(true, 3),
|
||||
std::make_tuple(true, 4),
|
||||
std::make_tuple(true, 5)));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
namespace {
|
||||
namespace BFP2 {
|
||||
@@ -1343,8 +1553,7 @@ TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
|
||||
for (int i = 0; i < numkeys; i += 2) {
|
||||
keys.push_back(i);
|
||||
}
|
||||
std::random_shuffle(std::begin(keys), std::end(keys));
|
||||
|
||||
RandomShuffle(std::begin(keys), std::end(keys));
|
||||
int num_inserted = 0;
|
||||
for (int key : keys) {
|
||||
ASSERT_OK(Put(1, Key(key), "val"));
|
||||
|
||||
+140
-12
@@ -157,21 +157,21 @@ Options DeletionTriggerOptions(Options options) {
|
||||
bool HaveOverlappingKeyRanges(
|
||||
const Comparator* c,
|
||||
const SstFileMetaData& a, const SstFileMetaData& b) {
|
||||
if (c->Compare(a.smallestkey, b.smallestkey) >= 0) {
|
||||
if (c->Compare(a.smallestkey, b.largestkey) <= 0) {
|
||||
if (c->CompareWithoutTimestamp(a.smallestkey, b.smallestkey) >= 0) {
|
||||
if (c->CompareWithoutTimestamp(a.smallestkey, b.largestkey) <= 0) {
|
||||
// b.smallestkey <= a.smallestkey <= b.largestkey
|
||||
return true;
|
||||
}
|
||||
} else if (c->Compare(a.largestkey, b.smallestkey) >= 0) {
|
||||
} else if (c->CompareWithoutTimestamp(a.largestkey, b.smallestkey) >= 0) {
|
||||
// a.smallestkey < b.smallestkey <= a.largestkey
|
||||
return true;
|
||||
}
|
||||
if (c->Compare(a.largestkey, b.largestkey) <= 0) {
|
||||
if (c->Compare(a.largestkey, b.smallestkey) >= 0) {
|
||||
if (c->CompareWithoutTimestamp(a.largestkey, b.largestkey) <= 0) {
|
||||
if (c->CompareWithoutTimestamp(a.largestkey, b.smallestkey) >= 0) {
|
||||
// b.smallestkey <= a.largestkey <= b.largestkey
|
||||
return true;
|
||||
}
|
||||
} else if (c->Compare(a.smallestkey, b.largestkey) <= 0) {
|
||||
} else if (c->CompareWithoutTimestamp(a.smallestkey, b.largestkey) <= 0) {
|
||||
// a.smallestkey <= b.largestkey < a.largestkey
|
||||
return true;
|
||||
}
|
||||
@@ -593,6 +593,72 @@ TEST_P(DBCompactionTestWithParam, CompactionDeletionTriggerReopen) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactRangeBottomPri) {
|
||||
ASSERT_OK(Put(Key(50), ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put(Key(100), ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put(Key(200), ""));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
{
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 2;
|
||||
dbfull()->CompactRange(cro, nullptr, nullptr);
|
||||
}
|
||||
ASSERT_EQ("0,0,3", FilesPerLevel(0));
|
||||
|
||||
ASSERT_OK(Put(Key(1), ""));
|
||||
ASSERT_OK(Put(Key(199), ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put(Key(2), ""));
|
||||
ASSERT_OK(Put(Key(199), ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("2,0,3", FilesPerLevel(0));
|
||||
|
||||
// Now we have 2 L0 files, and 3 L2 files, and a manual compaction will
|
||||
// be triggered.
|
||||
// Two compaction jobs will run. One compacts 2 L0 files in Low Pri Pool
|
||||
// and one compact to L2 in bottom pri pool.
|
||||
int low_pri_count = 0;
|
||||
int bottom_pri_count = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"ThreadPoolImpl::Impl::BGThread:BeforeRun", [&](void* arg) {
|
||||
Env::Priority* pri = reinterpret_cast<Env::Priority*>(arg);
|
||||
// First time is low pri pool in the test case.
|
||||
if (low_pri_count == 0 && bottom_pri_count == 0) {
|
||||
ASSERT_EQ(Env::Priority::LOW, *pri);
|
||||
}
|
||||
if (*pri == Env::Priority::LOW) {
|
||||
low_pri_count++;
|
||||
} else {
|
||||
bottom_pri_count++;
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
env_->SetBackgroundThreads(1, Env::Priority::BOTTOM);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ(1, low_pri_count);
|
||||
ASSERT_EQ(1, bottom_pri_count);
|
||||
ASSERT_EQ("0,0,2", FilesPerLevel(0));
|
||||
|
||||
// Recompact bottom most level uses bottom pool
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
dbfull()->CompactRange(cro, nullptr, nullptr);
|
||||
ASSERT_EQ(1, low_pri_count);
|
||||
ASSERT_EQ(2, bottom_pri_count);
|
||||
|
||||
env_->SetBackgroundThreads(0, Env::Priority::BOTTOM);
|
||||
dbfull()->CompactRange(cro, nullptr, nullptr);
|
||||
// Low pri pool is used if bottom pool has size 0.
|
||||
ASSERT_EQ(2, low_pri_count);
|
||||
ASSERT_EQ(2, bottom_pri_count);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableStatsUpdateReopen) {
|
||||
uint64_t db_size[3];
|
||||
for (int test = 0; test < 2; ++test) {
|
||||
@@ -2459,7 +2525,7 @@ TEST_P(DBCompactionTestWithParam, ManualCompaction) {
|
||||
ASSERT_EQ("1,1,1", FilesPerLevel(1));
|
||||
|
||||
// Compaction range overlaps files
|
||||
Compact(1, "p1", "p9");
|
||||
Compact(1, "p", "q");
|
||||
ASSERT_EQ("0,0,1", FilesPerLevel(1));
|
||||
|
||||
// Populate a different range
|
||||
@@ -2526,7 +2592,7 @@ TEST_P(DBCompactionTestWithParam, ManualLevelCompactionOutputPathId) {
|
||||
ASSERT_EQ("3", FilesPerLevel(1));
|
||||
|
||||
// Compaction range overlaps files
|
||||
Compact(1, "p1", "p9", 1);
|
||||
Compact(1, "p", "q", 1);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,1", FilesPerLevel(1));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
@@ -3118,6 +3184,15 @@ TEST_P(DBCompactionTestWithParam, IntraL0Compaction) {
|
||||
options.level0_file_num_compaction_trigger = 5;
|
||||
options.max_background_compactions = 2;
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.write_buffer_size = 2 << 20; // 2MB
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewLRUCache(64 << 20); // 64MB
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const size_t kValueSize = 1 << 20;
|
||||
@@ -3148,6 +3223,7 @@ TEST_P(DBCompactionTestWithParam, IntraL0Compaction) {
|
||||
ASSERT_OK(Put(Key(i + 1), value));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(i + 1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -3162,6 +3238,16 @@ TEST_P(DBCompactionTestWithParam, IntraL0Compaction) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
ASSERT_GE(level_to_files[0][i].fd.file_size, 1 << 21);
|
||||
}
|
||||
|
||||
// The index/filter in the file produced by intra-L0 should not be pinned.
|
||||
// That means clearing unref'd entries in block cache and re-accessing the
|
||||
// file produced by intra-L0 should bump the index block miss count.
|
||||
uint64_t prev_index_misses =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS);
|
||||
table_options.block_cache->EraseUnRefEntries();
|
||||
ASSERT_EQ("", Get(Key(0)));
|
||||
ASSERT_EQ(prev_index_misses + 1,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, IntraL0CompactionDoesNotObsoleteDeletions) {
|
||||
@@ -4655,7 +4741,7 @@ TEST_P(DBCompactionDirectIOTest, DirectIO) {
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
MakeTables(3, "p", "q", 1);
|
||||
ASSERT_EQ("1,1,1", FilesPerLevel(1));
|
||||
Compact(1, "p1", "p9");
|
||||
Compact(1, "p", "q");
|
||||
ASSERT_EQ(readahead, options.use_direct_reads);
|
||||
ASSERT_EQ("0,0,1", FilesPerLevel(1));
|
||||
Destroy(options);
|
||||
@@ -4696,7 +4782,7 @@ TEST_P(CompactionPriTest, Test) {
|
||||
for (int i = 0; i < kNKeys; i++) {
|
||||
keys[i] = i;
|
||||
}
|
||||
std::random_shuffle(std::begin(keys), std::end(keys));
|
||||
RandomShuffle(std::begin(keys), std::end(keys), rnd.Next());
|
||||
|
||||
for (int i = 0; i < kNKeys; i++) {
|
||||
ASSERT_OK(Put(Key(keys[i]), RandomString(&rnd, 102)));
|
||||
@@ -4958,10 +5044,11 @@ TEST_P(DBCompactionTestWithParam, FixFileIngestionCompactionDeadlock) {
|
||||
|
||||
TEST_F(DBCompactionTest, ConsistencyFailTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.force_consistency_checks = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistency", [&](void* arg) {
|
||||
"VersionBuilder::CheckConsistency0", [&](void* arg) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
// just swap the two FileMetaData so that we hit error
|
||||
@@ -4980,6 +5067,48 @@ TEST_F(DBCompactionTest, ConsistencyFailTest) {
|
||||
|
||||
ASSERT_NOK(Put("foo", "bar"));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ConsistencyFailTest2) {
|
||||
Options options = CurrentOptions();
|
||||
options.force_consistency_checks = true;
|
||||
options.target_file_size_base = 1000;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = 400; // small block size
|
||||
options.table_factory.reset(new BlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistency1", [&](void* arg) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
// just swap the two FileMetaData so that we hit error
|
||||
// in CheckConsistency funcion
|
||||
FileMetaData* temp = *(p->first);
|
||||
*(p->first) = *(p->second);
|
||||
*(p->second) = temp;
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
std::string value = RandomString(&rnd, 1000);
|
||||
|
||||
ASSERT_OK(Put("foo1", value));
|
||||
ASSERT_OK(Put("z", ""));
|
||||
Flush();
|
||||
ASSERT_OK(Put("foo2", value));
|
||||
ASSERT_OK(Put("z", ""));
|
||||
Flush();
|
||||
|
||||
// This probably returns non-OK, but we rely on the next Put()
|
||||
// to determine the DB is frozen.
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_NOK(Put("foo", "bar"));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
void IngestOneKeyValue(DBImpl* db, const std::string& key,
|
||||
@@ -5054,7 +5183,6 @@ TEST_P(DBCompactionTestWithParam,
|
||||
// Put one key, to make biggest log sequence number in this memtable is bigger
|
||||
// than sst which would be ingested in next step.
|
||||
ASSERT_OK(Put(Key(2), "b"));
|
||||
ASSERT_EQ(10, NumTableFilesAtLevel(0));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
std::vector<std::vector<FileMetaData>> level_to_files;
|
||||
|
||||
@@ -50,7 +50,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase) {
|
||||
keys[i] = i;
|
||||
}
|
||||
if (ordered_insert == 0) {
|
||||
std::random_shuffle(std::begin(keys), std::end(keys));
|
||||
RandomShuffle(std::begin(keys), std::end(keys), rnd.Next());
|
||||
}
|
||||
for (int max_background_compactions = 1; max_background_compactions < 4;
|
||||
max_background_compactions += 2) {
|
||||
|
||||
+21
-10
@@ -90,6 +90,9 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
mutex_.Unlock();
|
||||
status = AtomicFlushMemTables(cfds, FlushOptions(),
|
||||
FlushReason::kGetLiveFiles);
|
||||
if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
@@ -103,8 +106,10 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:2");
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (!status.ok()) {
|
||||
if (!status.ok() && !status.IsColumnFamilyDropped()) {
|
||||
break;
|
||||
} else if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,27 +123,33 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
}
|
||||
}
|
||||
|
||||
// Make a set of all of the live *.sst files
|
||||
std::vector<FileDescriptor> live;
|
||||
// Make a set of all of the live table and blob files
|
||||
std::vector<uint64_t> live_table_files;
|
||||
std::vector<uint64_t> live_blob_files;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->current()->AddLiveFiles(&live);
|
||||
cfd->current()->AddLiveFiles(&live_table_files, &live_blob_files);
|
||||
}
|
||||
|
||||
ret.clear();
|
||||
ret.reserve(live.size() + 3); // *.sst + CURRENT + MANIFEST + OPTIONS
|
||||
ret.reserve(live_table_files.size() + live_blob_files.size() +
|
||||
3); // for CURRENT + MANIFEST + OPTIONS
|
||||
|
||||
// create names of the live files. The names are not absolute
|
||||
// paths, instead they are relative to dbname_;
|
||||
for (const auto& live_file : live) {
|
||||
ret.push_back(MakeTableFileName("", live_file.GetNumber()));
|
||||
for (const auto& table_file_number : live_table_files) {
|
||||
ret.emplace_back(MakeTableFileName("", table_file_number));
|
||||
}
|
||||
|
||||
ret.push_back(CurrentFileName(""));
|
||||
ret.push_back(DescriptorFileName("", versions_->manifest_file_number()));
|
||||
ret.push_back(OptionsFileName("", versions_->options_file_number()));
|
||||
for (const auto& blob_file_number : live_blob_files) {
|
||||
ret.emplace_back(BlobFileName("", blob_file_number));
|
||||
}
|
||||
|
||||
ret.emplace_back(CurrentFileName(""));
|
||||
ret.emplace_back(DescriptorFileName("", versions_->manifest_file_number()));
|
||||
ret.emplace_back(OptionsFileName("", versions_->options_file_number()));
|
||||
|
||||
// find length of manifest file while holding the mutex lock
|
||||
*manifest_file_size = versions_->manifest_file_size();
|
||||
|
||||
+2
-2
@@ -499,13 +499,13 @@ TEST_P(DBAtomicFlushTest, AtomicFlushTriggeredByMemTableFull) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBAtomicFlushTest::AtomicFlushTriggeredByMemTableFull:BeforeCheck");
|
||||
if (options.atomic_flush) {
|
||||
for (size_t i = 0; i != num_cfs - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 != num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i != num_cfs - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 != num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_FALSE(cfh->cfd()->mem()->IsEmpty());
|
||||
|
||||
+338
-109
@@ -21,7 +21,6 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -151,7 +150,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
own_info_log_(options.info_log == nullptr),
|
||||
initial_db_options_(SanitizeOptions(dbname, options)),
|
||||
env_(initial_db_options_.env),
|
||||
fs_(initial_db_options_.file_system),
|
||||
fs_(initial_db_options_.env->GetFileSystem()),
|
||||
immutable_db_options_(initial_db_options_),
|
||||
mutable_db_options_(initial_db_options_),
|
||||
stats_(immutable_db_options_.statistics.get()),
|
||||
@@ -250,7 +249,8 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
new ColumnFamilyMemTablesImpl(versions_->GetColumnFamilySet()));
|
||||
|
||||
DumpRocksDBBuildVersion(immutable_db_options_.info_log.get());
|
||||
DumpDBFileSummary(immutable_db_options_, dbname_);
|
||||
SetDbSessionId();
|
||||
DumpDBFileSummary(immutable_db_options_, dbname_, db_session_id_);
|
||||
immutable_db_options_.Dump(immutable_db_options_.info_log.get());
|
||||
mutable_db_options_.Dump(immutable_db_options_.info_log.get());
|
||||
DumpSupportInfo(immutable_db_options_.info_log.get());
|
||||
@@ -312,6 +312,11 @@ Status DBImpl::ResumeImpl() {
|
||||
s = bg_error;
|
||||
}
|
||||
|
||||
// Make sure the IO Status stored in version set is set to OK.
|
||||
if (s.ok()) {
|
||||
versions_->SetIOStatusOK();
|
||||
}
|
||||
|
||||
// We cannot guarantee consistency of the WAL. So force flush Memtables of
|
||||
// all the column families
|
||||
if (s.ok()) {
|
||||
@@ -866,9 +871,7 @@ void DBImpl::DumpStats() {
|
||||
Status DBImpl::TablesRangeTombstoneSummary(ColumnFamilyHandle* column_family,
|
||||
int max_entries_to_print,
|
||||
std::string* out_str) {
|
||||
auto* cfh =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl, ColumnFamilyHandle>(
|
||||
column_family);
|
||||
auto* cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
@@ -890,9 +893,9 @@ void DBImpl::ScheduleBgLogWriterClose(JobContext* job_context) {
|
||||
}
|
||||
}
|
||||
|
||||
Directory* DBImpl::GetDataDir(ColumnFamilyData* cfd, size_t path_id) const {
|
||||
FSDirectory* DBImpl::GetDataDir(ColumnFamilyData* cfd, size_t path_id) const {
|
||||
assert(cfd);
|
||||
Directory* ret_dir = cfd->GetDataDir(path_id);
|
||||
FSDirectory* ret_dir = cfd->GetDataDir(path_id);
|
||||
if (ret_dir == nullptr) {
|
||||
return directories_.GetDataDir(path_id);
|
||||
}
|
||||
@@ -1006,12 +1009,12 @@ Status DBImpl::SetDBOptions(
|
||||
}
|
||||
if (s.ok()) {
|
||||
const BGJobLimits current_bg_job_limits =
|
||||
GetBGJobLimits(immutable_db_options_.max_background_flushes,
|
||||
GetBGJobLimits(mutable_db_options_.max_background_flushes,
|
||||
mutable_db_options_.max_background_compactions,
|
||||
mutable_db_options_.max_background_jobs,
|
||||
/* parallelize_compactions */ true);
|
||||
const BGJobLimits new_bg_job_limits = GetBGJobLimits(
|
||||
immutable_db_options_.max_background_flushes,
|
||||
new_options.max_background_flushes,
|
||||
new_options.max_background_compactions,
|
||||
new_options.max_background_jobs, /* parallelize_compactions */ true);
|
||||
|
||||
@@ -1147,25 +1150,25 @@ int DBImpl::FindMinimumEmptyLevelFitting(
|
||||
|
||||
Status DBImpl::FlushWAL(bool sync) {
|
||||
if (manual_wal_flush_) {
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
{
|
||||
// We need to lock log_write_mutex_ since logs_ might change concurrently
|
||||
InstrumentedMutexLock wl(&log_write_mutex_);
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
s = cur_log_writer->WriteBuffer();
|
||||
io_s = cur_log_writer->WriteBuffer();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
if (!io_s.ok()) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log, "WAL flush error %s",
|
||||
s.ToString().c_str());
|
||||
io_s.ToString().c_str());
|
||||
// In case there is a fs error we should set it globally to prevent the
|
||||
// future writes
|
||||
WriteStatusCheck(s);
|
||||
IOStatusCheck(io_s);
|
||||
// whether sync or not, we should abort the rest of function upon error
|
||||
return s;
|
||||
return std::move(io_s);
|
||||
}
|
||||
if (!sync) {
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "FlushWAL sync=false");
|
||||
return s;
|
||||
return std::move(io_s);
|
||||
}
|
||||
}
|
||||
if (!sync) {
|
||||
@@ -1217,14 +1220,23 @@ Status DBImpl::SyncWAL() {
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:1");
|
||||
RecordTick(stats_, WAL_FILE_SYNCED);
|
||||
Status status;
|
||||
IOStatus io_s;
|
||||
for (log::Writer* log : logs_to_sync) {
|
||||
status = log->file()->SyncWithoutFlush(immutable_db_options_.use_fsync);
|
||||
if (!status.ok()) {
|
||||
io_s = log->file()->SyncWithoutFlush(immutable_db_options_.use_fsync);
|
||||
if (!io_s.ok()) {
|
||||
status = io_s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log, "WAL Sync error %s",
|
||||
io_s.ToString().c_str());
|
||||
// In case there is a fs error we should set it globally to prevent the
|
||||
// future writes
|
||||
IOStatusCheck(io_s);
|
||||
}
|
||||
if (status.ok() && need_log_dir_sync) {
|
||||
status = directories_.GetWalDir()->Fsync();
|
||||
status = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
|
||||
}
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:2");
|
||||
|
||||
@@ -1249,7 +1261,7 @@ Status DBImpl::LockWAL() {
|
||||
// future writes
|
||||
WriteStatusCheck(status);
|
||||
}
|
||||
return status;
|
||||
return std::move(status);
|
||||
}
|
||||
|
||||
Status DBImpl::UnlockWAL() {
|
||||
@@ -1300,7 +1312,7 @@ bool DBImpl::SetPreserveDeletesSequenceNumber(SequenceNumber seqnum) {
|
||||
|
||||
InternalIterator* DBImpl::NewInternalIterator(
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
ColumnFamilyHandle* column_family) {
|
||||
ColumnFamilyHandle* column_family, bool allow_unprepared_value) {
|
||||
ColumnFamilyData* cfd;
|
||||
if (column_family == nullptr) {
|
||||
cfd = default_cf_handle_->cfd();
|
||||
@@ -1314,7 +1326,7 @@ InternalIterator* DBImpl::NewInternalIterator(
|
||||
mutex_.Unlock();
|
||||
ReadOptions roptions;
|
||||
return NewInternalIterator(roptions, cfd, super_version, arena, range_del_agg,
|
||||
sequence);
|
||||
sequence, allow_unprepared_value);
|
||||
}
|
||||
|
||||
void DBImpl::SchedulePurge() {
|
||||
@@ -1437,7 +1449,8 @@ InternalIterator* DBImpl::NewInternalIterator(const ReadOptions& read_options,
|
||||
SuperVersion* super_version,
|
||||
Arena* arena,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber sequence) {
|
||||
SequenceNumber sequence,
|
||||
bool allow_unprepared_value) {
|
||||
InternalIterator* internal_iter;
|
||||
assert(arena != nullptr);
|
||||
assert(range_del_agg != nullptr);
|
||||
@@ -1469,7 +1482,8 @@ InternalIterator* DBImpl::NewInternalIterator(const ReadOptions& read_options,
|
||||
// Collect iterators for files in L0 - Ln
|
||||
if (read_options.read_tier != kMemtableTier) {
|
||||
super_version->current->AddIterators(read_options, file_options_,
|
||||
&merge_iter_builder, range_del_agg);
|
||||
&merge_iter_builder, range_del_agg,
|
||||
allow_unprepared_value);
|
||||
}
|
||||
internal_iter = merge_iter_builder.Finish();
|
||||
IterState* cleanup =
|
||||
@@ -1496,16 +1510,38 @@ ColumnFamilyHandle* DBImpl::PersistentStatsColumnFamily() const {
|
||||
Status DBImpl::Get(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) {
|
||||
return Get(read_options, column_family, key, value, /*timestamp=*/nullptr);
|
||||
}
|
||||
|
||||
Status DBImpl::Get(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value, std::string* timestamp) {
|
||||
GetImplOptions get_impl_options;
|
||||
get_impl_options.column_family = column_family;
|
||||
get_impl_options.value = value;
|
||||
return GetImpl(read_options, key, get_impl_options);
|
||||
get_impl_options.timestamp = timestamp;
|
||||
Status s = GetImpl(read_options, key, get_impl_options);
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
GetImplOptions get_impl_options) {
|
||||
GetImplOptions& get_impl_options) {
|
||||
assert(get_impl_options.value != nullptr ||
|
||||
get_impl_options.merge_operands != nullptr);
|
||||
|
||||
#ifndef NDEBUG
|
||||
assert(get_impl_options.column_family);
|
||||
ColumnFamilyHandle* cf = get_impl_options.column_family;
|
||||
const Comparator* const ucmp = cf->GetComparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() > 0) {
|
||||
assert(read_options.timestamp);
|
||||
assert(read_options.timestamp->size() == ucmp->timestamp_size());
|
||||
} else {
|
||||
assert(!read_options.timestamp);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, env_);
|
||||
StopWatch sw(env_, stats_, DB_GET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
@@ -1583,10 +1619,14 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
bool skip_memtable = (read_options.read_tier == kPersistedTier &&
|
||||
has_unpersisted_data_.load(std::memory_order_relaxed));
|
||||
bool done = false;
|
||||
const Comparator* comparator =
|
||||
get_impl_options.column_family->GetComparator();
|
||||
size_t ts_sz = comparator->timestamp_size();
|
||||
std::string* timestamp = ts_sz > 0 ? get_impl_options.timestamp : nullptr;
|
||||
if (!skip_memtable) {
|
||||
// Get value associated with key
|
||||
if (get_impl_options.get_value) {
|
||||
if (sv->mem->Get(lkey, get_impl_options.value->GetSelf(), &s,
|
||||
if (sv->mem->Get(lkey, get_impl_options.value->GetSelf(), timestamp, &s,
|
||||
&merge_context, &max_covering_tombstone_seq,
|
||||
read_options, get_impl_options.callback,
|
||||
get_impl_options.is_blob_index)) {
|
||||
@@ -1594,9 +1634,10 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
get_impl_options.value->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if ((s.ok() || s.IsMergeInProgress()) &&
|
||||
sv->imm->Get(lkey, get_impl_options.value->GetSelf(), &s,
|
||||
&merge_context, &max_covering_tombstone_seq,
|
||||
read_options, get_impl_options.callback,
|
||||
sv->imm->Get(lkey, get_impl_options.value->GetSelf(),
|
||||
timestamp, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options,
|
||||
get_impl_options.callback,
|
||||
get_impl_options.is_blob_index)) {
|
||||
done = true;
|
||||
get_impl_options.value->PinSelf();
|
||||
@@ -1605,9 +1646,9 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
} else {
|
||||
// Get Merge Operands associated with key, Merge Operands should not be
|
||||
// merged and raw values should be returned to the user.
|
||||
if (sv->mem->Get(lkey, nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options, nullptr,
|
||||
nullptr, false)) {
|
||||
if (sv->mem->Get(lkey, /*value*/ nullptr, /*timestamp=*/nullptr, &s,
|
||||
&merge_context, &max_covering_tombstone_seq,
|
||||
read_options, nullptr, nullptr, false)) {
|
||||
done = true;
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if ((s.ok() || s.IsMergeInProgress()) &&
|
||||
@@ -1626,8 +1667,8 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
if (!done) {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
sv->current->Get(
|
||||
read_options, lkey, get_impl_options.value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq,
|
||||
read_options, lkey, get_impl_options.value, timestamp, &s,
|
||||
&merge_context, &max_covering_tombstone_seq,
|
||||
get_impl_options.get_value ? get_impl_options.value_found : nullptr,
|
||||
nullptr, nullptr,
|
||||
get_impl_options.get_value ? get_impl_options.callback : nullptr,
|
||||
@@ -1675,12 +1716,34 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values) {
|
||||
return MultiGet(read_options, column_family, keys, values,
|
||||
/*timestamps=*/nullptr);
|
||||
}
|
||||
|
||||
std::vector<Status> DBImpl::MultiGet(
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values,
|
||||
std::vector<std::string>* timestamps) {
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, env_);
|
||||
StopWatch sw(env_, stats_, DB_MULTIGET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (const auto* cfh : column_family) {
|
||||
assert(cfh);
|
||||
const Comparator* const ucmp = cfh->GetComparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() > 0) {
|
||||
assert(read_options.timestamp);
|
||||
assert(ucmp->timestamp_size() == read_options.timestamp->size());
|
||||
} else {
|
||||
assert(!read_options.timestamp);
|
||||
}
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
SequenceNumber consistent_seqnum;
|
||||
;
|
||||
|
||||
std::unordered_map<uint32_t, MultiGetColumnFamilyData> multiget_cf_data(
|
||||
column_family.size());
|
||||
@@ -1711,6 +1774,9 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
size_t num_keys = keys.size();
|
||||
std::vector<Status> stat_list(num_keys);
|
||||
values->resize(num_keys);
|
||||
if (timestamps) {
|
||||
timestamps->resize(num_keys);
|
||||
}
|
||||
|
||||
// Keep track of bytes that we read for statistics-recording later
|
||||
uint64_t bytes_read = 0;
|
||||
@@ -1721,13 +1787,17 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
// s is both in/out. When in, s could either be OK or MergeInProgress.
|
||||
// merge_operands will contain the sequence of merges in the latter case.
|
||||
size_t num_found = 0;
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
size_t keys_read;
|
||||
uint64_t curr_value_size = 0;
|
||||
for (keys_read = 0; keys_read < num_keys; ++keys_read) {
|
||||
merge_context.Clear();
|
||||
Status& s = stat_list[i];
|
||||
std::string* value = &(*values)[i];
|
||||
Status& s = stat_list[keys_read];
|
||||
std::string* value = &(*values)[keys_read];
|
||||
std::string* timestamp = timestamps ? &(*timestamps)[keys_read] : nullptr;
|
||||
|
||||
LookupKey lkey(keys[i], consistent_seqnum);
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family[i]);
|
||||
LookupKey lkey(keys[keys_read], consistent_seqnum, read_options.timestamp);
|
||||
auto cfh =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(column_family[keys_read]);
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
auto mgd_iter = multiget_cf_data.find(cfh->cfd()->GetID());
|
||||
assert(mgd_iter != multiget_cf_data.end());
|
||||
@@ -1738,13 +1808,13 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
has_unpersisted_data_.load(std::memory_order_relaxed));
|
||||
bool done = false;
|
||||
if (!skip_memtable) {
|
||||
if (super_version->mem->Get(lkey, value, &s, &merge_context,
|
||||
if (super_version->mem->Get(lkey, value, timestamp, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options)) {
|
||||
done = true;
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if (super_version->imm->Get(lkey, value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq,
|
||||
read_options)) {
|
||||
} else if (super_version->imm->Get(
|
||||
lkey, value, timestamp, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options)) {
|
||||
done = true;
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
}
|
||||
@@ -1752,8 +1822,9 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
if (!done) {
|
||||
PinnableSlice pinnable_val;
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
super_version->current->Get(read_options, lkey, &pinnable_val, &s,
|
||||
&merge_context, &max_covering_tombstone_seq);
|
||||
super_version->current->Get(read_options, lkey, &pinnable_val, timestamp,
|
||||
&s, &merge_context,
|
||||
&max_covering_tombstone_seq);
|
||||
value->assign(pinnable_val.data(), pinnable_val.size());
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
@@ -1761,6 +1832,29 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
if (s.ok()) {
|
||||
bytes_read += value->size();
|
||||
num_found++;
|
||||
curr_value_size += value->size();
|
||||
if (curr_value_size > read_options.value_size_soft_limit) {
|
||||
while (++keys_read < num_keys) {
|
||||
stat_list[keys_read] = Status::Aborted();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (read_options.deadline.count() &&
|
||||
env_->NowMicros() >
|
||||
static_cast<uint64_t>(read_options.deadline.count())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (keys_read < num_keys) {
|
||||
// The only reason to break out of the loop is when the deadline is
|
||||
// exceeded
|
||||
assert(env_->NowMicros() >
|
||||
static_cast<uint64_t>(read_options.deadline.count()));
|
||||
for (++keys_read; keys_read < num_keys; ++keys_read) {
|
||||
stat_list[keys_read] = Status::TimedOut();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1915,14 +2009,39 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, Status* statuses,
|
||||
const bool sorted_input) {
|
||||
return MultiGet(read_options, num_keys, column_families, keys, values,
|
||||
/*timestamps=*/nullptr, statuses, sorted_input);
|
||||
}
|
||||
|
||||
void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, std::string* timestamps,
|
||||
Status* statuses, const bool sorted_input) {
|
||||
if (num_keys == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
ColumnFamilyHandle* cfh = column_families[i];
|
||||
assert(cfh);
|
||||
const Comparator* const ucmp = cfh->GetComparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() > 0) {
|
||||
assert(read_options.timestamp);
|
||||
assert(read_options.timestamp->size() == ucmp->timestamp_size());
|
||||
} else {
|
||||
assert(!read_options.timestamp);
|
||||
}
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
autovector<KeyContext, MultiGetContext::MAX_BATCH_SIZE> key_context;
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE> sorted_keys;
|
||||
sorted_keys.resize(num_keys);
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
key_context.emplace_back(column_families[i], keys[i], &values[i],
|
||||
timestamps ? ×tamps[i] : nullptr,
|
||||
&statuses[i]);
|
||||
}
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
@@ -1963,14 +2082,31 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
read_options, nullptr, iter_deref_lambda, &multiget_cf_data,
|
||||
&consistent_seqnum);
|
||||
|
||||
for (auto cf_iter = multiget_cf_data.begin();
|
||||
cf_iter != multiget_cf_data.end(); ++cf_iter) {
|
||||
MultiGetImpl(read_options, cf_iter->start, cf_iter->num_keys, &sorted_keys,
|
||||
cf_iter->super_version, consistent_seqnum, nullptr, nullptr);
|
||||
Status s;
|
||||
auto cf_iter = multiget_cf_data.begin();
|
||||
for (; cf_iter != multiget_cf_data.end(); ++cf_iter) {
|
||||
s = MultiGetImpl(read_options, cf_iter->start, cf_iter->num_keys,
|
||||
&sorted_keys, cf_iter->super_version, consistent_seqnum,
|
||||
nullptr, nullptr);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
assert(s.IsTimedOut() || s.IsAborted());
|
||||
for (++cf_iter; cf_iter != multiget_cf_data.end(); ++cf_iter) {
|
||||
for (size_t i = cf_iter->start; i < cf_iter->start + cf_iter->num_keys;
|
||||
++i) {
|
||||
*sorted_keys[i]->s = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& iter : multiget_cf_data) {
|
||||
if (!unref_only) {
|
||||
ReturnAndCleanupSuperVersion(cf_iter->cfd, cf_iter->super_version);
|
||||
ReturnAndCleanupSuperVersion(iter.cfd, iter.super_version);
|
||||
} else {
|
||||
cf_iter->cfd->GetSuperVersion()->Unref();
|
||||
iter.cfd->GetSuperVersion()->Unref();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1993,7 +2129,8 @@ struct CompareKeyContext {
|
||||
}
|
||||
|
||||
// Both keys are from the same column family
|
||||
int cmp = comparator->Compare(*(lhs->key), *(rhs->key));
|
||||
int cmp = comparator->CompareWithoutTimestamp(
|
||||
*(lhs->key), /*a_has_ts=*/false, *(rhs->key), /*b_has_ts=*/false);
|
||||
if (cmp < 0) {
|
||||
return true;
|
||||
}
|
||||
@@ -2025,7 +2162,8 @@ void DBImpl::PrepareMultiGetKeys(
|
||||
}
|
||||
|
||||
// Both keys are from the same column family
|
||||
int cmp = comparator->Compare(*(lhs->key), *(rhs->key));
|
||||
int cmp = comparator->CompareWithoutTimestamp(
|
||||
*(lhs->key), /*a_has_ts=*/false, *(rhs->key), /*b_has_ts=*/false);
|
||||
assert(cmp <= 0);
|
||||
}
|
||||
index++;
|
||||
@@ -2043,11 +2181,22 @@ void DBImpl::MultiGet(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const size_t num_keys,
|
||||
const Slice* keys, PinnableSlice* values,
|
||||
Status* statuses, const bool sorted_input) {
|
||||
return MultiGet(read_options, column_family, num_keys, keys, values,
|
||||
/*timestamp=*/nullptr, statuses, sorted_input);
|
||||
}
|
||||
|
||||
void DBImpl::MultiGet(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const size_t num_keys,
|
||||
const Slice* keys, PinnableSlice* values,
|
||||
std::string* timestamps, Status* statuses,
|
||||
const bool sorted_input) {
|
||||
autovector<KeyContext, MultiGetContext::MAX_BATCH_SIZE> key_context;
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE> sorted_keys;
|
||||
sorted_keys.resize(num_keys);
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
key_context.emplace_back(column_family, keys[i], &values[i], &statuses[i]);
|
||||
key_context.emplace_back(column_family, keys[i], &values[i],
|
||||
timestamps ? ×tamps[i] : nullptr,
|
||||
&statuses[i]);
|
||||
}
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
sorted_keys[i] = &key_context[i];
|
||||
@@ -2100,14 +2249,24 @@ void DBImpl::MultiGetWithCallback(
|
||||
consistent_seqnum = callback->max_visible_seq();
|
||||
}
|
||||
|
||||
MultiGetImpl(read_options, 0, num_keys, sorted_keys,
|
||||
multiget_cf_data[0].super_version, consistent_seqnum, nullptr,
|
||||
nullptr);
|
||||
Status s = MultiGetImpl(read_options, 0, num_keys, sorted_keys,
|
||||
multiget_cf_data[0].super_version, consistent_seqnum,
|
||||
nullptr, nullptr);
|
||||
assert(s.ok() || s.IsTimedOut() || s.IsAborted());
|
||||
ReturnAndCleanupSuperVersion(multiget_cf_data[0].cfd,
|
||||
multiget_cf_data[0].super_version);
|
||||
}
|
||||
|
||||
void DBImpl::MultiGetImpl(
|
||||
// The actual implementation of batched MultiGet. Parameters -
|
||||
// start_key - Index in the sorted_keys vector to start processing from
|
||||
// num_keys - Number of keys to lookup, starting with sorted_keys[start_key]
|
||||
// sorted_keys - The entire batch of sorted keys for this CF
|
||||
//
|
||||
// The per key status is returned in the KeyContext structures pointed to by
|
||||
// sorted_keys. An overall Status is also returned, with the only possible
|
||||
// values being Status::OK() and Status::TimedOut(). The latter indicates
|
||||
// that the call exceeded read_options.deadline
|
||||
Status DBImpl::MultiGetImpl(
|
||||
const ReadOptions& read_options, size_t start_key, size_t num_keys,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys,
|
||||
SuperVersion* super_version, SequenceNumber snapshot,
|
||||
@@ -2120,13 +2279,23 @@ void DBImpl::MultiGetImpl(
|
||||
// s is both in/out. When in, s could either be OK or MergeInProgress.
|
||||
// merge_operands will contain the sequence of merges in the latter case.
|
||||
size_t keys_left = num_keys;
|
||||
Status s;
|
||||
uint64_t curr_value_size = 0;
|
||||
while (keys_left) {
|
||||
if (read_options.deadline.count() &&
|
||||
env_->NowMicros() >
|
||||
static_cast<uint64_t>(read_options.deadline.count())) {
|
||||
s = Status::TimedOut();
|
||||
break;
|
||||
}
|
||||
|
||||
size_t batch_size = (keys_left > MultiGetContext::MAX_BATCH_SIZE)
|
||||
? MultiGetContext::MAX_BATCH_SIZE
|
||||
: keys_left;
|
||||
MultiGetContext ctx(sorted_keys, start_key + num_keys - keys_left,
|
||||
batch_size, snapshot);
|
||||
batch_size, snapshot, read_options);
|
||||
MultiGetRange range = ctx.GetMultiGetRange();
|
||||
range.AddValueSize(curr_value_size);
|
||||
bool lookup_current = false;
|
||||
|
||||
keys_left -= batch_size;
|
||||
@@ -2157,19 +2326,32 @@ void DBImpl::MultiGetImpl(
|
||||
super_version->current->MultiGet(read_options, &range, callback,
|
||||
is_blob_index);
|
||||
}
|
||||
curr_value_size = range.GetValueSize();
|
||||
if (curr_value_size > read_options.value_size_soft_limit) {
|
||||
s = Status::Aborted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Post processing (decrement reference counts and record statistics)
|
||||
PERF_TIMER_GUARD(get_post_process_time);
|
||||
size_t num_found = 0;
|
||||
uint64_t bytes_read = 0;
|
||||
for (size_t i = start_key; i < start_key + num_keys; ++i) {
|
||||
for (size_t i = start_key; i < start_key + num_keys - keys_left; ++i) {
|
||||
KeyContext* key = (*sorted_keys)[i];
|
||||
if (key->s->ok()) {
|
||||
bytes_read += key->value->size();
|
||||
num_found++;
|
||||
}
|
||||
}
|
||||
if (keys_left) {
|
||||
assert(s.IsTimedOut() || s.IsAborted());
|
||||
for (size_t i = start_key + num_keys - keys_left; i < start_key + num_keys;
|
||||
++i) {
|
||||
KeyContext* key = (*sorted_keys)[i];
|
||||
*key->s = s;
|
||||
}
|
||||
}
|
||||
|
||||
RecordTick(stats_, NUMBER_MULTIGET_CALLS);
|
||||
RecordTick(stats_, NUMBER_MULTIGET_KEYS_READ, num_keys);
|
||||
@@ -2178,6 +2360,8 @@ void DBImpl::MultiGetImpl(
|
||||
RecordInHistogram(stats_, BYTES_PER_MULTIGET, bytes_read);
|
||||
PERF_COUNTER_ADD(multiget_read_bytes, bytes_read);
|
||||
PERF_TIMER_STOP(get_post_process_time);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::CreateColumnFamily(const ColumnFamilyOptions& cf_options,
|
||||
@@ -2301,7 +2485,7 @@ Status DBImpl::CreateColumnFamilyImpl(const ColumnFamilyOptions& cf_options,
|
||||
auto* cfd =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(column_family_name);
|
||||
assert(cfd != nullptr);
|
||||
std::map<std::string, std::shared_ptr<Directory>> dummy_created_dirs;
|
||||
std::map<std::string, std::shared_ptr<FSDirectory>> dummy_created_dirs;
|
||||
s = cfd->AddDirectories(&dummy_created_dirs);
|
||||
}
|
||||
if (s.ok()) {
|
||||
@@ -2436,7 +2620,8 @@ Status DBImpl::DropColumnFamilyImpl(ColumnFamilyHandle* column_family) {
|
||||
|
||||
bool DBImpl::KeyMayExist(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value, bool* value_found) {
|
||||
std::string* value, std::string* timestamp,
|
||||
bool* value_found) {
|
||||
assert(value != nullptr);
|
||||
if (value_found != nullptr) {
|
||||
// falsify later if key-may-exist but can't fetch value
|
||||
@@ -2449,6 +2634,7 @@ bool DBImpl::KeyMayExist(const ReadOptions& read_options,
|
||||
get_impl_options.column_family = column_family;
|
||||
get_impl_options.value = &pinnable_val;
|
||||
get_impl_options.value_found = value_found;
|
||||
get_impl_options.timestamp = timestamp;
|
||||
auto s = GetImpl(roptions, key, get_impl_options);
|
||||
value->assign(pinnable_val.data(), pinnable_val.size());
|
||||
|
||||
@@ -2464,6 +2650,12 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
return NewErrorIterator(
|
||||
Status::NotSupported("Managed iterator is not supported anymore."));
|
||||
}
|
||||
// We will eventually support deadline for iterators too, but safeguard
|
||||
// for now
|
||||
if (read_options.deadline != std::chrono::microseconds::zero()) {
|
||||
return NewErrorIterator(
|
||||
Status::NotSupported("ReadOptions deadline is not supported"));
|
||||
}
|
||||
Iterator* result = nullptr;
|
||||
if (read_options.read_tier == kPersistedTier) {
|
||||
return NewErrorIterator(Status::NotSupported(
|
||||
@@ -2479,7 +2671,8 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
" guaranteed to be preserved, try larger iter_start_seqnum opt."));
|
||||
}
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
assert(cfd != nullptr);
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
if (read_options.tailing) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
@@ -2488,7 +2681,8 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
|
||||
#else
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(this);
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv);
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv,
|
||||
/* allow_unprepared_value */ true);
|
||||
result = NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
cfd->user_comparator(), iter, kMaxSequenceNumber,
|
||||
@@ -2499,10 +2693,11 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
// Note: no need to consider the special case of
|
||||
// last_seq_same_as_publish_seq_==false since NewIterator is overridden in
|
||||
// WritePreparedTxnDB
|
||||
auto snapshot = read_options.snapshot != nullptr
|
||||
? read_options.snapshot->GetSequenceNumber()
|
||||
: versions_->LastSequence();
|
||||
result = NewIteratorImpl(read_options, cfd, snapshot, read_callback);
|
||||
result = NewIteratorImpl(read_options, cfd,
|
||||
(read_options.snapshot != nullptr)
|
||||
? read_options.snapshot->GetSequenceNumber()
|
||||
: kMaxSequenceNumber,
|
||||
read_callback);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -2515,6 +2710,24 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
bool allow_refresh) {
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(this);
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:1");
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:2");
|
||||
|
||||
if (snapshot == kMaxSequenceNumber) {
|
||||
// Note that the snapshot is assigned AFTER referencing the super
|
||||
// version because otherwise a flush happening in between may compact away
|
||||
// data for the snapshot, so the reader would see neither data that was be
|
||||
// visible to the snapshot before compaction nor the newer data inserted
|
||||
// afterwards.
|
||||
// Note that the super version might not contain all the data available
|
||||
// to this snapshot, but in that case it can see all the data in the
|
||||
// super version, which is a valid consistent state after the user
|
||||
// calls NewIterator().
|
||||
snapshot = versions_->LastSequence();
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:3");
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:4");
|
||||
}
|
||||
|
||||
// Try to generate a DB iterator tree in continuous memory area to be
|
||||
// cache friendly. Here is an example of result:
|
||||
// +-------------------------------+
|
||||
@@ -2565,7 +2778,8 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
|
||||
InternalIterator* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), snapshot);
|
||||
db_iter->GetRangeDelAggregator(), snapshot,
|
||||
/* allow_unprepared_value */ true);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
|
||||
return db_iter;
|
||||
@@ -2593,7 +2807,8 @@ Status DBImpl::NewIterators(
|
||||
for (auto cfh : column_families) {
|
||||
auto cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(this);
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv);
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv,
|
||||
/* allow_unprepared_value */ true);
|
||||
iterators->push_back(NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
cfd->user_comparator(), iter, kMaxSequenceNumber,
|
||||
@@ -3407,6 +3622,21 @@ Status DBImpl::GetDbIdentityFromIdentityFile(std::string* identity) const {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::GetDbSessionId(std::string& session_id) const {
|
||||
session_id.assign(db_session_id_);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void DBImpl::SetDbSessionId() {
|
||||
// GenerateUniqueId() generates an identifier
|
||||
// that has a negligible probability of being duplicated
|
||||
db_session_id_ = env_->GenerateUniqueId();
|
||||
// Remove the extra '\n' at the end if there is one
|
||||
if (!db_session_id_.empty() && db_session_id_.back() == '\n') {
|
||||
db_session_id_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// Default implementation -- returns not supported status
|
||||
Status DB::CreateColumnFamily(const ColumnFamilyOptions& /*cf_options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
@@ -3462,12 +3692,8 @@ Status DBImpl::Close() {
|
||||
Status DB::ListColumnFamilies(const DBOptions& db_options,
|
||||
const std::string& name,
|
||||
std::vector<std::string>* column_families) {
|
||||
FileSystem* fs = db_options.file_system.get();
|
||||
LegacyFileSystemWrapper legacy_fs(db_options.env);
|
||||
if (!fs) {
|
||||
fs = &legacy_fs;
|
||||
}
|
||||
return VersionSet::ListColumnFamilies(column_families, name, fs);
|
||||
const std::shared_ptr<FileSystem>& fs = db_options.env->GetFileSystem();
|
||||
return VersionSet::ListColumnFamilies(column_families, name, fs.get());
|
||||
}
|
||||
|
||||
Snapshot::~Snapshot() {}
|
||||
@@ -3511,24 +3737,15 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> paths;
|
||||
|
||||
for (const auto& path : options.db_paths) {
|
||||
paths.emplace_back(path.path);
|
||||
std::set<std::string> paths;
|
||||
for (const DbPath& db_path : options.db_paths) {
|
||||
paths.insert(db_path.path);
|
||||
}
|
||||
for (const auto& cf : column_families) {
|
||||
for (const auto& path : cf.options.cf_paths) {
|
||||
paths.emplace_back(path.path);
|
||||
for (const ColumnFamilyDescriptor& cf : column_families) {
|
||||
for (const DbPath& cf_path : cf.options.cf_paths) {
|
||||
paths.insert(cf_path.path);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove duplicate paths.
|
||||
// Note that we compare only the actual paths but not path ids.
|
||||
// This reason is that same path can appear at different path_ids
|
||||
// for different column families.
|
||||
std::sort(paths.begin(), paths.end());
|
||||
paths.erase(std::unique(paths.begin(), paths.end()), paths.end());
|
||||
|
||||
for (const auto& path : paths) {
|
||||
if (env->GetChildren(path, &filenames).ok()) {
|
||||
for (const auto& fname : filenames) {
|
||||
@@ -3819,8 +4036,9 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
*found_record_for_key = false;
|
||||
|
||||
// Check if there is a record for this key in the latest memtable
|
||||
sv->mem->Get(lkey, nullptr, &s, &merge_context, &max_covering_tombstone_seq,
|
||||
seq, read_options, nullptr /*read_callback*/, is_blob_index);
|
||||
sv->mem->Get(lkey, nullptr, nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, seq, read_options,
|
||||
nullptr /*read_callback*/, is_blob_index);
|
||||
|
||||
if (!(s.ok() || s.IsNotFound() || s.IsMergeInProgress())) {
|
||||
// unexpected error reading memtable.
|
||||
@@ -3845,8 +4063,9 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
}
|
||||
|
||||
// Check if there is a record for this key in the immutable memtables
|
||||
sv->imm->Get(lkey, nullptr, &s, &merge_context, &max_covering_tombstone_seq,
|
||||
seq, read_options, nullptr /*read_callback*/, is_blob_index);
|
||||
sv->imm->Get(lkey, nullptr, nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, seq, read_options,
|
||||
nullptr /*read_callback*/, is_blob_index);
|
||||
|
||||
if (!(s.ok() || s.IsNotFound() || s.IsMergeInProgress())) {
|
||||
// unexpected error reading memtable.
|
||||
@@ -3871,7 +4090,7 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
}
|
||||
|
||||
// Check if there is a record for this key in the immutable memtables
|
||||
sv->imm->GetFromHistory(lkey, nullptr, &s, &merge_context,
|
||||
sv->imm->GetFromHistory(lkey, nullptr, nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, seq, read_options,
|
||||
is_blob_index);
|
||||
|
||||
@@ -3899,7 +4118,7 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
// SST files if cache_only=true?
|
||||
if (!cache_only) {
|
||||
// Check tables
|
||||
sv->current->Get(read_options, lkey, nullptr, &s, &merge_context,
|
||||
sv->current->Get(read_options, lkey, nullptr, nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, nullptr /* value_found */,
|
||||
found_record_for_key, seq, nullptr /*read_callback*/,
|
||||
is_blob_index);
|
||||
@@ -3997,7 +4216,8 @@ Status DBImpl::IngestExternalFiles(
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)->cfd();
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
exec_results[i].second = ingestion_jobs[i].Prepare(
|
||||
args[i].external_files, start_file_number, super_version);
|
||||
args[i].external_files, args[i].files_checksums,
|
||||
args[i].files_checksum_func_names, start_file_number, super_version);
|
||||
exec_results[i].first = true;
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
@@ -4008,7 +4228,8 @@ Status DBImpl::IngestExternalFiles(
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[0].column_family)->cfd();
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
exec_results[0].second = ingestion_jobs[0].Prepare(
|
||||
args[0].external_files, next_file_number, super_version);
|
||||
args[0].external_files, args[0].files_checksums,
|
||||
args[0].files_checksum_func_names, next_file_number, super_version);
|
||||
exec_results[0].first = true;
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
@@ -4522,13 +4743,21 @@ Status DBImpl::GetCreationTimeOfOldestFile(uint64_t* creation_time) {
|
||||
if (mutable_db_options_.max_open_files == -1) {
|
||||
uint64_t oldest_time = port::kMaxUint64;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
uint64_t ctime;
|
||||
cfd->current()->GetCreationTimeOfOldestFile(&ctime);
|
||||
if (ctime < oldest_time) {
|
||||
oldest_time = ctime;
|
||||
}
|
||||
if (oldest_time == 0) {
|
||||
break;
|
||||
if (!cfd->IsDropped()) {
|
||||
uint64_t ctime;
|
||||
{
|
||||
SuperVersion* sv = GetAndRefSuperVersion(cfd);
|
||||
Version* version = sv->current;
|
||||
version->GetCreationTimeOfOldestFile(&ctime);
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
}
|
||||
|
||||
if (ctime < oldest_time) {
|
||||
oldest_time = ctime;
|
||||
}
|
||||
if (oldest_time == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*creation_time = oldest_time;
|
||||
|
||||
+86
-30
@@ -82,13 +82,13 @@ struct MemTableInfo;
|
||||
// Class to maintain directories for all database paths other than main one.
|
||||
class Directories {
|
||||
public:
|
||||
Status SetDirectories(Env* env, const std::string& dbname,
|
||||
const std::string& wal_dir,
|
||||
const std::vector<DbPath>& data_paths);
|
||||
IOStatus SetDirectories(FileSystem* fs, const std::string& dbname,
|
||||
const std::string& wal_dir,
|
||||
const std::vector<DbPath>& data_paths);
|
||||
|
||||
Directory* GetDataDir(size_t path_id) const {
|
||||
FSDirectory* GetDataDir(size_t path_id) const {
|
||||
assert(path_id < data_dirs_.size());
|
||||
Directory* ret_dir = data_dirs_[path_id].get();
|
||||
FSDirectory* ret_dir = data_dirs_[path_id].get();
|
||||
if (ret_dir == nullptr) {
|
||||
// Should use db_dir_
|
||||
return db_dir_.get();
|
||||
@@ -96,19 +96,19 @@ class Directories {
|
||||
return ret_dir;
|
||||
}
|
||||
|
||||
Directory* GetWalDir() {
|
||||
FSDirectory* GetWalDir() {
|
||||
if (wal_dir_) {
|
||||
return wal_dir_.get();
|
||||
}
|
||||
return db_dir_.get();
|
||||
}
|
||||
|
||||
Directory* GetDbDir() { return db_dir_.get(); }
|
||||
FSDirectory* GetDbDir() { return db_dir_.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<Directory> db_dir_;
|
||||
std::vector<std::unique_ptr<Directory>> data_dirs_;
|
||||
std::unique_ptr<Directory> wal_dir_;
|
||||
std::unique_ptr<FSDirectory> db_dir_;
|
||||
std::vector<std::unique_ptr<FSDirectory>> data_dirs_;
|
||||
std::unique_ptr<FSDirectory> wal_dir_;
|
||||
};
|
||||
|
||||
// While DB is the public interface of RocksDB, and DBImpl is the actual
|
||||
@@ -163,6 +163,9 @@ class DBImpl : public DB {
|
||||
virtual Status Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) override;
|
||||
virtual Status Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value, std::string* timestamp) override;
|
||||
|
||||
using DB::GetMergeOperands;
|
||||
Status GetMergeOperands(const ReadOptions& options,
|
||||
@@ -185,6 +188,11 @@ class DBImpl : public DB {
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys,
|
||||
std::vector<std::string>* values) override;
|
||||
virtual std::vector<Status> MultiGet(
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values,
|
||||
std::vector<std::string>* timestamps) override;
|
||||
|
||||
// This MultiGet is a batched version, which may be faster than calling Get
|
||||
// multiple times, especially if the keys have some spatial locality that
|
||||
@@ -198,11 +206,22 @@ class DBImpl : public DB {
|
||||
const size_t num_keys, const Slice* keys,
|
||||
PinnableSlice* values, Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
virtual void MultiGet(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const size_t num_keys, const Slice* keys,
|
||||
PinnableSlice* values, std::string* timestamps,
|
||||
Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
|
||||
virtual void MultiGet(const ReadOptions& options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
virtual void MultiGet(const ReadOptions& options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, std::string* timestamps,
|
||||
Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
|
||||
virtual void MultiGetWithCallback(
|
||||
const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
@@ -230,7 +249,7 @@ class DBImpl : public DB {
|
||||
using DB::KeyMayExist;
|
||||
virtual bool KeyMayExist(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value,
|
||||
std::string* value, std::string* timestamp,
|
||||
bool* value_found = nullptr) override;
|
||||
|
||||
using DB::NewIterator;
|
||||
@@ -331,6 +350,8 @@ class DBImpl : public DB {
|
||||
|
||||
virtual Status GetDbIdentityFromIdentityFile(std::string* identity) const;
|
||||
|
||||
virtual Status GetDbSessionId(std::string& session_id) const override;
|
||||
|
||||
ColumnFamilyHandle* DefaultColumnFamily() const override;
|
||||
|
||||
ColumnFamilyHandle* PersistentStatsColumnFamily() const;
|
||||
@@ -433,6 +454,7 @@ class DBImpl : public DB {
|
||||
struct GetImplOptions {
|
||||
ColumnFamilyHandle* column_family = nullptr;
|
||||
PinnableSlice* value = nullptr;
|
||||
std::string* timestamp = nullptr;
|
||||
bool* value_found = nullptr;
|
||||
ReadCallback* callback = nullptr;
|
||||
bool* is_blob_index = nullptr;
|
||||
@@ -455,8 +477,9 @@ class DBImpl : public DB {
|
||||
// If get_impl_options.get_value = false get merge operands associated with
|
||||
// get_impl_options.key via get_impl_options.merge_operands
|
||||
Status GetImpl(const ReadOptions& options, const Slice& key,
|
||||
GetImplOptions get_impl_options);
|
||||
GetImplOptions& get_impl_options);
|
||||
|
||||
// If `snapshot` == kMaxSequenceNumber, set a recent one inside the file.
|
||||
ArenaWrappedDBIter* NewIteratorImpl(const ReadOptions& options,
|
||||
ColumnFamilyData* cfd,
|
||||
SequenceNumber snapshot,
|
||||
@@ -561,9 +584,14 @@ class DBImpl : public DB {
|
||||
// Return an internal iterator over the current state of the database.
|
||||
// The keys of this iterator are internal keys (see format.h).
|
||||
// The returned iterator should be deleted when no longer needed.
|
||||
// If allow_unprepared_value is true, the returned iterator may defer reading
|
||||
// the value and so will require PrepareValue() to be called before value();
|
||||
// allow_unprepared_value = false is convenient when this optimization is not
|
||||
// useful, e.g. when reading the whole column family.
|
||||
InternalIterator* NewInternalIterator(
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
ColumnFamilyHandle* column_family = nullptr);
|
||||
ColumnFamilyHandle* column_family = nullptr,
|
||||
bool allow_unprepared_value = false);
|
||||
|
||||
LogsWithPrepTracker* logs_with_prep_tracker() {
|
||||
return &logs_with_prep_tracker_;
|
||||
@@ -689,7 +717,8 @@ class DBImpl : public DB {
|
||||
|
||||
InternalIterator* NewInternalIterator(
|
||||
const ReadOptions&, ColumnFamilyData* cfd, SuperVersion* super_version,
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence);
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
bool allow_unprepared_value);
|
||||
|
||||
// hollow transactions shell used for recovery.
|
||||
// these will then be passed to TransactionDB so that
|
||||
@@ -826,8 +855,9 @@ class DBImpl : public DB {
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
const bool seq_per_batch, const bool batch_per_txn);
|
||||
|
||||
static Status CreateAndNewDirectory(Env* env, const std::string& dirname,
|
||||
std::unique_ptr<Directory>* directory);
|
||||
static IOStatus CreateAndNewDirectory(
|
||||
FileSystem* fs, const std::string& dirname,
|
||||
std::unique_ptr<FSDirectory>* directory);
|
||||
|
||||
// find stats map from stats_history_ with smallest timestamp in
|
||||
// the range of [start_time, end_time)
|
||||
@@ -942,11 +972,20 @@ class DBImpl : public DB {
|
||||
void TEST_WaitForPersistStatsRun(std::function<void()> callback) const;
|
||||
bool TEST_IsPersistentStatsEnabled() const;
|
||||
size_t TEST_EstimateInMemoryStatsHistorySize() const;
|
||||
|
||||
VersionSet* TEST_GetVersionSet() const { return versions_.get(); }
|
||||
|
||||
const std::unordered_set<uint64_t>& TEST_GetFilesGrabbedForPurge() const {
|
||||
return files_grabbed_for_purge_;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
protected:
|
||||
const std::string dbname_;
|
||||
std::string db_id_;
|
||||
// db_session_id_ is an identifier that gets reset
|
||||
// every time the DB is opened
|
||||
std::string db_session_id_;
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
// Flag to check whether we allocated and own the info log file
|
||||
bool own_info_log_;
|
||||
@@ -1119,6 +1158,19 @@ class DBImpl : public DB {
|
||||
|
||||
virtual bool OwnTablesAndLogs() const { return true; }
|
||||
|
||||
// REQUIRES: db mutex held when calling this function, but the db mutex can
|
||||
// be released and re-acquired. Db mutex will be held when the function
|
||||
// returns.
|
||||
// After best-efforts recovery, there may be SST files in db/cf paths that are
|
||||
// not referenced in the MANIFEST. We delete these SST files. In the
|
||||
// meantime, we find out the largest file number present in the paths, and
|
||||
// bump up the version set's next_file_number_ to be 1 + largest_file_number.
|
||||
Status FinishBestEffortsRecovery();
|
||||
|
||||
// SetDbSessionId() should be called in the constuctor DBImpl()
|
||||
// to ensure that db_session_id_ gets updated every time the DB is opened
|
||||
void SetDbSessionId();
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
friend class ErrorHandler;
|
||||
@@ -1332,7 +1384,7 @@ class DBImpl : public DB {
|
||||
void ReleaseFileNumberFromPendingOutputs(
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>& v);
|
||||
|
||||
Status SyncClosedLogs(JobContext* job_context);
|
||||
IOStatus SyncClosedLogs(JobContext* job_context);
|
||||
|
||||
// Flush the in-memory write buffer to storage. Switches to a new
|
||||
// log-file/memtable and writes a new descriptor iff successful. Then
|
||||
@@ -1469,21 +1521,25 @@ class DBImpl : public DB {
|
||||
WriteBatch* tmp_batch, size_t* write_with_wal,
|
||||
WriteBatch** to_be_cached_state);
|
||||
|
||||
Status WriteToWAL(const WriteBatch& merged_batch, log::Writer* log_writer,
|
||||
uint64_t* log_used, uint64_t* log_size);
|
||||
IOStatus WriteToWAL(const WriteBatch& merged_batch, log::Writer* log_writer,
|
||||
uint64_t* log_used, uint64_t* log_size);
|
||||
|
||||
Status WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence);
|
||||
IOStatus WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence);
|
||||
|
||||
Status ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
uint64_t* log_used, SequenceNumber* last_sequence,
|
||||
size_t seq_inc);
|
||||
IOStatus ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
uint64_t* log_used,
|
||||
SequenceNumber* last_sequence, size_t seq_inc);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ if paranoid check is enabled.
|
||||
void WriteStatusCheck(const Status& status);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ when IO error happens, e.g., write
|
||||
// WAL, sync WAL fails, if paranoid check is enabled.
|
||||
void IOStatusCheck(const IOStatus& status);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ in case of memtable insert error.
|
||||
void MemTableInsertStatusCheck(const Status& memtable_insert_status);
|
||||
|
||||
@@ -1598,7 +1654,7 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t GetMaxTotalWalSize() const;
|
||||
|
||||
Directory* GetDataDir(ColumnFamilyData* cfd, size_t path_id) const;
|
||||
FSDirectory* GetDataDir(ColumnFamilyData* cfd, size_t path_id) const;
|
||||
|
||||
Status CloseHelper();
|
||||
|
||||
@@ -1727,7 +1783,7 @@ class DBImpl : public DB {
|
||||
// to have acquired the SuperVersion and pass in a snapshot sequence number
|
||||
// in order to construct the LookupKeys. The start_key and num_keys specify
|
||||
// the range of keys in the sorted_keys vector for a single column family.
|
||||
void MultiGetImpl(
|
||||
Status MultiGetImpl(
|
||||
const ReadOptions& read_options, size_t start_key, size_t num_keys,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys,
|
||||
SuperVersion* sv, SequenceNumber snap_seqnum, ReadCallback* callback,
|
||||
@@ -1895,7 +1951,7 @@ class DBImpl : public DB {
|
||||
std::unordered_map<uint64_t, PurgeFileInfo> purge_files_;
|
||||
|
||||
// A vector to store the file numbers that have been assigned to certain
|
||||
// JobContext. Current implementation tracks ssts only.
|
||||
// JobContext. Current implementation tracks table and blob files only.
|
||||
std::unordered_set<uint64_t> files_grabbed_for_purge_;
|
||||
|
||||
// A queue to store log writers to close
|
||||
@@ -2021,7 +2077,7 @@ class DBImpl : public DB {
|
||||
// REQUIRES: mutex locked
|
||||
std::unique_ptr<ROCKSDB_NAMESPACE::RepeatableThread> thread_persist_stats_;
|
||||
|
||||
// When set, we use a separate queue for writes that dont write to memtable.
|
||||
// When set, we use a separate queue for writes that don't write to memtable.
|
||||
// In 2PC these are the writes at Prepare phase.
|
||||
const bool two_write_queues_;
|
||||
const bool manual_wal_flush_;
|
||||
|
||||
@@ -79,7 +79,7 @@ bool DBImpl::RequestCompactionToken(ColumnFamilyData* cfd, bool force,
|
||||
return false;
|
||||
}
|
||||
|
||||
Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
IOStatus DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Start");
|
||||
mutex_.AssertHeld();
|
||||
autovector<log::Writer*, 1> logs_to_sync;
|
||||
@@ -96,7 +96,7 @@ Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
logs_to_sync.push_back(log.writer);
|
||||
}
|
||||
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
if (!logs_to_sync.empty()) {
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -104,34 +104,34 @@ Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[JOB %d] Syncing log #%" PRIu64, job_context->job_id,
|
||||
log->get_log_number());
|
||||
s = log->file()->Sync(immutable_db_options_.use_fsync);
|
||||
if (!s.ok()) {
|
||||
io_s = log->file()->Sync(immutable_db_options_.use_fsync);
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (immutable_db_options_.recycle_log_file_num > 0) {
|
||||
s = log->Close();
|
||||
if (!s.ok()) {
|
||||
io_s = log->Close();
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = directories_.GetWalDir()->Fsync();
|
||||
if (io_s.ok()) {
|
||||
io_s = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
|
||||
}
|
||||
|
||||
mutex_.Lock();
|
||||
|
||||
// "number <= current_log_number - 1" is equivalent to
|
||||
// "number < current_log_number".
|
||||
MarkLogsSynced(current_log_number - 1, true, s);
|
||||
if (!s.ok()) {
|
||||
error_handler_.SetBGError(s, BackgroundErrorReason::kFlush);
|
||||
MarkLogsSynced(current_log_number - 1, true, io_s);
|
||||
if (!io_s.ok()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Failed");
|
||||
return s;
|
||||
return io_s;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
return io_s;
|
||||
}
|
||||
|
||||
Status DBImpl::FlushMemTableToOutputFile(
|
||||
@@ -154,8 +154,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
GetDataDir(cfd, 0U),
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
true /* sync_output_directory */, true /* write_manifest */, thread_pri);
|
||||
|
||||
true /* sync_output_directory */, true /* write_manifest */, thread_pri,
|
||||
db_id_, db_session_id_);
|
||||
FileMetaData file_meta;
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:BeforePickMemtables");
|
||||
@@ -168,6 +168,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
if (logfile_number_ > 0 &&
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1) {
|
||||
// If there are more than one column families, we need to make sure that
|
||||
@@ -176,7 +177,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// flushed SST may contain data from write batches whose updates to
|
||||
// other column families are missing.
|
||||
// SyncClosedLogs() may unlock and re-lock the db_mutex.
|
||||
s = SyncClosedLogs(job_context);
|
||||
io_s = SyncClosedLogs(job_context);
|
||||
s = io_s;
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Skip");
|
||||
}
|
||||
@@ -192,6 +194,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
} else {
|
||||
flush_job.Cancel();
|
||||
}
|
||||
io_s = flush_job.io_status();
|
||||
|
||||
if (s.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, superversion_context,
|
||||
@@ -206,8 +209,13 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
}
|
||||
|
||||
if (!s.ok() && !s.IsShutdownInProgress() && !s.IsColumnFamilyDropped()) {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
if (!io_s.ok() && !io_s.IsShutdownInProgress() &&
|
||||
!io_s.IsColumnFamilyDropped()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -301,7 +309,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
|
||||
autovector<Directory*> distinct_output_dirs;
|
||||
autovector<FSDirectory*> distinct_output_dirs;
|
||||
autovector<std::string> distinct_output_dir_paths;
|
||||
std::vector<std::unique_ptr<FlushJob>> jobs;
|
||||
std::vector<MutableCFOptions> all_mutable_cf_options;
|
||||
@@ -309,7 +317,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
all_mutable_cf_options.reserve(num_cfs);
|
||||
for (int i = 0; i < num_cfs; ++i) {
|
||||
auto cfd = cfds[i];
|
||||
Directory* data_dir = GetDataDir(cfd, 0U);
|
||||
FSDirectory* data_dir = GetDataDir(cfd, 0U);
|
||||
const std::string& curr_path = cfd->ioptions()->cf_paths[0].path;
|
||||
|
||||
// Add to distinct output directories if eligible. Use linear search. Since
|
||||
@@ -338,12 +346,13 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
data_dir, GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
stats_, &event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
false /* sync_output_directory */, false /* write_manifest */,
|
||||
thread_pri));
|
||||
thread_pri, db_id_, db_session_id_));
|
||||
jobs.back()->PickMemTable();
|
||||
}
|
||||
|
||||
std::vector<FileMetaData> file_meta(num_cfs);
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
assert(num_cfs == static_cast<int>(jobs.size()));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -358,15 +367,18 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
if (logfile_number_ > 0) {
|
||||
// TODO (yanqin) investigate whether we should sync the closed logs for
|
||||
// single column family case.
|
||||
s = SyncClosedLogs(job_context);
|
||||
io_s = SyncClosedLogs(job_context);
|
||||
s = io_s;
|
||||
}
|
||||
|
||||
// exec_status stores the execution status of flush_jobs as
|
||||
// <bool /* executed */, Status /* status code */>
|
||||
autovector<std::pair<bool, Status>> exec_status;
|
||||
autovector<IOStatus> io_status;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
// Initially all jobs are not executed, with status OK.
|
||||
exec_status.emplace_back(false, Status::OK());
|
||||
io_status.emplace_back(IOStatus::OK());
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -375,6 +387,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
exec_status[i].second =
|
||||
jobs[i]->Run(&logs_with_prep_tracker_, &file_meta[i]);
|
||||
exec_status[i].first = true;
|
||||
io_status[i] = jobs[i]->io_status();
|
||||
}
|
||||
if (num_cfs > 1) {
|
||||
TEST_SYNC_POINT(
|
||||
@@ -387,6 +400,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
exec_status[0].second =
|
||||
jobs[0]->Run(&logs_with_prep_tracker_, &file_meta[0]);
|
||||
exec_status[0].first = true;
|
||||
io_status[0] = jobs[0]->io_status();
|
||||
|
||||
Status error_status;
|
||||
for (const auto& e : exec_status) {
|
||||
@@ -405,6 +419,20 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
s = error_status.ok() ? s : error_status;
|
||||
}
|
||||
|
||||
if (io_s.ok()) {
|
||||
IOStatus io_error = IOStatus::OK();
|
||||
for (int i = 0; i != static_cast<int>(io_status.size()); i++) {
|
||||
if (!io_status[i].ok() && !io_status[i].IsShutdownInProgress() &&
|
||||
!io_status[i].IsColumnFamilyDropped()) {
|
||||
io_error = io_status[i];
|
||||
}
|
||||
}
|
||||
io_s = io_error;
|
||||
if (s.ok() && !io_s.ok()) {
|
||||
s = io_s;
|
||||
}
|
||||
}
|
||||
|
||||
if (s.IsColumnFamilyDropped()) {
|
||||
s = Status::OK();
|
||||
}
|
||||
@@ -413,7 +441,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// Sync on all distinct output directories.
|
||||
for (auto dir : distinct_output_dirs) {
|
||||
if (dir != nullptr) {
|
||||
Status error_status = dir->Fsync();
|
||||
Status error_status = dir->Fsync(IOOptions(), nullptr);
|
||||
if (!error_status.ok()) {
|
||||
s = error_status;
|
||||
break;
|
||||
@@ -543,9 +571,15 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
if (!s.ok() && !s.IsShutdownInProgress()) {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
// Need to undo atomic flush if something went wrong, i.e. s is not OK and
|
||||
// it is not because of CF drop.
|
||||
if (!s.ok() && !s.IsColumnFamilyDropped()) {
|
||||
if (!io_s.ok() && !io_s.IsColumnFamilyDropped()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -652,8 +686,6 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
return Status::InvalidArgument("Invalid target path ID");
|
||||
}
|
||||
|
||||
bool exclusive = options.exclusive_manual_compaction;
|
||||
|
||||
bool flush_needed = true;
|
||||
if (begin != nullptr && end != nullptr) {
|
||||
// TODO(ajkr): We could also optimize away the flush in certain cases where
|
||||
@@ -686,25 +718,9 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
}
|
||||
}
|
||||
|
||||
int max_level_with_files = 0;
|
||||
// max_file_num_to_ignore can be used to filter out newly created SST files,
|
||||
// useful for bottom level compaction in a manual compaction
|
||||
uint64_t max_file_num_to_ignore = port::kMaxUint64;
|
||||
uint64_t next_file_number = port::kMaxUint64;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
Version* base = cfd->current();
|
||||
for (int level = 1; level < base->storage_info()->num_non_empty_levels();
|
||||
level++) {
|
||||
if (base->storage_info()->OverlapInLevel(level, begin, end)) {
|
||||
max_level_with_files = level;
|
||||
}
|
||||
}
|
||||
next_file_number = versions_->current_next_file_number();
|
||||
}
|
||||
|
||||
int final_output_level = 0;
|
||||
|
||||
constexpr int kInvalidLevel = -1;
|
||||
int final_output_level = kInvalidLevel;
|
||||
bool exclusive = options.exclusive_manual_compaction;
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal &&
|
||||
cfd->NumberLevels() > 1) {
|
||||
// Always compact all files together.
|
||||
@@ -715,58 +731,98 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
}
|
||||
s = RunManualCompaction(cfd, ColumnFamilyData::kCompactAllLevels,
|
||||
final_output_level, options, begin, end, exclusive,
|
||||
false, max_file_num_to_ignore);
|
||||
false, port::kMaxUint64);
|
||||
} else {
|
||||
for (int level = 0; level <= max_level_with_files; level++) {
|
||||
int first_overlapped_level = kInvalidLevel;
|
||||
int max_overlapped_level = kInvalidLevel;
|
||||
{
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
Version* current_version = super_version->current;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
bool overlap;
|
||||
for (int level = 0;
|
||||
level < current_version->storage_info()->num_non_empty_levels();
|
||||
level++) {
|
||||
overlap = true;
|
||||
if (begin != nullptr && end != nullptr) {
|
||||
Status status = current_version->OverlapWithLevelIterator(
|
||||
ro, file_options_, *begin, *end, level, &overlap);
|
||||
if (!status.ok()) {
|
||||
overlap = current_version->storage_info()->OverlapInLevel(
|
||||
level, begin, end);
|
||||
}
|
||||
} else {
|
||||
overlap = current_version->storage_info()->OverlapInLevel(level,
|
||||
begin, end);
|
||||
}
|
||||
if (overlap) {
|
||||
if (first_overlapped_level == kInvalidLevel) {
|
||||
first_overlapped_level = level;
|
||||
}
|
||||
max_overlapped_level = level;
|
||||
}
|
||||
}
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
if (s.ok() && first_overlapped_level != kInvalidLevel) {
|
||||
// max_file_num_to_ignore can be used to filter out newly created SST
|
||||
// files, useful for bottom level compaction in a manual compaction
|
||||
uint64_t max_file_num_to_ignore = port::kMaxUint64;
|
||||
uint64_t next_file_number = versions_->current_next_file_number();
|
||||
final_output_level = max_overlapped_level;
|
||||
int output_level;
|
||||
// in case the compaction is universal or if we're compacting the
|
||||
// bottom-most level, the output level will be the same as input one.
|
||||
// level 0 can never be the bottommost level (i.e. if all files are in
|
||||
// level 0, we will compact to level 1)
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
output_level = level;
|
||||
} else if (level == max_level_with_files && level > 0) {
|
||||
if (options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kSkip) {
|
||||
// Skip bottommost level compaction
|
||||
continue;
|
||||
} else if (options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kIfHaveCompactionFilter &&
|
||||
cfd->ioptions()->compaction_filter == nullptr &&
|
||||
cfd->ioptions()->compaction_filter_factory == nullptr) {
|
||||
// Skip bottommost level compaction since we don't have a compaction
|
||||
// filter
|
||||
continue;
|
||||
for (int level = first_overlapped_level; level <= max_overlapped_level;
|
||||
level++) {
|
||||
// in case the compaction is universal or if we're compacting the
|
||||
// bottom-most level, the output level will be the same as input one.
|
||||
// level 0 can never be the bottommost level (i.e. if all files are in
|
||||
// level 0, we will compact to level 1)
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
output_level = level;
|
||||
} else if (level == max_overlapped_level && level > 0) {
|
||||
if (options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kSkip) {
|
||||
// Skip bottommost level compaction
|
||||
continue;
|
||||
} else if (options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kIfHaveCompactionFilter &&
|
||||
cfd->ioptions()->compaction_filter == nullptr &&
|
||||
cfd->ioptions()->compaction_filter_factory == nullptr) {
|
||||
// Skip bottommost level compaction since we don't have a compaction
|
||||
// filter
|
||||
continue;
|
||||
}
|
||||
output_level = level;
|
||||
// update max_file_num_to_ignore only for bottom level compaction
|
||||
// because data in newly compacted files in middle levels may still
|
||||
// need to be pushed down
|
||||
max_file_num_to_ignore = next_file_number;
|
||||
} else {
|
||||
output_level = level + 1;
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleLevel &&
|
||||
cfd->ioptions()->level_compaction_dynamic_level_bytes &&
|
||||
level == 0) {
|
||||
output_level = ColumnFamilyData::kCompactToBaseLevel;
|
||||
}
|
||||
}
|
||||
output_level = level;
|
||||
// update max_file_num_to_ignore only for bottom level compaction
|
||||
// because data in newly compacted files in middle levels may still need
|
||||
// to be pushed down
|
||||
max_file_num_to_ignore = next_file_number;
|
||||
} else {
|
||||
output_level = level + 1;
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleLevel &&
|
||||
cfd->ioptions()->level_compaction_dynamic_level_bytes &&
|
||||
level == 0) {
|
||||
output_level = ColumnFamilyData::kCompactToBaseLevel;
|
||||
s = RunManualCompaction(cfd, level, output_level, options, begin, end,
|
||||
exclusive, false, max_file_num_to_ignore);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
if (output_level == ColumnFamilyData::kCompactToBaseLevel) {
|
||||
final_output_level = cfd->NumberLevels() - 1;
|
||||
} else if (output_level > final_output_level) {
|
||||
final_output_level = output_level;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction()::1");
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction()::2");
|
||||
}
|
||||
s = RunManualCompaction(cfd, level, output_level, options, begin, end,
|
||||
exclusive, false, max_file_num_to_ignore);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
if (output_level == ColumnFamilyData::kCompactToBaseLevel) {
|
||||
final_output_level = cfd->NumberLevels() - 1;
|
||||
} else if (output_level > final_output_level) {
|
||||
final_output_level = output_level;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction()::1");
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction()::2");
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
if (!s.ok() || final_output_level == kInvalidLevel) {
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
return s;
|
||||
}
|
||||
@@ -976,7 +1032,8 @@ Status DBImpl::CompactFilesImpl(
|
||||
snapshot_checker, table_cache_, &event_logger_,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, Env::Priority::USER, &manual_compaction_paused_);
|
||||
&compaction_job_stats, Env::Priority::USER, &manual_compaction_paused_,
|
||||
db_id_, db_session_id_);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
@@ -1037,7 +1094,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
}
|
||||
|
||||
if (output_file_names != nullptr) {
|
||||
for (const auto newf : c->edit()->GetNewFiles()) {
|
||||
for (const auto& newf : c->edit()->GetNewFiles()) {
|
||||
(*output_file_names)
|
||||
.push_back(TableFileName(c->immutable_cf_options()->cf_paths,
|
||||
newf.second.fd.GetNumber(),
|
||||
@@ -1137,7 +1194,7 @@ void DBImpl::NotifyOnCompactionBegin(ColumnFamilyData* cfd, Compaction* c,
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto newf : c->edit()->GetNewFiles()) {
|
||||
for (const auto& newf : c->edit()->GetNewFiles()) {
|
||||
const FileMetaData& meta = newf.second;
|
||||
const FileDescriptor& desc = meta.fd;
|
||||
const uint64_t file_number = desc.GetNumber();
|
||||
@@ -1488,7 +1545,12 @@ Status DBImpl::RunManualCompaction(
|
||||
}
|
||||
manual.incomplete = false;
|
||||
bg_compaction_scheduled_++;
|
||||
env_->Schedule(&DBImpl::BGWorkCompaction, ca, Env::Priority::LOW, this,
|
||||
Env::Priority thread_pool_pri = Env::Priority::LOW;
|
||||
if (compaction->bottommost_level() &&
|
||||
env_->GetBackgroundThreads(Env::Priority::BOTTOM) > 0) {
|
||||
thread_pool_pri = Env::Priority::BOTTOM;
|
||||
}
|
||||
env_->Schedule(&DBImpl::BGWorkCompaction, ca, thread_pool_pri, this,
|
||||
&DBImpl::UnscheduleCompactionCallback);
|
||||
scheduled = true;
|
||||
}
|
||||
@@ -1846,7 +1908,7 @@ Status DBImpl::WaitForFlushMemTables(
|
||||
}
|
||||
}
|
||||
if (1 == num_dropped && 1 == num) {
|
||||
return Status::InvalidArgument("Cannot flush a dropped CF");
|
||||
return Status::ColumnFamilyDropped();
|
||||
}
|
||||
// Column families involved in this flush request have either been dropped
|
||||
// or finished flush. Then it's time to finish waiting.
|
||||
@@ -1970,7 +2032,7 @@ void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
|
||||
DBImpl::BGJobLimits DBImpl::GetBGJobLimits() const {
|
||||
mutex_.AssertHeld();
|
||||
return GetBGJobLimits(immutable_db_options_.max_background_flushes,
|
||||
return GetBGJobLimits(mutable_db_options_.max_background_flushes,
|
||||
mutable_db_options_.max_background_compactions,
|
||||
mutable_db_options_.max_background_jobs,
|
||||
write_controller_.NeedSpeedupCompaction());
|
||||
@@ -2083,8 +2145,7 @@ void DBImpl::BGWorkFlush(void* arg) {
|
||||
|
||||
IOSTATS_SET_THREAD_POOL_ID(fta.thread_pri_);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkFlush");
|
||||
static_cast_with_check<DBImpl, DB>(fta.db_)->BackgroundCallFlush(
|
||||
fta.thread_pri_);
|
||||
static_cast_with_check<DBImpl>(fta.db_)->BackgroundCallFlush(fta.thread_pri_);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkFlush:done");
|
||||
}
|
||||
|
||||
@@ -2095,7 +2156,7 @@ void DBImpl::BGWorkCompaction(void* arg) {
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkCompaction");
|
||||
auto prepicked_compaction =
|
||||
static_cast<PrepickedCompaction*>(ca.prepicked_compaction);
|
||||
static_cast_with_check<DBImpl, DB>(ca.db)->BackgroundCallCompaction(
|
||||
static_cast_with_check<DBImpl>(ca.db)->BackgroundCallCompaction(
|
||||
prepicked_compaction, Env::Priority::LOW);
|
||||
delete prepicked_compaction;
|
||||
}
|
||||
@@ -2489,8 +2550,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
"[%s] Manual compaction from level-%d from %s .. "
|
||||
"%s; nothing to do\n",
|
||||
m->cfd->GetName().c_str(), m->input_level,
|
||||
(m->begin ? m->begin->DebugString().c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString().c_str() : "(end)"));
|
||||
(m->begin ? m->begin->DebugString(true).c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString(true).c_str() : "(end)"));
|
||||
} else {
|
||||
// First check if we have enough room to do the compaction
|
||||
bool enough_room = EnoughRoomForCompaction(
|
||||
@@ -2509,11 +2570,11 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
"[%s] Manual compaction from level-%d to level-%d from %s .. "
|
||||
"%s; will stop at %s\n",
|
||||
m->cfd->GetName().c_str(), m->input_level, c->output_level(),
|
||||
(m->begin ? m->begin->DebugString().c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString().c_str() : "(end)"),
|
||||
(m->begin ? m->begin->DebugString(true).c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString(true).c_str() : "(end)"),
|
||||
((m->done || m->manual_end == nullptr)
|
||||
? "(end)"
|
||||
: m->manual_end->DebugString().c_str()));
|
||||
: m->manual_end->DebugString(true).c_str()));
|
||||
}
|
||||
}
|
||||
} else if (!is_prepicked && !compaction_queue_.empty()) {
|
||||
@@ -2606,6 +2667,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
}
|
||||
}
|
||||
|
||||
IOStatus io_s;
|
||||
if (!c) {
|
||||
// Nothing to do
|
||||
ROCKS_LOG_BUFFER(log_buffer, "Compaction nothing to do");
|
||||
@@ -2627,9 +2689,11 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
for (const auto& f : *c->inputs(0)) {
|
||||
c->edit()->DeleteFile(c->level(), f->fd.GetNumber());
|
||||
}
|
||||
versions_->SetIOStatusOK();
|
||||
status = versions_->LogAndApply(c->column_family_data(),
|
||||
*c->mutable_cf_options(), c->edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
io_s = versions_->io_status();
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
@@ -2683,9 +2747,11 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
}
|
||||
}
|
||||
|
||||
versions_->SetIOStatusOK();
|
||||
status = versions_->LogAndApply(c->column_family_data(),
|
||||
*c->mutable_cf_options(), c->edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
io_s = versions_->io_status();
|
||||
// Use latest MutableCFOptions
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
@@ -2758,7 +2824,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
&event_logger_, c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, thread_pri,
|
||||
is_manual ? &manual_compaction_paused_ : nullptr);
|
||||
is_manual ? &manual_compaction_paused_ : nullptr, db_id_,
|
||||
db_session_id_);
|
||||
compaction_job.Prepare();
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
@@ -2772,6 +2839,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
mutex_.Lock();
|
||||
|
||||
status = compaction_job.Install(*c->mutable_cf_options());
|
||||
io_s = compaction_job.io_status();
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
@@ -2781,6 +2849,11 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
c->column_family_data());
|
||||
}
|
||||
|
||||
if (status.ok() && !io_s.ok()) {
|
||||
status = io_s;
|
||||
}
|
||||
|
||||
if (c != nullptr) {
|
||||
c->ReleaseCompactionFiles(status);
|
||||
*made_progress = true;
|
||||
@@ -2806,7 +2879,11 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
} else {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log, "Compaction error: %s",
|
||||
status.ToString().c_str());
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kCompaction);
|
||||
if (!io_s.ok()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kCompaction);
|
||||
} else {
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kCompaction);
|
||||
}
|
||||
if (c != nullptr && !is_manual && !error_handler_.IsBGWorkStopped()) {
|
||||
// Put this cfd back in the compaction queue so we can retry after some
|
||||
// time
|
||||
|
||||
+112
-25
@@ -14,6 +14,7 @@
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "file/file_util.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
@@ -35,7 +36,7 @@ uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
|
||||
return std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
// * Returns the list of live files in 'sst_live'
|
||||
// * Returns the list of live files in 'sst_live' and 'blob_live'.
|
||||
// If it's doing full scan:
|
||||
// * Returns the list of all files in the filesystem in
|
||||
// 'full_scan_candidate_files'.
|
||||
@@ -76,26 +77,26 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
// Since job_context->min_pending_output is set, until file scan finishes,
|
||||
// mutex_ cannot be released. Otherwise, we might see no min_pending_output
|
||||
// here but later find newer generated unfinalized files while scanning.
|
||||
if (!pending_outputs_.empty()) {
|
||||
job_context->min_pending_output = *pending_outputs_.begin();
|
||||
} else {
|
||||
// delete all of them
|
||||
job_context->min_pending_output = std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
job_context->min_pending_output = MinObsoleteSstNumberToKeep();
|
||||
|
||||
// Get obsolete files. This function will also update the list of
|
||||
// pending files in VersionSet().
|
||||
versions_->GetObsoleteFiles(&job_context->sst_delete_files,
|
||||
&job_context->manifest_delete_files,
|
||||
job_context->min_pending_output);
|
||||
versions_->GetObsoleteFiles(
|
||||
&job_context->sst_delete_files, &job_context->blob_delete_files,
|
||||
&job_context->manifest_delete_files, job_context->min_pending_output);
|
||||
|
||||
// Mark the elements in job_context->sst_delete_files as grabbedForPurge
|
||||
// so that other threads calling FindObsoleteFiles with full_scan=true
|
||||
// will not add these files to candidate list for purge.
|
||||
// Mark the elements in job_context->sst_delete_files and
|
||||
// job_context->blob_delete_files as "grabbed for purge" so that other threads
|
||||
// calling FindObsoleteFiles with full_scan=true will not add these files to
|
||||
// candidate list for purge.
|
||||
for (const auto& sst_to_del : job_context->sst_delete_files) {
|
||||
MarkAsGrabbedForPurge(sst_to_del.metadata->fd.GetNumber());
|
||||
}
|
||||
|
||||
for (const auto& blob_file : job_context->blob_delete_files) {
|
||||
MarkAsGrabbedForPurge(blob_file.GetBlobFileNumber());
|
||||
}
|
||||
|
||||
// store the current filenum, lognum, etc
|
||||
job_context->manifest_file_number = versions_->manifest_file_number();
|
||||
job_context->pending_manifest_file_number =
|
||||
@@ -103,7 +104,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
job_context->log_number = MinLogNumberToKeep();
|
||||
job_context->prev_log_number = versions_->prev_log_number();
|
||||
|
||||
versions_->AddLiveFiles(&job_context->sst_live);
|
||||
versions_->AddLiveFiles(&job_context->sst_live, &job_context->blob_live);
|
||||
if (doing_the_full_scan) {
|
||||
InfoLogPrefix info_log_prefix(!immutable_db_options_.db_log_dir.empty(),
|
||||
dbname_);
|
||||
@@ -256,8 +257,11 @@ bool CompareCandidateFile(const JobContext::CandidateFileInfo& first,
|
||||
void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
const std::string& path_to_sync,
|
||||
FileType type, uint64_t number) {
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::DeleteObsoleteFileImpl::BeforeDeletion",
|
||||
const_cast<std::string*>(&fname));
|
||||
|
||||
Status file_deletion_status;
|
||||
if (type == kTableFile || type == kLogFile) {
|
||||
if (type == kTableFile || type == kBlobFile || type == kLogFile) {
|
||||
file_deletion_status =
|
||||
DeleteDBFile(&immutable_db_options_, fname, path_to_sync,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path_);
|
||||
@@ -303,19 +307,19 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
// FindObsoleteFiles() should've populated this so nonzero
|
||||
assert(state.manifest_file_number != 0);
|
||||
|
||||
// Now, convert live list to an unordered map, WITHOUT mutex held;
|
||||
// set is slow.
|
||||
std::unordered_map<uint64_t, const FileDescriptor*> sst_live_map;
|
||||
for (const FileDescriptor& fd : state.sst_live) {
|
||||
sst_live_map[fd.GetNumber()] = &fd;
|
||||
}
|
||||
// Now, convert lists to unordered sets, WITHOUT mutex held; set is slow.
|
||||
std::unordered_set<uint64_t> sst_live_set(state.sst_live.begin(),
|
||||
state.sst_live.end());
|
||||
std::unordered_set<uint64_t> blob_live_set(state.blob_live.begin(),
|
||||
state.blob_live.end());
|
||||
std::unordered_set<uint64_t> log_recycle_files_set(
|
||||
state.log_recycle_files.begin(), state.log_recycle_files.end());
|
||||
|
||||
auto candidate_files = state.full_scan_candidate_files;
|
||||
candidate_files.reserve(
|
||||
candidate_files.size() + state.sst_delete_files.size() +
|
||||
state.log_delete_files.size() + state.manifest_delete_files.size());
|
||||
state.blob_delete_files.size() + state.log_delete_files.size() +
|
||||
state.manifest_delete_files.size());
|
||||
// We may ignore the dbname when generating the file names.
|
||||
for (auto& file : state.sst_delete_files) {
|
||||
candidate_files.emplace_back(
|
||||
@@ -326,6 +330,11 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
file.DeleteMetadata();
|
||||
}
|
||||
|
||||
for (const auto& blob_file : state.blob_delete_files) {
|
||||
candidate_files.emplace_back(BlobFileName(blob_file.GetBlobFileNumber()),
|
||||
blob_file.GetPath());
|
||||
}
|
||||
|
||||
for (auto file_num : state.log_delete_files) {
|
||||
if (file_num > 0) {
|
||||
candidate_files.emplace_back(LogFileName(file_num),
|
||||
@@ -412,12 +421,19 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
case kTableFile:
|
||||
// If the second condition is not there, this makes
|
||||
// DontDeletePendingOutputs fail
|
||||
keep = (sst_live_map.find(number) != sst_live_map.end()) ||
|
||||
keep = (sst_live_set.find(number) != sst_live_set.end()) ||
|
||||
number >= state.min_pending_output;
|
||||
if (!keep) {
|
||||
files_to_del.insert(number);
|
||||
}
|
||||
break;
|
||||
case kBlobFile:
|
||||
keep = number >= state.min_pending_output ||
|
||||
(blob_live_set.find(number) != blob_live_set.end());
|
||||
if (!keep) {
|
||||
files_to_del.insert(number);
|
||||
}
|
||||
break;
|
||||
case kTempFile:
|
||||
// Any temp files that are currently being written to must
|
||||
// be recorded in pending_outputs_, which is inserted into "live".
|
||||
@@ -427,7 +443,8 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
//
|
||||
// TODO(yhchiang): carefully modify the third condition to safely
|
||||
// remove the temp options files.
|
||||
keep = (sst_live_map.find(number) != sst_live_map.end()) ||
|
||||
keep = (sst_live_set.find(number) != sst_live_set.end()) ||
|
||||
(blob_live_set.find(number) != blob_live_set.end()) ||
|
||||
(number == state.pending_manifest_file_number) ||
|
||||
(to_delete.find(kOptionsFileNamePrefix) != std::string::npos);
|
||||
break;
|
||||
@@ -450,7 +467,6 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
case kDBLockFile:
|
||||
case kIdentityFile:
|
||||
case kMetaDatabase:
|
||||
case kBlobFile:
|
||||
keep = true;
|
||||
break;
|
||||
}
|
||||
@@ -466,6 +482,9 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
TableCache::Evict(table_cache_.get(), number);
|
||||
fname = MakeTableFileName(candidate_file.file_path, number);
|
||||
dir_to_sync = candidate_file.file_path;
|
||||
} else if (type == kBlobFile) {
|
||||
fname = BlobFileName(candidate_file.file_path, number);
|
||||
dir_to_sync = candidate_file.file_path;
|
||||
} else {
|
||||
dir_to_sync =
|
||||
(type == kLogFile) ? immutable_db_options_.wal_dir : dbname_;
|
||||
@@ -664,4 +683,72 @@ uint64_t PrecomputeMinLogNumberToKeep(
|
||||
return min_log_number_to_keep;
|
||||
}
|
||||
|
||||
Status DBImpl::FinishBestEffortsRecovery() {
|
||||
mutex_.AssertHeld();
|
||||
std::vector<std::string> paths;
|
||||
paths.push_back(NormalizePath(dbname_ + std::string(1, kFilePathSeparator)));
|
||||
for (const auto& db_path : immutable_db_options_.db_paths) {
|
||||
paths.push_back(
|
||||
NormalizePath(db_path.path + std::string(1, kFilePathSeparator)));
|
||||
}
|
||||
for (const auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (const auto& cf_path : cfd->ioptions()->cf_paths) {
|
||||
paths.push_back(
|
||||
NormalizePath(cf_path.path + std::string(1, kFilePathSeparator)));
|
||||
}
|
||||
}
|
||||
// Dedup paths
|
||||
std::sort(paths.begin(), paths.end());
|
||||
paths.erase(std::unique(paths.begin(), paths.end()), paths.end());
|
||||
|
||||
uint64_t next_file_number = versions_->current_next_file_number();
|
||||
uint64_t largest_file_number = next_file_number;
|
||||
std::set<std::string> files_to_delete;
|
||||
for (const auto& path : paths) {
|
||||
std::vector<std::string> files;
|
||||
env_->GetChildren(path, &files);
|
||||
for (const auto& fname : files) {
|
||||
uint64_t number = 0;
|
||||
FileType type;
|
||||
if (!ParseFileName(fname, &number, &type)) {
|
||||
continue;
|
||||
}
|
||||
// path ends with '/' or '\\'
|
||||
const std::string normalized_fpath = path + fname;
|
||||
largest_file_number = std::max(largest_file_number, number);
|
||||
if (type == kTableFile && number >= next_file_number &&
|
||||
files_to_delete.find(normalized_fpath) == files_to_delete.end()) {
|
||||
files_to_delete.insert(normalized_fpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (largest_file_number > next_file_number) {
|
||||
versions_->next_file_number_.store(largest_file_number + 1);
|
||||
}
|
||||
|
||||
VersionEdit edit;
|
||||
edit.SetNextFile(versions_->next_file_number_.load());
|
||||
assert(versions_->GetColumnFamilySet());
|
||||
ColumnFamilyData* default_cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
assert(default_cfd);
|
||||
// Even if new_descriptor_log is false, we will still switch to a new
|
||||
// MANIFEST and update CURRENT file, since this is in recovery.
|
||||
Status s = versions_->LogAndApply(
|
||||
default_cfd, *default_cfd->GetLatestMutableCFOptions(), &edit, &mutex_,
|
||||
directories_.GetDbDir(), /*new_descriptor_log*/ false);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
mutex_.Unlock();
|
||||
for (const auto& fname : files_to_delete) {
|
||||
s = env_->DeleteFile(fname);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_.Lock();
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+107
-54
@@ -35,16 +35,8 @@ Options SanitizeOptions(const std::string& dbname, const Options& src) {
|
||||
DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
DBOptions result(src);
|
||||
|
||||
if (result.file_system == nullptr) {
|
||||
if (result.env == Env::Default()) {
|
||||
result.file_system = FileSystem::Default();
|
||||
} else {
|
||||
result.file_system.reset(new LegacyFileSystemWrapper(result.env));
|
||||
}
|
||||
} else {
|
||||
if (result.env == nullptr) {
|
||||
result.env = Env::Default();
|
||||
}
|
||||
if (result.env == nullptr) {
|
||||
result.env = Env::Default();
|
||||
}
|
||||
|
||||
// result.max_open_files means an "infinite" open files.
|
||||
@@ -252,6 +244,12 @@ Status DBImpl::ValidateOptions(const DBOptions& db_options) {
|
||||
"atomic_flush is incompatible with enable_pipelined_write");
|
||||
}
|
||||
|
||||
// TODO remove this restriction
|
||||
if (db_options.atomic_flush && db_options.best_efforts_recovery) {
|
||||
return Status::InvalidArgument(
|
||||
"atomic_flush is currently incompatible with best-efforts recovery");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -294,15 +292,16 @@ Status DBImpl::NewDB() {
|
||||
}
|
||||
if (s.ok()) {
|
||||
// Make "CURRENT" file that points to the new manifest file.
|
||||
s = SetCurrentFile(env_, dbname_, 1, directories_.GetDbDir());
|
||||
s = SetCurrentFile(fs_.get(), dbname_, 1, directories_.GetDbDir());
|
||||
} else {
|
||||
fs_->DeleteFile(manifest, IOOptions(), nullptr);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::CreateAndNewDirectory(Env* env, const std::string& dirname,
|
||||
std::unique_ptr<Directory>* directory) {
|
||||
IOStatus DBImpl::CreateAndNewDirectory(
|
||||
FileSystem* fs, const std::string& dirname,
|
||||
std::unique_ptr<FSDirectory>* directory) {
|
||||
// We call CreateDirIfMissing() as the directory may already exist (if we
|
||||
// are reopening a DB), when this happens we don't want creating the
|
||||
// directory to cause an error. However, we need to check if creating the
|
||||
@@ -310,24 +309,24 @@ Status DBImpl::CreateAndNewDirectory(Env* env, const std::string& dirname,
|
||||
// file not existing. One real-world example of this occurring is if
|
||||
// env->CreateDirIfMissing() doesn't create intermediate directories, e.g.
|
||||
// when dbname_ is "dir/db" but when "dir" doesn't exist.
|
||||
Status s = env->CreateDirIfMissing(dirname);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
IOStatus io_s = fs->CreateDirIfMissing(dirname, IOOptions(), nullptr);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
return env->NewDirectory(dirname, directory);
|
||||
return fs->NewDirectory(dirname, IOOptions(), directory, nullptr);
|
||||
}
|
||||
|
||||
Status Directories::SetDirectories(Env* env, const std::string& dbname,
|
||||
const std::string& wal_dir,
|
||||
const std::vector<DbPath>& data_paths) {
|
||||
Status s = DBImpl::CreateAndNewDirectory(env, dbname, &db_dir_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
IOStatus Directories::SetDirectories(FileSystem* fs, const std::string& dbname,
|
||||
const std::string& wal_dir,
|
||||
const std::vector<DbPath>& data_paths) {
|
||||
IOStatus io_s = DBImpl::CreateAndNewDirectory(fs, dbname, &db_dir_);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
if (!wal_dir.empty() && dbname != wal_dir) {
|
||||
s = DBImpl::CreateAndNewDirectory(env, wal_dir, &wal_dir_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
io_s = DBImpl::CreateAndNewDirectory(fs, wal_dir, &wal_dir_);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,16 +336,16 @@ Status Directories::SetDirectories(Env* env, const std::string& dbname,
|
||||
if (db_path == dbname) {
|
||||
data_dirs_.emplace_back(nullptr);
|
||||
} else {
|
||||
std::unique_ptr<Directory> path_directory;
|
||||
s = DBImpl::CreateAndNewDirectory(env, db_path, &path_directory);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
std::unique_ptr<FSDirectory> path_directory;
|
||||
io_s = DBImpl::CreateAndNewDirectory(fs, db_path, &path_directory);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
data_dirs_.emplace_back(path_directory.release());
|
||||
}
|
||||
}
|
||||
assert(data_dirs_.size() == data_paths.size());
|
||||
return Status::OK();
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::Recover(
|
||||
@@ -358,7 +357,7 @@ Status DBImpl::Recover(
|
||||
bool is_new_db = false;
|
||||
assert(db_lock_ == nullptr);
|
||||
if (!read_only) {
|
||||
Status s = directories_.SetDirectories(env_, dbname_,
|
||||
Status s = directories_.SetDirectories(fs_.get(), dbname_,
|
||||
immutable_db_options_.wal_dir,
|
||||
immutable_db_options_.db_paths);
|
||||
if (!s.ok()) {
|
||||
@@ -371,7 +370,30 @@ Status DBImpl::Recover(
|
||||
}
|
||||
|
||||
std::string current_fname = CurrentFileName(dbname_);
|
||||
s = env_->FileExists(current_fname);
|
||||
// Path to any MANIFEST file in the db dir. It does not matter which one.
|
||||
// Since best-efforts recovery ignores CURRENT file, existence of a
|
||||
// MANIFEST indicates the recovery to recover existing db. If no MANIFEST
|
||||
// can be found, a new db will be created.
|
||||
std::string manifest_path;
|
||||
if (!immutable_db_options_.best_efforts_recovery) {
|
||||
s = env_->FileExists(current_fname);
|
||||
} else {
|
||||
s = Status::NotFound();
|
||||
std::vector<std::string> files;
|
||||
// No need to check return value
|
||||
env_->GetChildren(dbname_, &files);
|
||||
for (const std::string& file : files) {
|
||||
uint64_t number = 0;
|
||||
FileType type = kLogFile; // initialize
|
||||
if (ParseFileName(file, &number, &type) && type == kDescriptorFile) {
|
||||
// Found MANIFEST (descriptor log), thus best-efforts recovery does
|
||||
// not have to treat the db as empty.
|
||||
s = Status::OK();
|
||||
manifest_path = dbname_ + "/" + file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.IsNotFound()) {
|
||||
if (immutable_db_options_.create_if_missing) {
|
||||
s = NewDB();
|
||||
@@ -399,14 +421,14 @@ Status DBImpl::Recover(
|
||||
FileOptions customized_fs(file_options_);
|
||||
customized_fs.use_direct_reads |=
|
||||
immutable_db_options_.use_direct_io_for_flush_and_compaction;
|
||||
s = fs_->NewRandomAccessFile(current_fname, customized_fs, &idfile,
|
||||
nullptr);
|
||||
const std::string& fname =
|
||||
manifest_path.empty() ? current_fname : manifest_path;
|
||||
s = fs_->NewRandomAccessFile(fname, customized_fs, &idfile, nullptr);
|
||||
if (!s.ok()) {
|
||||
std::string error_str = s.ToString();
|
||||
// Check if unsupported Direct I/O is the root cause
|
||||
customized_fs.use_direct_reads = false;
|
||||
s = fs_->NewRandomAccessFile(current_fname, customized_fs, &idfile,
|
||||
nullptr);
|
||||
s = fs_->NewRandomAccessFile(fname, customized_fs, &idfile, nullptr);
|
||||
if (s.ok()) {
|
||||
return Status::InvalidArgument(
|
||||
"Direct I/O is not supported by the specified DB.");
|
||||
@@ -418,7 +440,20 @@ Status DBImpl::Recover(
|
||||
}
|
||||
}
|
||||
assert(db_id_.empty());
|
||||
Status s = versions_->Recover(column_families, read_only, &db_id_);
|
||||
Status s;
|
||||
bool missing_table_file = false;
|
||||
if (!immutable_db_options_.best_efforts_recovery) {
|
||||
s = versions_->Recover(column_families, read_only, &db_id_);
|
||||
} else {
|
||||
s = versions_->TryRecover(column_families, read_only, &db_id_,
|
||||
&missing_table_file);
|
||||
if (s.ok()) {
|
||||
// TryRecover may delete previous column_family_set_.
|
||||
column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(versions_->GetColumnFamilySet()));
|
||||
s = FinishBestEffortsRecovery();
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -458,7 +493,7 @@ Status DBImpl::Recover(
|
||||
s = CheckConsistency();
|
||||
}
|
||||
if (s.ok() && !read_only) {
|
||||
std::map<std::string, std::shared_ptr<Directory>> created_dirs;
|
||||
std::map<std::string, std::shared_ptr<FSDirectory>> created_dirs;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
s = cfd->AddDirectories(&created_dirs);
|
||||
if (!s.ok()) {
|
||||
@@ -498,7 +533,9 @@ Status DBImpl::Recover(
|
||||
// attention to it in case we are recovering a database
|
||||
// produced by an older version of rocksdb.
|
||||
std::vector<std::string> filenames;
|
||||
s = env_->GetChildren(immutable_db_options_.wal_dir, &filenames);
|
||||
if (!immutable_db_options_.best_efforts_recovery) {
|
||||
s = env_->GetChildren(immutable_db_options_.wal_dir, &filenames);
|
||||
}
|
||||
if (s.IsNotFound()) {
|
||||
return Status::InvalidArgument("wal_dir not found",
|
||||
immutable_db_options_.wal_dir);
|
||||
@@ -690,10 +727,10 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
Status* status; // nullptr if immutable_db_options_.paranoid_checks==false
|
||||
void Corruption(size_t bytes, const Status& s) override {
|
||||
ROCKS_LOG_WARN(info_log, "%s%s: dropping %d bytes; %s",
|
||||
(this->status == nullptr ? "(ignoring error) " : ""),
|
||||
fname, static_cast<int>(bytes), s.ToString().c_str());
|
||||
if (this->status != nullptr && this->status->ok()) {
|
||||
*this->status = s;
|
||||
(status == nullptr ? "(ignoring error) " : ""), fname,
|
||||
static_cast<int>(bytes), s.ToString().c_str());
|
||||
if (status != nullptr && status->ok()) {
|
||||
*status = s;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -816,6 +853,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
Slice record;
|
||||
WriteBatch batch;
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::RecoverLogFiles:BeforeReadWal",
|
||||
/*arg=*/nullptr);
|
||||
while (!stop_replay_by_wal_filter &&
|
||||
reader.ReadRecord(&record, &scratch,
|
||||
immutable_db_options_.wal_recovery_mode) &&
|
||||
@@ -980,6 +1019,16 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
status = Status::OK();
|
||||
} else if (immutable_db_options_.wal_recovery_mode ==
|
||||
WALRecoveryMode::kPointInTimeRecovery) {
|
||||
if (status.IsIOError()) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"IOError during point-in-time reading log #%" PRIu64
|
||||
" seq #%" PRIu64
|
||||
". %s. This likely mean loss of synced WAL, "
|
||||
"thus recovery fails.",
|
||||
log_number, *next_sequence,
|
||||
status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
// We should ignore the error but not continue replaying
|
||||
status = Status::OK();
|
||||
stop_replay_for_corruption = true;
|
||||
@@ -1228,6 +1277,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
if (range_del_iter != nullptr) {
|
||||
range_del_iters.emplace_back(range_del_iter);
|
||||
}
|
||||
IOStatus io_s;
|
||||
s = BuildTable(
|
||||
dbname_, env_, fs_.get(), *cfd->ioptions(), mutable_cf_options,
|
||||
file_options_for_compaction_, cfd->table_cache(), iter.get(),
|
||||
@@ -1236,10 +1286,11 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
mutable_cf_options.sample_for_compression,
|
||||
cfd->ioptions()->compression_opts, paranoid_file_checks,
|
||||
cfd->internal_stats(), TableFileCreationReason::kRecovery,
|
||||
mutable_cf_options.compression_opts, paranoid_file_checks,
|
||||
cfd->internal_stats(), TableFileCreationReason::kRecovery, &io_s,
|
||||
&event_logger_, job_id, Env::IO_HIGH, nullptr /* table_properties */,
|
||||
-1 /* level */, current_time, write_hint);
|
||||
-1 /* level */, current_time, 0 /* oldest_key_time */, write_hint,
|
||||
0 /* file_creation_time */, db_id_, db_session_id_);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] [WriteLevel0TableForRecovery]"
|
||||
@@ -1400,13 +1451,9 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
impl->error_handler_.EnableAutoRecovery();
|
||||
}
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
delete impl;
|
||||
return s;
|
||||
if (s.ok()) {
|
||||
s = impl->CreateArchivalDirectory();
|
||||
}
|
||||
|
||||
s = impl->CreateArchivalDirectory();
|
||||
if (!s.ok()) {
|
||||
delete impl;
|
||||
return s;
|
||||
@@ -1477,7 +1524,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
|
||||
impl->DeleteObsoleteFiles();
|
||||
s = impl->directories_.GetDbDir()->Fsync();
|
||||
s = impl->directories_.GetDbDir()->Fsync(IOOptions(), nullptr);
|
||||
}
|
||||
if (s.ok()) {
|
||||
// In WritePrepared there could be gap in sequence numbers. This breaks
|
||||
@@ -1558,6 +1605,12 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(
|
||||
impl->immutable_db_options_.sst_file_manager.get());
|
||||
if (s.ok() && sfm) {
|
||||
// Set Statistics ptr for SstFileManager to dump the stats of
|
||||
// DeleteScheduler.
|
||||
sfm->SetStatisticsPtr(impl->immutable_db_options_.statistics);
|
||||
ROCKS_LOG_INFO(impl->immutable_db_options_.info_log,
|
||||
"SstFileManager instance %p", sfm);
|
||||
|
||||
// Notify SstFileManager about all sst files that already exist in
|
||||
// db_paths[0] and cf_paths[0] when the DB is opened.
|
||||
|
||||
|
||||
@@ -48,14 +48,16 @@ Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
LookupKey lkey(key, snapshot);
|
||||
PERF_TIMER_STOP(get_snapshot_time);
|
||||
if (super_version->mem->Get(lkey, pinnable_val->GetSelf(), &s, &merge_context,
|
||||
if (super_version->mem->Get(lkey, pinnable_val->GetSelf(),
|
||||
/*timestamp=*/nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options)) {
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
super_version->current->Get(read_options, lkey, pinnable_val, &s,
|
||||
&merge_context, &max_covering_tombstone_seq);
|
||||
super_version->current->Get(read_options, lkey, pinnable_val,
|
||||
/*timestamp=*/nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq);
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
RecordTick(stats_, NUMBER_KEYS_READ);
|
||||
@@ -85,7 +87,8 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
super_version->version_number, read_callback);
|
||||
auto internal_iter =
|
||||
NewInternalIterator(read_options, cfd, super_version, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), read_seq);
|
||||
db_iter->GetRangeDelAggregator(), read_seq,
|
||||
/* allow_unprepared_value */ true);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
return db_iter;
|
||||
}
|
||||
@@ -116,7 +119,8 @@ Status DBImplReadOnly::NewIterators(
|
||||
sv->version_number, read_callback);
|
||||
auto* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), read_seq);
|
||||
db_iter->GetRangeDelAggregator(), read_seq,
|
||||
/* allow_unprepared_value */ true);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
iterators->push_back(db_iter);
|
||||
}
|
||||
@@ -124,12 +128,38 @@ Status DBImplReadOnly::NewIterators(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Return OK if dbname exists in the file system
|
||||
// or create_if_missing is false
|
||||
Status OpenForReadOnlyCheckExistence(const DBOptions& db_options,
|
||||
const std::string& dbname) {
|
||||
Status s;
|
||||
if (!db_options.create_if_missing) {
|
||||
// Attempt to read "CURRENT" file
|
||||
const std::shared_ptr<FileSystem>& fs = db_options.env->GetFileSystem();
|
||||
std::string manifest_path;
|
||||
uint64_t manifest_file_number;
|
||||
s = VersionSet::GetCurrentManifestPath(dbname, fs.get(), &manifest_path,
|
||||
&manifest_file_number);
|
||||
if (!s.ok()) {
|
||||
return Status::NotFound(CurrentFileName(dbname), "does not exist");
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
|
||||
DB** dbptr, bool /*error_if_log_file_exist*/) {
|
||||
// If dbname does not exist in the file system, should not do anything
|
||||
Status s = OpenForReadOnlyCheckExistence(options, dbname);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
*dbptr = nullptr;
|
||||
|
||||
// Try to first open DB as fully compacted DB
|
||||
Status s;
|
||||
s = CompactedDBImpl::Open(options, dbname, dbptr);
|
||||
if (s.ok()) {
|
||||
return s;
|
||||
@@ -142,7 +172,8 @@ Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
|
||||
s = DB::OpenForReadOnly(db_options, dbname, column_families, &handles, dbptr);
|
||||
s = DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
db_options, dbname, column_families, &handles, dbptr);
|
||||
if (s.ok()) {
|
||||
assert(handles.size() == 1);
|
||||
// i can delete the handle since DBImpl is always holding a
|
||||
@@ -157,6 +188,22 @@ Status DB::OpenForReadOnly(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
bool error_if_log_file_exist) {
|
||||
// If dbname does not exist in the file system, should not do anything
|
||||
Status s = OpenForReadOnlyCheckExistence(db_options, dbname);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
return DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
db_options, dbname, column_families, handles, dbptr,
|
||||
error_if_log_file_exist);
|
||||
}
|
||||
|
||||
Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
bool error_if_log_file_exist) {
|
||||
*dbptr = nullptr;
|
||||
handles->clear();
|
||||
|
||||
|
||||
@@ -130,6 +130,15 @@ class DBImplReadOnly : public DBImpl {
|
||||
}
|
||||
|
||||
private:
|
||||
// A "helper" function for DB::OpenForReadOnly without column families
|
||||
// to reduce unnecessary I/O
|
||||
// It has the same functionality as DB::OpenForReadOnly with column families
|
||||
// but does not check the existence of dbname in the file system
|
||||
static Status OpenForReadOnlyWithoutCheck(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
bool error_if_log_file_exist = false);
|
||||
friend class DB;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -340,15 +340,16 @@ Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
|
||||
PERF_TIMER_STOP(get_snapshot_time);
|
||||
|
||||
bool done = false;
|
||||
if (super_version->mem->Get(lkey, pinnable_val->GetSelf(), &s, &merge_context,
|
||||
if (super_version->mem->Get(lkey, pinnable_val->GetSelf(),
|
||||
/*timestamp=*/nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options)) {
|
||||
done = true;
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if ((s.ok() || s.IsMergeInProgress()) &&
|
||||
super_version->imm->Get(
|
||||
lkey, pinnable_val->GetSelf(), &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options)) {
|
||||
lkey, pinnable_val->GetSelf(), /*timestamp=*/nullptr, &s,
|
||||
&merge_context, &max_covering_tombstone_seq, read_options)) {
|
||||
done = true;
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
@@ -359,8 +360,9 @@ Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
|
||||
}
|
||||
if (!done) {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
super_version->current->Get(read_options, lkey, pinnable_val, &s,
|
||||
&merge_context, &max_covering_tombstone_seq);
|
||||
super_version->current->Get(read_options, lkey, pinnable_val,
|
||||
/*timestamp=*/nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq);
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
{
|
||||
@@ -415,7 +417,8 @@ ArenaWrappedDBIter* DBImplSecondary::NewIteratorImpl(
|
||||
super_version->version_number, read_callback);
|
||||
auto internal_iter =
|
||||
NewInternalIterator(read_options, cfd, super_version, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), snapshot);
|
||||
db_iter->GetRangeDelAggregator(), snapshot,
|
||||
/* allow_unprepared_value */ true);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
+92
-40
@@ -101,6 +101,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
disable_memtable);
|
||||
|
||||
Status status;
|
||||
IOStatus io_s;
|
||||
if (write_options.low_pri) {
|
||||
status = ThrottleLowPriWritesIfNeeded(write_options, my_batch);
|
||||
if (!status.ok()) {
|
||||
@@ -322,21 +323,22 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
if (!two_write_queues_) {
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
status = WriteToWAL(write_group, log_writer, log_used, need_log_sync,
|
||||
need_log_dir_sync, last_sequence + 1);
|
||||
io_s = WriteToWAL(write_group, log_writer, log_used, need_log_sync,
|
||||
need_log_dir_sync, last_sequence + 1);
|
||||
}
|
||||
} else {
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
// LastAllocatedSequence is increased inside WriteToWAL under
|
||||
// wal_write_mutex_ to ensure ordered events in WAL
|
||||
status = ConcurrentWriteToWAL(write_group, log_used, &last_sequence,
|
||||
seq_inc);
|
||||
io_s = ConcurrentWriteToWAL(write_group, log_used, &last_sequence,
|
||||
seq_inc);
|
||||
} else {
|
||||
// Otherwise we inc seq number for memtable writes
|
||||
last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
|
||||
}
|
||||
}
|
||||
status = io_s;
|
||||
assert(last_sequence != kMaxSequenceNumber);
|
||||
const SequenceNumber current_sequence = last_sequence + 1;
|
||||
last_sequence += seq_inc;
|
||||
@@ -411,7 +413,11 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
WriteStatusCheck(status);
|
||||
if (!io_s.ok()) {
|
||||
IOStatusCheck(io_s);
|
||||
} else {
|
||||
WriteStatusCheck(status);
|
||||
}
|
||||
}
|
||||
|
||||
if (need_log_sync) {
|
||||
@@ -515,6 +521,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
|
||||
IOStatus io_s;
|
||||
if (w.status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneBySelf, 1);
|
||||
@@ -524,12 +531,17 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
wal_write_group.size - 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, wal_write_group.size - 1);
|
||||
}
|
||||
w.status = WriteToWAL(wal_write_group, log_writer, log_used,
|
||||
need_log_sync, need_log_dir_sync, current_sequence);
|
||||
io_s = WriteToWAL(wal_write_group, log_writer, log_used, need_log_sync,
|
||||
need_log_dir_sync, current_sequence);
|
||||
w.status = io_s;
|
||||
}
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
WriteStatusCheck(w.status);
|
||||
if (!io_s.ok()) {
|
||||
IOStatusCheck(io_s);
|
||||
} else {
|
||||
WriteStatusCheck(w.status);
|
||||
}
|
||||
}
|
||||
|
||||
if (need_log_sync) {
|
||||
@@ -740,9 +752,10 @@ Status DBImpl::WriteImplWALOnly(
|
||||
}
|
||||
seq_inc = total_batch_cnt;
|
||||
}
|
||||
IOStatus io_s;
|
||||
if (!write_options.disableWAL) {
|
||||
status =
|
||||
ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
|
||||
io_s = ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
|
||||
status = io_s;
|
||||
} else {
|
||||
// Otherwise we inc seq number to do solely the seq allocation
|
||||
last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
|
||||
@@ -777,7 +790,11 @@ Status DBImpl::WriteImplWALOnly(
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
WriteStatusCheck(status);
|
||||
if (!io_s.ok()) {
|
||||
IOStatusCheck(io_s);
|
||||
} else {
|
||||
WriteStatusCheck(status);
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
size_t index = 0;
|
||||
@@ -823,6 +840,17 @@ void DBImpl::WriteStatusCheck(const Status& status) {
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::IOStatusCheck(const IOStatus& io_status) {
|
||||
// Is setting bg_error_ enough here? This will at least stop
|
||||
// compaction and fail any further writes.
|
||||
if (immutable_db_options_.paranoid_checks && !io_status.ok() &&
|
||||
!io_status.IsBusy() && !io_status.IsIncomplete()) {
|
||||
mutex_.Lock();
|
||||
error_handler_.SetBGError(io_status, BackgroundErrorReason::kWriteCallback);
|
||||
mutex_.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::MemTableInsertStatusCheck(const Status& status) {
|
||||
// A non-OK status here indicates that the state implied by the
|
||||
// WAL has diverged from the in-memory state. This could be
|
||||
@@ -961,9 +989,9 @@ WriteBatch* DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
|
||||
// When two_write_queues_ is disabled, this function is called from the only
|
||||
// write thread. Otherwise this must be called holding log_write_mutex_.
|
||||
Status DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
uint64_t* log_size) {
|
||||
IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
uint64_t* log_size) {
|
||||
assert(log_size != nullptr);
|
||||
Slice log_entry = WriteBatchInternal::Contents(&merged_batch);
|
||||
*log_size = log_entry.size();
|
||||
@@ -978,7 +1006,8 @@ Status DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
Status status = log_writer->AddRecord(log_entry);
|
||||
IOStatus io_s = log_writer->AddRecord(log_entry);
|
||||
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
@@ -990,15 +1019,14 @@ Status DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
// since alive_log_files_ might be modified concurrently
|
||||
alive_log_files_.back().AddSize(log_entry.size());
|
||||
log_empty_ = false;
|
||||
return status;
|
||||
return io_s;
|
||||
}
|
||||
|
||||
Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence) {
|
||||
Status status;
|
||||
|
||||
IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence) {
|
||||
IOStatus io_s;
|
||||
assert(!write_group.leader->disable_wal);
|
||||
// Same holds for all in the batch group
|
||||
size_t write_with_wal = 0;
|
||||
@@ -1016,13 +1044,13 @@ Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
WriteBatchInternal::SetSequence(merged_batch, sequence);
|
||||
|
||||
uint64_t log_size;
|
||||
status = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
io_s = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
}
|
||||
|
||||
if (status.ok() && need_log_sync) {
|
||||
if (io_s.ok() && need_log_sync) {
|
||||
StopWatch sw(env_, stats_, WAL_FILE_SYNC_MICROS);
|
||||
// It's safe to access logs_ with unlocked mutex_ here because:
|
||||
// - we've set getting_synced=true for all logs,
|
||||
@@ -1032,23 +1060,24 @@ Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
// - as long as other threads don't modify it, it's safe to read
|
||||
// from std::deque from multiple threads concurrently.
|
||||
for (auto& log : logs_) {
|
||||
status = log.writer->file()->Sync(immutable_db_options_.use_fsync);
|
||||
if (!status.ok()) {
|
||||
io_s = log.writer->file()->Sync(immutable_db_options_.use_fsync);
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (status.ok() && need_log_dir_sync) {
|
||||
|
||||
if (io_s.ok() && need_log_dir_sync) {
|
||||
// We only sync WAL directory the first time WAL syncing is
|
||||
// requested, so that in case users never turn on WAL sync,
|
||||
// we can avoid the disk I/O in the write code path.
|
||||
status = directories_.GetWalDir()->Fsync();
|
||||
io_s = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (merged_batch == &tmp_batch_) {
|
||||
tmp_batch_.Clear();
|
||||
}
|
||||
if (status.ok()) {
|
||||
if (io_s.ok()) {
|
||||
auto stats = default_cf_internal_stats_;
|
||||
if (need_log_sync) {
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileSynced, 1);
|
||||
@@ -1059,14 +1088,13 @@ Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteWithWal, write_with_wal);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
}
|
||||
return status;
|
||||
return io_s;
|
||||
}
|
||||
|
||||
Status DBImpl::ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
uint64_t* log_used,
|
||||
SequenceNumber* last_sequence,
|
||||
size_t seq_inc) {
|
||||
Status status;
|
||||
IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
const WriteThread::WriteGroup& write_group, uint64_t* log_used,
|
||||
SequenceNumber* last_sequence, size_t seq_inc) {
|
||||
IOStatus io_s;
|
||||
|
||||
assert(!write_group.leader->disable_wal);
|
||||
// Same holds for all in the batch group
|
||||
@@ -1092,14 +1120,14 @@ Status DBImpl::ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
uint64_t log_size;
|
||||
status = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
io_s = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
|
||||
if (status.ok()) {
|
||||
if (io_s.ok()) {
|
||||
const bool concurrent = true;
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileBytes, log_size,
|
||||
@@ -1109,7 +1137,7 @@ Status DBImpl::ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
concurrent);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
}
|
||||
return status;
|
||||
return io_s;
|
||||
}
|
||||
|
||||
Status DBImpl::WriteRecoverableState() {
|
||||
@@ -1792,6 +1820,8 @@ Status DB::Put(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
const Slice* ts = opt.timestamp;
|
||||
assert(nullptr != ts);
|
||||
size_t ts_sz = ts->size();
|
||||
assert(column_family->GetComparator());
|
||||
assert(ts_sz == column_family->GetComparator()->timestamp_size());
|
||||
WriteBatch batch(key.size() + ts_sz + value.size() + 24, /*max_bytes=*/0,
|
||||
ts_sz);
|
||||
Status s = batch.Put(column_family, key, value);
|
||||
@@ -1807,8 +1837,30 @@ Status DB::Put(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
|
||||
Status DB::Delete(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
const Slice& key) {
|
||||
WriteBatch batch;
|
||||
batch.Delete(column_family, key);
|
||||
if (nullptr == opt.timestamp) {
|
||||
WriteBatch batch;
|
||||
Status s = batch.Delete(column_family, key);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
const Slice* ts = opt.timestamp;
|
||||
assert(ts != nullptr);
|
||||
const size_t ts_sz = ts->size();
|
||||
constexpr size_t kKeyAndValueLenSize = 11;
|
||||
constexpr size_t kWriteBatchOverhead =
|
||||
WriteBatchInternal::kHeader + sizeof(ValueType) + kKeyAndValueLenSize;
|
||||
WriteBatch batch(key.size() + ts_sz + kWriteBatchOverhead, /*max_bytes=*/0,
|
||||
ts_sz);
|
||||
Status s = batch.Delete(column_family, key);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
s = batch.AssignTimestamp(*ts);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ class DBSecondaryTest : public DBTestBase {
|
||||
|
||||
void OpenSecondary(const Options& options);
|
||||
|
||||
Status TryOpenSecondary(const Options& options);
|
||||
|
||||
void OpenSecondaryWithColumnFamilies(
|
||||
const std::vector<std::string>& column_families, const Options& options);
|
||||
|
||||
@@ -70,9 +72,13 @@ class DBSecondaryTest : public DBTestBase {
|
||||
};
|
||||
|
||||
void DBSecondaryTest::OpenSecondary(const Options& options) {
|
||||
ASSERT_OK(TryOpenSecondary(options));
|
||||
}
|
||||
|
||||
Status DBSecondaryTest::TryOpenSecondary(const Options& options) {
|
||||
Status s =
|
||||
DB::OpenAsSecondary(options, dbname_, secondary_path_, &db_secondary_);
|
||||
ASSERT_OK(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
void DBSecondaryTest::OpenSecondaryWithColumnFamilies(
|
||||
@@ -858,6 +864,56 @@ TEST_F(DBSecondaryTest, CheckConsistencyWhenOpen) {
|
||||
thread.join();
|
||||
ASSERT_TRUE(called);
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, StartFromInconsistent) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Options options1;
|
||||
Status s = TryOpenSecondary(options1);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, InconsistencyDuringCatchUp) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Options options1;
|
||||
OpenSecondary(options1);
|
||||
|
||||
{
|
||||
std::string value;
|
||||
ASSERT_OK(db_secondary_->Get(ReadOptions(), "foo", &value));
|
||||
ASSERT_EQ("value", value);
|
||||
}
|
||||
|
||||
ASSERT_OK(Put("bar", "value1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = db_secondary_->TryCatchUpWithPrimary();
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
#endif //! ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
const std::string& dbname) {
|
||||
const std::string& dbname,
|
||||
const std::string& session_id) {
|
||||
if (options.info_log == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -32,6 +33,8 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
std::string file_info, wal_info;
|
||||
|
||||
Header(options.info_log, "DB SUMMARY\n");
|
||||
Header(options.info_log, "DB Session ID: %s\n", session_id.c_str());
|
||||
|
||||
// Get files in dbname dir
|
||||
if (!env->GetChildren(dbname, &files).ok()) {
|
||||
Error(options.info_log,
|
||||
|
||||
+2
-1
@@ -10,5 +10,6 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
const std::string& dbname);
|
||||
const std::string& dbname,
|
||||
const std::string& session_id = "");
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+155
-38
@@ -49,6 +49,8 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
read_callback_(read_callback),
|
||||
sequence_(s),
|
||||
statistics_(cf_options.statistics),
|
||||
max_skip_(max_sequential_skip_in_iterations),
|
||||
max_skippable_internal_keys_(read_options.max_skippable_internal_keys),
|
||||
num_internal_keys_skipped_(0),
|
||||
iterate_lower_bound_(read_options.iterate_lower_bound),
|
||||
iterate_upper_bound_(read_options.iterate_upper_bound),
|
||||
@@ -69,16 +71,18 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
range_del_agg_(&cf_options.internal_comparator, s),
|
||||
db_impl_(db_impl),
|
||||
cfd_(cfd),
|
||||
start_seqnum_(read_options.iter_start_seqnum) {
|
||||
start_seqnum_(read_options.iter_start_seqnum),
|
||||
timestamp_ub_(read_options.timestamp),
|
||||
timestamp_lb_(read_options.iter_start_ts),
|
||||
timestamp_size_(timestamp_ub_ ? timestamp_ub_->size() : 0) {
|
||||
RecordTick(statistics_, NO_ITERATOR_CREATED);
|
||||
max_skip_ = max_sequential_skip_in_iterations;
|
||||
max_skippable_internal_keys_ = read_options.max_skippable_internal_keys;
|
||||
if (pin_thru_lifetime_) {
|
||||
pinned_iters_mgr_.StartPinning();
|
||||
}
|
||||
if (iter_.iter()) {
|
||||
iter_.iter()->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
}
|
||||
assert(timestamp_size_ == user_comparator_.timestamp_size());
|
||||
}
|
||||
|
||||
Status DBIter::GetProperty(std::string prop_name, std::string* prop) {
|
||||
@@ -220,9 +224,13 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
is_key_seqnum_zero_ = (ikey_.sequence == 0);
|
||||
|
||||
assert(iterate_upper_bound_ == nullptr || iter_.MayBeOutOfUpperBound() ||
|
||||
user_comparator_.Compare(ikey_.user_key, *iterate_upper_bound_) < 0);
|
||||
user_comparator_.CompareWithoutTimestamp(
|
||||
ikey_.user_key, /*a_has_ts=*/true, *iterate_upper_bound_,
|
||||
/*b_has_ts=*/false) < 0);
|
||||
if (iterate_upper_bound_ != nullptr && iter_.MayBeOutOfUpperBound() &&
|
||||
user_comparator_.Compare(ikey_.user_key, *iterate_upper_bound_) >= 0) {
|
||||
user_comparator_.CompareWithoutTimestamp(
|
||||
ikey_.user_key, /*a_has_ts=*/true, *iterate_upper_bound_,
|
||||
/*b_has_ts=*/false) >= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -237,24 +245,38 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsVisible(ikey_.sequence)) {
|
||||
assert(ikey_.user_key.size() >= timestamp_size_);
|
||||
Slice ts;
|
||||
bool more_recent = false;
|
||||
if (timestamp_size_ > 0) {
|
||||
ts = ExtractTimestampFromUserKey(ikey_.user_key, timestamp_size_);
|
||||
}
|
||||
if (IsVisible(ikey_.sequence, ts, &more_recent)) {
|
||||
// If the previous entry is of seqnum 0, the current entry will not
|
||||
// possibly be skipped. This condition can potentially be relaxed to
|
||||
// prev_key.seq <= ikey_.sequence. We are cautious because it will be more
|
||||
// prone to bugs causing the same user key with the same sequence number.
|
||||
if (!is_prev_key_seqnum_zero && skipping_saved_key &&
|
||||
user_comparator_.Compare(ikey_.user_key, saved_key_.GetUserKey()) <=
|
||||
0) {
|
||||
// Note that with current timestamp implementation, the same user key can
|
||||
// have different timestamps and zero sequence number on the bottommost
|
||||
// level. This may change in the future.
|
||||
if ((!is_prev_key_seqnum_zero || timestamp_size_ > 0) &&
|
||||
skipping_saved_key &&
|
||||
CompareKeyForSkip(ikey_.user_key, saved_key_.GetUserKey()) <= 0) {
|
||||
num_skipped++; // skip this entry
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
} else {
|
||||
assert(!skipping_saved_key ||
|
||||
user_comparator_.Compare(ikey_.user_key,
|
||||
saved_key_.GetUserKey()) > 0);
|
||||
CompareKeyForSkip(ikey_.user_key, saved_key_.GetUserKey()) > 0);
|
||||
if (!iter_.PrepareValue()) {
|
||||
assert(!iter_.status().ok());
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
num_skipped = 0;
|
||||
reseek_done = false;
|
||||
switch (ikey_.type) {
|
||||
case kTypeDeletion:
|
||||
case kTypeDeletionWithTimestamp:
|
||||
case kTypeSingleDeletion:
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
// they are hidden by this deletion.
|
||||
@@ -351,13 +373,15 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PERF_COUNTER_ADD(internal_recent_skipped_count, 1);
|
||||
if (more_recent) {
|
||||
PERF_COUNTER_ADD(internal_recent_skipped_count, 1);
|
||||
}
|
||||
|
||||
// This key was inserted after our snapshot was taken.
|
||||
// If this happens too many times in a row for the same user key, we want
|
||||
// to seek to the target sequence number.
|
||||
int cmp =
|
||||
user_comparator_.Compare(ikey_.user_key, saved_key_.GetUserKey());
|
||||
// This key was inserted after our snapshot was taken or skipped by
|
||||
// timestamp range. If this happens too many times in a row for the same
|
||||
// user key, we want to seek to the target sequence number.
|
||||
int cmp = user_comparator_.CompareWithoutTimestamp(
|
||||
ikey_.user_key, saved_key_.GetUserKey());
|
||||
if (cmp == 0 || (skipping_saved_key && cmp < 0)) {
|
||||
num_skipped++;
|
||||
} else {
|
||||
@@ -388,8 +412,17 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
// We're looking for the next user-key but all we see are the same
|
||||
// user-key with decreasing sequence numbers. Fast forward to
|
||||
// sequence number 0 and type deletion (the smallest type).
|
||||
AppendInternalKey(&last_key, ParsedInternalKey(saved_key_.GetUserKey(),
|
||||
0, kTypeDeletion));
|
||||
if (timestamp_size_ == 0) {
|
||||
AppendInternalKey(
|
||||
&last_key,
|
||||
ParsedInternalKey(saved_key_.GetUserKey(), 0, kTypeDeletion));
|
||||
} else {
|
||||
std::string min_ts(timestamp_size_, static_cast<char>(0));
|
||||
AppendInternalKeyWithDifferentTimestamp(
|
||||
&last_key,
|
||||
ParsedInternalKey(saved_key_.GetUserKey(), 0, kTypeDeletion),
|
||||
min_ts);
|
||||
}
|
||||
// Don't set skipping_saved_key = false because we may still see more
|
||||
// user-keys equal to saved_key_.
|
||||
} else {
|
||||
@@ -398,9 +431,17 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
// Note that this only covers a case when a higher key was overwritten
|
||||
// many times since our snapshot was taken, not the case when a lot of
|
||||
// different keys were inserted after our snapshot was taken.
|
||||
AppendInternalKey(&last_key,
|
||||
ParsedInternalKey(saved_key_.GetUserKey(), sequence_,
|
||||
kValueTypeForSeek));
|
||||
if (timestamp_size_ == 0) {
|
||||
AppendInternalKey(
|
||||
&last_key, ParsedInternalKey(saved_key_.GetUserKey(), sequence_,
|
||||
kValueTypeForSeek));
|
||||
} else {
|
||||
AppendInternalKeyWithDifferentTimestamp(
|
||||
&last_key,
|
||||
ParsedInternalKey(saved_key_.GetUserKey(), sequence_,
|
||||
kValueTypeForSeek),
|
||||
*timestamp_ub_);
|
||||
}
|
||||
}
|
||||
iter_.Seek(last_key);
|
||||
RecordTick(statistics_, NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
@@ -417,6 +458,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
// Scan from the newer entries to older entries.
|
||||
// PRE: iter_.key() points to the first merge type entry
|
||||
// saved_key_ stores the user key
|
||||
// iter_.PrepareValue() has been called
|
||||
// POST: saved_value_ has the merged value for the user key
|
||||
// iter_ points to the next entry (or invalid)
|
||||
bool DBIter::MergeValuesNewToOld() {
|
||||
@@ -446,14 +488,21 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
if (!user_comparator_.Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
// hit the next user key, stop right here
|
||||
break;
|
||||
} else if (kTypeDeletion == ikey.type || kTypeSingleDeletion == ikey.type ||
|
||||
}
|
||||
if (kTypeDeletion == ikey.type || kTypeSingleDeletion == ikey.type ||
|
||||
range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelPositioningMode::kForwardTraversal)) {
|
||||
// hit a delete with the same user key, stop right here
|
||||
// iter_ is positioned after delete
|
||||
iter_.Next();
|
||||
break;
|
||||
} else if (kTypeValue == ikey.type) {
|
||||
}
|
||||
if (!iter_.PrepareValue()) {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kTypeValue == ikey.type) {
|
||||
// hit a put, merge the put value with operands and store the
|
||||
// final result in saved_value_. We are done!
|
||||
const Slice val = iter_.value();
|
||||
@@ -519,6 +568,13 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
}
|
||||
|
||||
void DBIter::Prev() {
|
||||
if (timestamp_size_ > 0) {
|
||||
valid_ = false;
|
||||
status_ = Status::NotSupported(
|
||||
"SeekToLast/SeekForPrev/Prev currently not supported with timestamp.");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(valid_);
|
||||
assert(status_.ok());
|
||||
|
||||
@@ -697,7 +753,13 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsVisible(ikey.sequence) ||
|
||||
assert(ikey.user_key.size() >= timestamp_size_);
|
||||
Slice ts;
|
||||
if (timestamp_size_ > 0) {
|
||||
ts = Slice(ikey.user_key.data() + ikey.user_key.size() - timestamp_size_,
|
||||
timestamp_size_);
|
||||
}
|
||||
if (!IsVisible(ikey.sequence, ts) ||
|
||||
!user_comparator_.Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
break;
|
||||
}
|
||||
@@ -712,6 +774,11 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
return FindValueForCurrentKeyUsingSeek();
|
||||
}
|
||||
|
||||
if (!iter_.PrepareValue()) {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
last_key_entry_type = ikey.type;
|
||||
switch (last_key_entry_type) {
|
||||
case kTypeValue:
|
||||
@@ -853,6 +920,13 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
if (!ParseKey(&ikey)) {
|
||||
return false;
|
||||
}
|
||||
assert(ikey.user_key.size() >= timestamp_size_);
|
||||
Slice ts;
|
||||
if (timestamp_size_ > 0) {
|
||||
ts = Slice(ikey.user_key.data() + ikey.user_key.size() - timestamp_size_,
|
||||
timestamp_size_);
|
||||
}
|
||||
|
||||
if (!user_comparator_.Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
// No visible values for this key, even though FindValueForCurrentKey()
|
||||
// has seen some. This is possible if we're using a tailing iterator, and
|
||||
@@ -861,7 +935,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsVisible(ikey.sequence)) {
|
||||
if (IsVisible(ikey.sequence, ts)) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -882,6 +956,10 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (!iter_.PrepareValue()) {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (ikey.type == kTypeValue || ikey.type == kTypeBlobIndex) {
|
||||
assert(iter_.iter()->IsValuePinned());
|
||||
pinned_value_ = iter_.value();
|
||||
@@ -913,12 +991,17 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
if (!user_comparator_.Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion ||
|
||||
range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelPositioningMode::kForwardTraversal)) {
|
||||
break;
|
||||
} else if (ikey.type == kTypeValue) {
|
||||
}
|
||||
if (!iter_.PrepareValue()) {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ikey.type == kTypeValue) {
|
||||
const Slice val = iter_.value();
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), &val,
|
||||
@@ -1001,7 +1084,13 @@ bool DBIter::FindUserKeyBeforeSavedKey() {
|
||||
}
|
||||
|
||||
assert(ikey.sequence != kMaxSequenceNumber);
|
||||
if (!IsVisible(ikey.sequence)) {
|
||||
assert(ikey.user_key.size() >= timestamp_size_);
|
||||
Slice ts;
|
||||
if (timestamp_size_ > 0) {
|
||||
ts = Slice(ikey.user_key.data() + ikey.user_key.size() - timestamp_size_,
|
||||
timestamp_size_);
|
||||
}
|
||||
if (!IsVisible(ikey.sequence, ts)) {
|
||||
PERF_COUNTER_ADD(internal_recent_skipped_count, 1);
|
||||
} else {
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
@@ -1046,26 +1135,40 @@ bool DBIter::TooManyInternalKeysSkipped(bool increment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DBIter::IsVisible(SequenceNumber sequence) {
|
||||
if (read_callback_ == nullptr) {
|
||||
return sequence <= sequence_;
|
||||
} else {
|
||||
return read_callback_->IsVisible(sequence);
|
||||
bool DBIter::IsVisible(SequenceNumber sequence, const Slice& ts,
|
||||
bool* more_recent) {
|
||||
// Remember that comparator orders preceding timestamp as larger.
|
||||
// TODO(yanqin): support timestamp in read_callback_.
|
||||
bool visible_by_seq = (read_callback_ == nullptr)
|
||||
? sequence <= sequence_
|
||||
: read_callback_->IsVisible(sequence);
|
||||
|
||||
bool visible_by_ts =
|
||||
(timestamp_ub_ == nullptr ||
|
||||
user_comparator_.CompareTimestamp(ts, *timestamp_ub_) <= 0) &&
|
||||
(timestamp_lb_ == nullptr ||
|
||||
user_comparator_.CompareTimestamp(ts, *timestamp_lb_) >= 0);
|
||||
|
||||
if (more_recent) {
|
||||
*more_recent = !visible_by_seq;
|
||||
}
|
||||
return visible_by_seq && visible_by_ts;
|
||||
}
|
||||
|
||||
void DBIter::SetSavedKeyToSeekTarget(const Slice& target) {
|
||||
is_key_seqnum_zero_ = false;
|
||||
SequenceNumber seq = sequence_;
|
||||
saved_key_.Clear();
|
||||
saved_key_.SetInternalKey(target, seq);
|
||||
saved_key_.SetInternalKey(target, seq, kValueTypeForSeek, timestamp_ub_);
|
||||
|
||||
if (iterate_lower_bound_ != nullptr &&
|
||||
user_comparator_.Compare(saved_key_.GetUserKey(), *iterate_lower_bound_) <
|
||||
0) {
|
||||
user_comparator_.CompareWithoutTimestamp(
|
||||
saved_key_.GetUserKey(), /*a_has_ts=*/true, *iterate_lower_bound_,
|
||||
/*b_has_ts=*/false) < 0) {
|
||||
// Seek key is smaller than the lower bound.
|
||||
saved_key_.Clear();
|
||||
saved_key_.SetInternalKey(*iterate_lower_bound_, seq);
|
||||
saved_key_.SetInternalKey(*iterate_lower_bound_, seq, kValueTypeForSeek,
|
||||
timestamp_ub_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1155,6 +1258,13 @@ void DBIter::SeekForPrev(const Slice& target) {
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
if (timestamp_size_ > 0) {
|
||||
valid_ = false;
|
||||
status_ = Status::NotSupported(
|
||||
"SeekToLast/SeekForPrev/Prev currently not supported with timestamp.");
|
||||
return;
|
||||
}
|
||||
|
||||
status_ = Status::OK();
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
@@ -1248,6 +1358,13 @@ void DBIter::SeekToFirst() {
|
||||
}
|
||||
|
||||
void DBIter::SeekToLast() {
|
||||
if (timestamp_size_ > 0) {
|
||||
valid_ = false;
|
||||
status_ = Status::NotSupported(
|
||||
"SeekToLast/SeekForPrev/Prev currently not supported with timestamp.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (iterate_upper_bound_ != nullptr) {
|
||||
// Seek to last key strictly less than ReadOptions.iterate_upper_bound.
|
||||
SeekForPrev(*iterate_upper_bound_);
|
||||
|
||||
+25
-2
@@ -146,7 +146,8 @@ class DBIter final : public Iterator {
|
||||
if (start_seqnum_ > 0) {
|
||||
return saved_key_.GetInternalKey();
|
||||
} else {
|
||||
return saved_key_.GetUserKey();
|
||||
const Slice ukey_and_ts = saved_key_.GetUserKey();
|
||||
return Slice(ukey_and_ts.data(), ukey_and_ts.size() - timestamp_size_);
|
||||
}
|
||||
}
|
||||
Slice value() const override {
|
||||
@@ -169,6 +170,13 @@ class DBIter final : public Iterator {
|
||||
return status_;
|
||||
}
|
||||
}
|
||||
Slice timestamp() const override {
|
||||
assert(valid_);
|
||||
assert(timestamp_size_ > 0);
|
||||
const Slice ukey_and_ts = saved_key_.GetUserKey();
|
||||
assert(timestamp_size_ < ukey_and_ts.size());
|
||||
return ExtractTimestampFromUserKey(ukey_and_ts, timestamp_size_);
|
||||
}
|
||||
bool IsBlob() const {
|
||||
assert(valid_ && (allow_blob_ || !is_blob_));
|
||||
return is_blob_;
|
||||
@@ -178,6 +186,8 @@ class DBIter final : public Iterator {
|
||||
|
||||
void Next() final override;
|
||||
void Prev() final override;
|
||||
// 'target' does not contain timestamp, even if user timestamp feature is
|
||||
// enabled.
|
||||
void Seek(const Slice& target) final override;
|
||||
void SeekForPrev(const Slice& target) final override;
|
||||
void SeekToFirst() final override;
|
||||
@@ -221,7 +231,8 @@ class DBIter final : public Iterator {
|
||||
// entry can be found within the prefix.
|
||||
void PrevInternal(const Slice* prefix);
|
||||
bool TooManyInternalKeysSkipped(bool increment = true);
|
||||
bool IsVisible(SequenceNumber sequence);
|
||||
bool IsVisible(SequenceNumber sequence, const Slice& ts,
|
||||
bool* more_recent = nullptr);
|
||||
|
||||
// Temporarily pin the blocks that we encounter until ReleaseTempPinnedData()
|
||||
// is called
|
||||
@@ -260,6 +271,15 @@ class DBIter final : public Iterator {
|
||||
return expect_total_order_inner_iter_;
|
||||
}
|
||||
|
||||
// If lower bound of timestamp is given by ReadOptions.iter_start_ts, we need
|
||||
// to return versions of the same key. We cannot just skip if the key value
|
||||
// is the same but timestamps are different but fall in timestamp range.
|
||||
inline int CompareKeyForSkip(const Slice& a, const Slice& b) {
|
||||
return timestamp_lb_ != nullptr
|
||||
? user_comparator_.Compare(a, b)
|
||||
: user_comparator_.CompareWithoutTimestamp(a, b);
|
||||
}
|
||||
|
||||
const SliceTransform* prefix_extractor_;
|
||||
Env* const env_;
|
||||
Logger* logger_;
|
||||
@@ -327,6 +347,9 @@ class DBIter final : public Iterator {
|
||||
// for diff snapshots we want the lower bound on the seqnum;
|
||||
// if this value > 0 iterator will return internal keys
|
||||
SequenceNumber start_seqnum_;
|
||||
const Slice* const timestamp_ub_;
|
||||
const Slice* const timestamp_lb_;
|
||||
const size_t timestamp_size_;
|
||||
};
|
||||
|
||||
// Return a new iterator that converts internal keys (yielded by
|
||||
|
||||
@@ -97,7 +97,8 @@ struct StressTestIterator : public InternalIterator {
|
||||
|
||||
bool MaybeFail() {
|
||||
if (rnd->Next() >=
|
||||
std::numeric_limits<uint64_t>::max() * error_probability) {
|
||||
static_cast<double>(std::numeric_limits<uint64_t>::max()) *
|
||||
error_probability) {
|
||||
return false;
|
||||
}
|
||||
if (rnd->Next() % 2) {
|
||||
@@ -114,7 +115,8 @@ struct StressTestIterator : public InternalIterator {
|
||||
|
||||
void MaybeMutate() {
|
||||
if (rnd->Next() >=
|
||||
std::numeric_limits<uint64_t>::max() * mutation_probability) {
|
||||
static_cast<double>(std::numeric_limits<uint64_t>::max()) *
|
||||
mutation_probability) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
@@ -126,8 +128,9 @@ struct StressTestIterator : public InternalIterator {
|
||||
if (data->hidden.empty()) {
|
||||
hide_probability = 1;
|
||||
}
|
||||
bool do_hide =
|
||||
rnd->Next() < std::numeric_limits<uint64_t>::max() * hide_probability;
|
||||
bool do_hide = rnd->Next() <
|
||||
static_cast<double>(std::numeric_limits<uint64_t>::max()) *
|
||||
hide_probability;
|
||||
if (do_hide) {
|
||||
// Hide a random entry.
|
||||
size_t idx = rnd->Next() % data->entries.size();
|
||||
|
||||
+42
-12
@@ -1167,32 +1167,62 @@ TEST_P(DBIteratorTest, IndexWithFirstKey) {
|
||||
ropt.tailing = tailing;
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ropt));
|
||||
|
||||
ropt.read_tier = ReadTier::kBlockCacheTier;
|
||||
std::unique_ptr<Iterator> nonblocking_iter(NewIterator(ropt));
|
||||
|
||||
iter->Seek("b10");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("b2", iter->key().ToString());
|
||||
EXPECT_EQ("y2", iter->value().ToString());
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
// The cache-only iterator should succeed too, using the blocks pulled into
|
||||
// the cache by the previous iterator.
|
||||
nonblocking_iter->Seek("b10");
|
||||
ASSERT_TRUE(nonblocking_iter->Valid());
|
||||
EXPECT_EQ("b2", nonblocking_iter->key().ToString());
|
||||
EXPECT_EQ("y2", nonblocking_iter->value().ToString());
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
// ... but it shouldn't be able to step forward since the next block is
|
||||
// not in cache yet.
|
||||
nonblocking_iter->Next();
|
||||
ASSERT_FALSE(nonblocking_iter->Valid());
|
||||
ASSERT_TRUE(nonblocking_iter->status().IsIncomplete());
|
||||
|
||||
// ... nor should a seek to the next key succeed.
|
||||
nonblocking_iter->Seek("b20");
|
||||
ASSERT_FALSE(nonblocking_iter->Valid());
|
||||
ASSERT_TRUE(nonblocking_iter->status().IsIncomplete());
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("b3", iter->key().ToString());
|
||||
EXPECT_EQ("y3", iter->value().ToString());
|
||||
EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(4, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
// After the blocking iterator loaded the next block, the nonblocking
|
||||
// iterator's seek should succeed.
|
||||
nonblocking_iter->Seek("b20");
|
||||
ASSERT_TRUE(nonblocking_iter->Valid());
|
||||
EXPECT_EQ("b3", nonblocking_iter->key().ToString());
|
||||
EXPECT_EQ("y3", nonblocking_iter->value().ToString());
|
||||
EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
iter->Seek("c0");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("c0", iter->key().ToString());
|
||||
EXPECT_EQ("z1,z2", iter->value().ToString());
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(4, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(6, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("c3", iter->key().ToString());
|
||||
EXPECT_EQ("z3", iter->value().ToString());
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(7, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter.reset();
|
||||
|
||||
@@ -1207,13 +1237,13 @@ TEST_P(DBIteratorTest, IndexWithFirstKey) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("b2", iter->key().ToString());
|
||||
EXPECT_EQ("y2", iter->value().ToString());
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(7, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(7, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1873,7 +1903,7 @@ TEST_P(DBIteratorTest, IterPrevKeyCrossingBlocksRandomized) {
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kNumKeys = 500;
|
||||
// Small number of merge operands to make sure that DBIter::Prev() dont
|
||||
// Small number of merge operands to make sure that DBIter::Prev() don't
|
||||
// fall back to Seek()
|
||||
const int kNumMergeOperands = 3;
|
||||
// Use value size that will make sure that every block contain 1 key
|
||||
@@ -1908,7 +1938,7 @@ TEST_P(DBIteratorTest, IterPrevKeyCrossingBlocksRandomized) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Separate values and merge operands in different file so that we
|
||||
// make sure that we dont merge them while flushing but actually
|
||||
// make sure that we don't merge them while flushing but actually
|
||||
// merge them in the read path
|
||||
for (int i = 0; i < kNumKeys; i++) {
|
||||
if (rnd.PercentTrue(kNoMergeOpPercentage)) {
|
||||
|
||||
@@ -0,0 +1,513 @@
|
||||
// Copyright (c) 2020-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "test_util/testharness.h"
|
||||
|
||||
#ifdef OS_LINUX
|
||||
#include "env/io_posix.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class EnvWithCustomLogicalBlockSizeCache : public EnvWrapper {
|
||||
public:
|
||||
EnvWithCustomLogicalBlockSizeCache(Env* env, LogicalBlockSizeCache* cache)
|
||||
: EnvWrapper(env), cache_(cache) {}
|
||||
|
||||
Status RegisterDbPaths(const std::vector<std::string>& paths) override {
|
||||
return cache_->RefAndCacheLogicalBlockSize(paths);
|
||||
}
|
||||
|
||||
Status UnregisterDbPaths(const std::vector<std::string>& paths) override {
|
||||
cache_->UnrefAndTryRemoveCachedLogicalBlockSize(paths);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
LogicalBlockSizeCache* cache_;
|
||||
};
|
||||
|
||||
class DBLogicalBlockSizeCacheTest : public testing::Test {
|
||||
public:
|
||||
DBLogicalBlockSizeCacheTest()
|
||||
: dbname_(test::PerThreadDBPath("logical_block_size_cache_test")),
|
||||
data_path_0_(dbname_ + "/data_path_0"),
|
||||
data_path_1_(dbname_ + "/data_path_1"),
|
||||
cf_path_0_(dbname_ + "/cf_path_0"),
|
||||
cf_path_1_(dbname_ + "/cf_path_1") {
|
||||
auto get_fd_block_size = [&](int fd) { return fd; };
|
||||
auto get_dir_block_size = [&](const std::string& /*dir*/, size_t* size) {
|
||||
*size = 1024;
|
||||
return Status::OK();
|
||||
};
|
||||
cache_.reset(
|
||||
new LogicalBlockSizeCache(get_fd_block_size, get_dir_block_size));
|
||||
env_.reset(
|
||||
new EnvWithCustomLogicalBlockSizeCache(Env::Default(), cache_.get()));
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string dbname_;
|
||||
std::string data_path_0_;
|
||||
std::string data_path_1_;
|
||||
std::string cf_path_0_;
|
||||
std::string cf_path_1_;
|
||||
std::unique_ptr<LogicalBlockSizeCache> cache_;
|
||||
std::unique_ptr<Env> env_;
|
||||
};
|
||||
|
||||
TEST_F(DBLogicalBlockSizeCacheTest, OpenClose) {
|
||||
// Tests that Open will cache the logical block size for data paths,
|
||||
// and Close will remove the cached sizes.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_.get();
|
||||
options.db_paths = {{data_path_0_, 2048}, {data_path_1_, 2048}};
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
DB* db;
|
||||
if (!i) {
|
||||
printf("Open\n");
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db));
|
||||
} else {
|
||||
#ifdef ROCKSDB_LITE
|
||||
break;
|
||||
#else
|
||||
printf("OpenForReadOnly\n");
|
||||
ASSERT_OK(DB::OpenForReadOnly(options, dbname_, &db));
|
||||
#endif
|
||||
}
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_0_));
|
||||
ASSERT_TRUE(cache_->Contains(data_path_1_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_1_));
|
||||
ASSERT_OK(db->Close());
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
delete db;
|
||||
}
|
||||
ASSERT_OK(DestroyDB(dbname_, options, {}));
|
||||
}
|
||||
|
||||
TEST_F(DBLogicalBlockSizeCacheTest, OpenDelete) {
|
||||
// Tests that Open will cache the logical block size for data paths,
|
||||
// and delete the db pointer will remove the cached sizes.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_.get();
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
DB* db;
|
||||
if (!i) {
|
||||
printf("Open\n");
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db));
|
||||
} else {
|
||||
#ifdef ROCKSDB_LITE
|
||||
break;
|
||||
#else
|
||||
printf("OpenForReadOnly\n");
|
||||
ASSERT_OK(DB::OpenForReadOnly(options, dbname_, &db));
|
||||
#endif
|
||||
}
|
||||
ASSERT_EQ(1, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
delete db;
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
}
|
||||
ASSERT_OK(DestroyDB(dbname_, options, {}));
|
||||
}
|
||||
|
||||
TEST_F(DBLogicalBlockSizeCacheTest, CreateColumnFamily) {
|
||||
// Tests that CreateColumnFamily will cache the cf_paths,
|
||||
// drop the column family handle won't drop the cache,
|
||||
// drop and then delete the column family handle will drop the cache.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_.get();
|
||||
ColumnFamilyOptions cf_options;
|
||||
cf_options.cf_paths = {{cf_path_0_, 1024}, {cf_path_1_, 2048}};
|
||||
|
||||
DB* db;
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db));
|
||||
ASSERT_EQ(1, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
|
||||
ColumnFamilyHandle* cf = nullptr;
|
||||
ASSERT_OK(db->CreateColumnFamily(cf_options, "cf", &cf));
|
||||
ASSERT_EQ(3, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_1_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_1_));
|
||||
|
||||
// Drop column family does not drop cache.
|
||||
ASSERT_OK(db->DropColumnFamily(cf));
|
||||
ASSERT_EQ(3, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_1_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_1_));
|
||||
|
||||
// Delete handle will drop cache.
|
||||
ASSERT_OK(db->DestroyColumnFamilyHandle(cf));
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
|
||||
delete db;
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
ASSERT_OK(DestroyDB(dbname_, options, {{"cf", cf_options}}));
|
||||
}
|
||||
|
||||
TEST_F(DBLogicalBlockSizeCacheTest, CreateColumnFamilies) {
|
||||
// Tests that CreateColumnFamilies will cache the cf_paths,
|
||||
// drop the column family handle won't drop the cache,
|
||||
// drop and then delete the column family handle will drop the cache.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_.get();
|
||||
ColumnFamilyOptions cf_options;
|
||||
cf_options.cf_paths = {{cf_path_0_, 1024}};
|
||||
|
||||
DB* db;
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db));
|
||||
ASSERT_EQ(1, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
|
||||
std::vector<ColumnFamilyHandle*> cfs;
|
||||
ASSERT_OK(db->CreateColumnFamilies(cf_options, {"cf1", "cf2"}, &cfs));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(2, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
// Drop column family does not drop cache.
|
||||
for (ColumnFamilyHandle* cf : cfs) {
|
||||
ASSERT_OK(db->DropColumnFamily(cf));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(2, cache_->GetRefCount(cf_path_0_));
|
||||
}
|
||||
|
||||
// Delete one handle will not drop cache because another handle is still
|
||||
// referencing cf_path_0_.
|
||||
ASSERT_OK(db->DestroyColumnFamilyHandle(cfs[0]));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
// Delete the last handle will drop cache.
|
||||
ASSERT_OK(db->DestroyColumnFamilyHandle(cfs[1]));
|
||||
ASSERT_EQ(1, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
|
||||
delete db;
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
ASSERT_OK(DestroyDB(dbname_, options,
|
||||
{{"cf1", cf_options}, {"cf2", cf_options}}));
|
||||
}
|
||||
|
||||
TEST_F(DBLogicalBlockSizeCacheTest, OpenWithColumnFamilies) {
|
||||
// Tests that Open two column families with the same cf_path will cache the
|
||||
// cf_path and have 2 references to the cached size,
|
||||
// drop the column family handle won't drop the cache,
|
||||
// drop and then delete the column family handle will drop the cache.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_.get();
|
||||
|
||||
ColumnFamilyOptions cf_options;
|
||||
cf_options.cf_paths = {{cf_path_0_, 1024}};
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
DB* db;
|
||||
ColumnFamilyHandle* cf1 = nullptr;
|
||||
ColumnFamilyHandle* cf2 = nullptr;
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db));
|
||||
ASSERT_OK(db->CreateColumnFamily(cf_options, "cf1", &cf1));
|
||||
ASSERT_OK(db->CreateColumnFamily(cf_options, "cf2", &cf2));
|
||||
ASSERT_OK(db->DestroyColumnFamilyHandle(cf1));
|
||||
ASSERT_OK(db->DestroyColumnFamilyHandle(cf2));
|
||||
delete db;
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
|
||||
std::vector<ColumnFamilyHandle*> cfs;
|
||||
if (!i) {
|
||||
printf("Open\n");
|
||||
ASSERT_OK(DB::Open(options, dbname_,
|
||||
{{"cf1", cf_options},
|
||||
{"cf2", cf_options},
|
||||
{"default", ColumnFamilyOptions()}},
|
||||
&cfs, &db));
|
||||
} else {
|
||||
#ifdef ROCKSDB_LITE
|
||||
break;
|
||||
#else
|
||||
printf("OpenForReadOnly\n");
|
||||
ASSERT_OK(DB::OpenForReadOnly(options, dbname_,
|
||||
{{"cf1", cf_options},
|
||||
{"cf2", cf_options},
|
||||
{"default", ColumnFamilyOptions()}},
|
||||
&cfs, &db));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Logical block sizes of dbname_ and cf_path_0_ are cached during Open.
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(2, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
// Drop handles won't drop the cache.
|
||||
ASSERT_OK(db->DropColumnFamily(cfs[0]));
|
||||
ASSERT_OK(db->DropColumnFamily(cfs[1]));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(2, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
// Delete 1st handle won't drop the cache for cf_path_0_.
|
||||
ASSERT_OK(db->DestroyColumnFamilyHandle(cfs[0]));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
// Delete 2nd handle will drop the cache for cf_path_0_.
|
||||
ASSERT_OK(db->DestroyColumnFamilyHandle(cfs[1]));
|
||||
ASSERT_EQ(1, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
|
||||
// Delete the default handle won't affect the cache because db still refers
|
||||
// to the default CF.
|
||||
ASSERT_OK(db->DestroyColumnFamilyHandle(cfs[2]));
|
||||
ASSERT_EQ(1, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
|
||||
delete db;
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
}
|
||||
ASSERT_OK(DestroyDB(dbname_, options,
|
||||
{{"cf1", cf_options}, {"cf2", cf_options}}));
|
||||
}
|
||||
|
||||
TEST_F(DBLogicalBlockSizeCacheTest, DestroyColumnFamilyHandle) {
|
||||
// Tests that destroy column family without dropping won't drop the cache,
|
||||
// because compaction and flush might still need to get logical block size
|
||||
// when opening new files.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_.get();
|
||||
ColumnFamilyOptions cf_options;
|
||||
cf_options.cf_paths = {{cf_path_0_, 1024}};
|
||||
|
||||
DB* db;
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db));
|
||||
ASSERT_EQ(1, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ColumnFamilyHandle* cf = nullptr;
|
||||
ASSERT_OK(db->CreateColumnFamily(cf_options, "cf", &cf));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
// Delete handle won't drop cache.
|
||||
ASSERT_OK(db->DestroyColumnFamilyHandle(cf));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
delete db;
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
|
||||
// Open with column families.
|
||||
std::vector<ColumnFamilyHandle*> cfs;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (!i) {
|
||||
printf("Open\n");
|
||||
ASSERT_OK(DB::Open(
|
||||
options, dbname_,
|
||||
{{"cf", cf_options}, {"default", ColumnFamilyOptions()}}, &cfs, &db));
|
||||
} else {
|
||||
#ifdef ROCKSDB_LITE
|
||||
break;
|
||||
#else
|
||||
printf("OpenForReadOnly\n");
|
||||
ASSERT_OK(DB::OpenForReadOnly(
|
||||
options, dbname_,
|
||||
{{"cf", cf_options}, {"default", ColumnFamilyOptions()}}, &cfs, &db));
|
||||
#endif
|
||||
}
|
||||
// cf_path_0_ and dbname_ are cached.
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
// Deleting handle won't drop cache.
|
||||
ASSERT_OK(db->DestroyColumnFamilyHandle(cfs[0]));
|
||||
ASSERT_OK(db->DestroyColumnFamilyHandle(cfs[1]));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(dbname_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(dbname_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
delete db;
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
}
|
||||
ASSERT_OK(DestroyDB(dbname_, options, {{"cf", cf_options}}));
|
||||
}
|
||||
|
||||
TEST_F(DBLogicalBlockSizeCacheTest, MultiDBWithDifferentPaths) {
|
||||
// Tests the cache behavior when there are multiple DBs sharing the same env
|
||||
// with different db_paths and cf_paths.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_.get();
|
||||
|
||||
ASSERT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
|
||||
DB* db0;
|
||||
ASSERT_OK(DB::Open(options, data_path_0_, &db0));
|
||||
ASSERT_EQ(1, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
|
||||
ColumnFamilyOptions cf_options0;
|
||||
cf_options0.cf_paths = {{cf_path_0_, 1024}};
|
||||
ColumnFamilyHandle* cf0;
|
||||
db0->CreateColumnFamily(cf_options0, "cf", &cf0);
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_0_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
DB* db1;
|
||||
ASSERT_OK(DB::Open(options, data_path_1_, &db1));
|
||||
ASSERT_EQ(3, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_0_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
ASSERT_TRUE(cache_->Contains(data_path_1_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_1_));
|
||||
|
||||
ColumnFamilyOptions cf_options1;
|
||||
cf_options1.cf_paths = {{cf_path_1_, 1024}};
|
||||
ColumnFamilyHandle* cf1;
|
||||
db1->CreateColumnFamily(cf_options1, "cf", &cf1);
|
||||
ASSERT_EQ(4, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_0_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
ASSERT_TRUE(cache_->Contains(data_path_1_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_1_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_1_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_1_));
|
||||
|
||||
db0->DestroyColumnFamilyHandle(cf0);
|
||||
delete db0;
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_1_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_1_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_1_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_1_));
|
||||
ASSERT_OK(DestroyDB(data_path_0_, options, {{"cf", cf_options0}}));
|
||||
|
||||
db1->DestroyColumnFamilyHandle(cf1);
|
||||
delete db1;
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
ASSERT_OK(DestroyDB(data_path_1_, options, {{"cf", cf_options1}}));
|
||||
}
|
||||
|
||||
TEST_F(DBLogicalBlockSizeCacheTest, MultiDBWithSamePaths) {
|
||||
// Tests the cache behavior when there are multiple DBs sharing the same env
|
||||
// with the same db_paths and cf_paths.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_.get();
|
||||
options.db_paths = {{data_path_0_, 1024}};
|
||||
ColumnFamilyOptions cf_options;
|
||||
cf_options.cf_paths = {{cf_path_0_, 1024}};
|
||||
|
||||
ASSERT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
|
||||
DB* db0;
|
||||
ASSERT_OK(DB::Open(options, dbname_ + "/db0", &db0));
|
||||
ASSERT_EQ(1, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_0_));
|
||||
|
||||
ColumnFamilyHandle* cf0;
|
||||
db0->CreateColumnFamily(cf_options, "cf", &cf0);
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_0_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
DB* db1;
|
||||
ASSERT_OK(DB::Open(options, dbname_ + "/db1", &db1));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
ASSERT_EQ(2, cache_->GetRefCount(data_path_0_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
ColumnFamilyHandle* cf1;
|
||||
db1->CreateColumnFamily(cf_options, "cf", &cf1);
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
ASSERT_EQ(2, cache_->GetRefCount(data_path_0_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(2, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
db0->DestroyColumnFamilyHandle(cf0);
|
||||
delete db0;
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_0_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
ASSERT_OK(DestroyDB(dbname_ + "/db0", options, {{"cf", cf_options}}));
|
||||
|
||||
db1->DestroyColumnFamilyHandle(cf1);
|
||||
delete db1;
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
ASSERT_OK(DestroyDB(dbname_ + "/db1", options, {{"cf", cf_options}}));
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
#endif // OS_LINUX
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -261,7 +261,7 @@ TEST_F(DBMemTableTest, ConcurrentMergeWrite) {
|
||||
ReadOptions roptions;
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
LookupKey lkey("key", kMaxSequenceNumber);
|
||||
res = mem->Get(lkey, &value, &status, &merge_context,
|
||||
res = mem->Get(lkey, &value, /*timestamp=*/nullptr, &status, &merge_context,
|
||||
&max_covering_tombstone_seq, roptions);
|
||||
ASSERT_TRUE(res);
|
||||
uint64_t ivalue = DecodeFixed64(Slice(value).data());
|
||||
@@ -303,14 +303,14 @@ TEST_F(DBMemTableTest, InsertWithHint) {
|
||||
ASSERT_EQ(hint_bar, rep->last_hint_in());
|
||||
ASSERT_EQ(hint_bar, rep->last_hint_out());
|
||||
ASSERT_EQ(5, rep->num_insert_with_hint());
|
||||
ASSERT_OK(Put("whitelisted", "vvv"));
|
||||
ASSERT_OK(Put("NotInPrefixDomain", "vvv"));
|
||||
ASSERT_EQ(5, rep->num_insert_with_hint());
|
||||
ASSERT_EQ("foo_v1", Get("foo_k1"));
|
||||
ASSERT_EQ("foo_v2", Get("foo_k2"));
|
||||
ASSERT_EQ("foo_v3", Get("foo_k3"));
|
||||
ASSERT_EQ("bar_v1", Get("bar_k1"));
|
||||
ASSERT_EQ("bar_v2", Get("bar_k2"));
|
||||
ASSERT_EQ("vvv", Get("whitelisted"));
|
||||
ASSERT_EQ("vvv", Get("NotInPrefixDomain"));
|
||||
}
|
||||
|
||||
TEST_F(DBMemTableTest, ColumnFamilyId) {
|
||||
|
||||
+85
-8
@@ -33,13 +33,14 @@ class DBOptionsTest : public DBTestBase {
|
||||
std::unordered_map<std::string, std::string> GetMutableDBOptionsMap(
|
||||
const DBOptions& options) {
|
||||
std::string options_str;
|
||||
GetStringFromDBOptions(&options_str, options);
|
||||
ConfigOptions config_options;
|
||||
config_options.delimiter = "; ";
|
||||
GetStringFromDBOptions(config_options, options, &options_str);
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
StringToMap(options_str, &options_map);
|
||||
std::unordered_map<std::string, std::string> mutable_map;
|
||||
for (const auto opt : db_options_type_info) {
|
||||
if (opt.second.is_mutable &&
|
||||
opt.second.verification != OptionVerificationType::kDeprecated) {
|
||||
for (const auto& opt : db_options_type_info) {
|
||||
if (opt.second.IsMutable() && opt.second.ShouldSerialize()) {
|
||||
mutable_map[opt.first] = options_map[opt.first];
|
||||
}
|
||||
}
|
||||
@@ -49,13 +50,15 @@ class DBOptionsTest : public DBTestBase {
|
||||
std::unordered_map<std::string, std::string> GetMutableCFOptionsMap(
|
||||
const ColumnFamilyOptions& options) {
|
||||
std::string options_str;
|
||||
GetStringFromColumnFamilyOptions(&options_str, options);
|
||||
ConfigOptions config_options;
|
||||
config_options.delimiter = "; ";
|
||||
|
||||
GetStringFromColumnFamilyOptions(config_options, options, &options_str);
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
StringToMap(options_str, &options_map);
|
||||
std::unordered_map<std::string, std::string> mutable_map;
|
||||
for (const auto opt : cf_options_type_info) {
|
||||
if (opt.second.is_mutable &&
|
||||
opt.second.verification != OptionVerificationType::kDeprecated) {
|
||||
for (const auto& opt : cf_options_type_info) {
|
||||
if (opt.second.IsMutable() && opt.second.ShouldSerialize()) {
|
||||
mutable_map[opt.first] = options_map[opt.first];
|
||||
}
|
||||
}
|
||||
@@ -404,6 +407,20 @@ TEST_F(DBOptionsTest, SetBackgroundCompactionThreads) {
|
||||
ASSERT_EQ(3, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetBackgroundFlushThreads) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.max_background_flushes = 1;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGFlushesAllowed());
|
||||
ASSERT_EQ(1, env_->GetBackgroundThreads(Env::Priority::HIGH));
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"max_background_flushes", "3"}}));
|
||||
ASSERT_EQ(3, env_->GetBackgroundThreads(Env::Priority::HIGH));
|
||||
ASSERT_EQ(3, dbfull()->TEST_BGFlushesAllowed());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBOptionsTest, SetBackgroundJobs) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
@@ -859,6 +876,66 @@ TEST_F(DBOptionsTest, FIFOTtlBackwardCompatible) {
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 191);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, ChangeCompression) {
|
||||
if (!Snappy_Supported() || !LZ4_Supported()) {
|
||||
return;
|
||||
}
|
||||
Options options;
|
||||
options.write_buffer_size = 10 << 10; // 10KB
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
options.compression = CompressionType::kLZ4Compression;
|
||||
options.bottommost_compression = CompressionType::kNoCompression;
|
||||
options.bottommost_compression_opts.level = 2;
|
||||
options.bottommost_compression_opts.parallel_threads = 1;
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
CompressionType compression_used = CompressionType::kLZ4Compression;
|
||||
CompressionOptions compression_opt_used;
|
||||
bool compacted = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* c = reinterpret_cast<Compaction*>(arg);
|
||||
compression_used = c->output_compression();
|
||||
compression_opt_used = c->output_compression_opts();
|
||||
compacted = true;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("foo", "foofoofoo"));
|
||||
ASSERT_OK(Put("bar", "foofoofoo"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo", "foofoofoo"));
|
||||
ASSERT_OK(Put("bar", "foofoofoo"));
|
||||
ASSERT_OK(Flush());
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_TRUE(compacted);
|
||||
ASSERT_EQ(CompressionType::kNoCompression, compression_used);
|
||||
ASSERT_EQ(options.compression_opts.level, compression_opt_used.level);
|
||||
ASSERT_EQ(options.compression_opts.parallel_threads,
|
||||
compression_opt_used.parallel_threads);
|
||||
|
||||
compression_used = CompressionType::kLZ4Compression;
|
||||
compacted = false;
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"bottommost_compression", "kSnappyCompression"},
|
||||
{"bottommost_compression_opts", "0:6:0:0:4:true"}}));
|
||||
ASSERT_OK(Put("foo", "foofoofoo"));
|
||||
ASSERT_OK(Put("bar", "foofoofoo"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo", "foofoofoo"));
|
||||
ASSERT_OK(Put("bar", "foofoofoo"));
|
||||
ASSERT_OK(Flush());
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_TRUE(compacted);
|
||||
ASSERT_EQ(CompressionType::kSnappyCompression, compression_used);
|
||||
ASSERT_EQ(6, compression_opt_used.level);
|
||||
// Right now parallel_level is not yet allowed to be changed.
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+16
-1
@@ -47,6 +47,21 @@ TEST_F(DBRangeDelTest, WriteBatchWithIndexNotSupported) {
|
||||
ASSERT_TRUE(indexedBatch.DeleteRange("dr1", "dr1").IsNotSupported());
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, EndSameAsStartCoversNothing) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "b", "val"));
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "b", "b"));
|
||||
ASSERT_EQ("val", Get("b"));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, EndComesBeforeStartInvalidArgument) {
|
||||
db_->Put(WriteOptions(), "b", "val");
|
||||
ASSERT_TRUE(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "b", "a")
|
||||
.IsInvalidArgument());
|
||||
ASSERT_EQ("val", Get("b"));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, FlushOutputHasOnlyRangeTombstones) {
|
||||
do {
|
||||
DestroyAndReopen(CurrentOptions());
|
||||
@@ -174,7 +189,7 @@ TEST_F(DBRangeDelTest, MaxCompactionBytesCutsOutputFiles) {
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
|
||||
for (size_t i = 0; i < files[1].size() - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 < files[1].size(); ++i) {
|
||||
ASSERT_TRUE(InternalKeyComparator(opts.comparator)
|
||||
.Compare(files[1][i].largest, files[1][i + 1].smallest) <
|
||||
0);
|
||||
|
||||
+17
-19
@@ -356,38 +356,27 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
"InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
|
||||
// Turn timed wait into a simulated sleep
|
||||
uint64_t* abs_time_us = static_cast<uint64_t*>(arg);
|
||||
int64_t cur_time = 0;
|
||||
env_->GetCurrentTime(&cur_time);
|
||||
if (*abs_time_us > static_cast<uint64_t>(cur_time)) {
|
||||
env_->addon_time_.fetch_add(*abs_time_us -
|
||||
static_cast<uint64_t>(cur_time));
|
||||
uint64_t cur_time = env_->NowMicros();
|
||||
if (*abs_time_us > cur_time) {
|
||||
env_->addon_time_.fetch_add(*abs_time_us - cur_time);
|
||||
}
|
||||
|
||||
// Randomly sleep shortly
|
||||
env_->addon_time_.fetch_add(
|
||||
static_cast<uint64_t>(Random::GetTLSInstance()->Uniform(10)));
|
||||
|
||||
// Set wait until time to before current to force not to sleep.
|
||||
int64_t real_cur_time = 0;
|
||||
Env::Default()->GetCurrentTime(&real_cur_time);
|
||||
*abs_time_us = static_cast<uint64_t>(real_cur_time);
|
||||
// Set wait until time to before (actual) current time to force not
|
||||
// to sleep
|
||||
*abs_time_us = Env::Default()->NowMicros();
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
env_->no_slowdown_ = true;
|
||||
env_->time_elapse_only_sleep_ = true;
|
||||
Options options = CurrentOptions();
|
||||
env_->SetTimeElapseOnlySleep(&options);
|
||||
options.disable_auto_compactions = true;
|
||||
// Need to disable stats dumping and persisting which also use
|
||||
// RepeatableThread, one of whose member variables is of type
|
||||
// InstrumentedCondVar. The callback for
|
||||
// InstrumentedCondVar::TimedWaitInternal can be triggered by stats dumping
|
||||
// and persisting threads and cause time_spent_deleting measurement to become
|
||||
// incorrect.
|
||||
options.stats_dump_period_sec = 0;
|
||||
options.stats_persist_period_sec = 0;
|
||||
options.env = env_;
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
|
||||
Status s;
|
||||
@@ -437,6 +426,9 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
}
|
||||
ASSERT_GT(time_spent_deleting, expected_penlty * 0.9);
|
||||
ASSERT_LT(time_spent_deleting, expected_penlty * 1.1);
|
||||
ASSERT_EQ(4, options.statistics->getAndResetTickerCount(FILES_MARKED_TRASH));
|
||||
ASSERT_EQ(
|
||||
0, options.statistics->getAndResetTickerCount(FILES_DELETED_IMMEDIATELY));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
@@ -828,6 +820,12 @@ TEST_F(DBSSTTest, CancellingCompactionsWorks) {
|
||||
ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
|
||||
// Make sure the stat is bumped
|
||||
ASSERT_GT(dbfull()->immutable_db_options().statistics.get()->getTickerCount(COMPACTION_CANCELLED), 0);
|
||||
ASSERT_EQ(0,
|
||||
dbfull()->immutable_db_options().statistics.get()->getTickerCount(
|
||||
FILES_MARKED_TRASH));
|
||||
ASSERT_EQ(4,
|
||||
dbfull()->immutable_db_options().statistics.get()->getTickerCount(
|
||||
FILES_DELETED_IMMEDIATELY));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,8 @@ void VerifyTableProperties(DB* db, uint64_t expected_entries_size) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class DBTablePropertiesTest : public DBTestBase {
|
||||
class DBTablePropertiesTest : public DBTestBase,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
DBTablePropertiesTest() : DBTestBase("/db_table_properties_test") {}
|
||||
TablePropertiesCollection TestGetPropertiesOfTablesInRange(
|
||||
@@ -251,7 +252,40 @@ TEST_F(DBTablePropertiesTest, GetColumnFamilyNameProperty) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
TEST_F(DBTablePropertiesTest, GetDbIdentifiersProperty) {
|
||||
CreateAndReopenWithCF({"goku"}, CurrentOptions());
|
||||
|
||||
for (uint32_t cf = 0; cf < 2; ++cf) {
|
||||
Put(cf, "key", "val");
|
||||
Put(cf, "foo", "bar");
|
||||
Flush(cf);
|
||||
|
||||
TablePropertiesCollection fname_to_props;
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(handles_[cf], &fname_to_props));
|
||||
ASSERT_EQ(1U, fname_to_props.size());
|
||||
|
||||
std::string id, sid;
|
||||
db_->GetDbIdentity(id);
|
||||
db_->GetDbSessionId(sid);
|
||||
ASSERT_EQ(id, fname_to_props.begin()->second->db_id);
|
||||
ASSERT_EQ(sid, fname_to_props.begin()->second->db_session_id);
|
||||
}
|
||||
}
|
||||
|
||||
class DeletionTriggeredCompactionTestListener : public EventListener {
|
||||
public:
|
||||
void OnCompactionBegin(DB* , const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(ci.compaction_reason,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
}
|
||||
|
||||
void OnCompactionCompleted(DB* , const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(ci.compaction_reason,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
int kNumKeys = 1000;
|
||||
int kWindowSize = 100;
|
||||
int kNumDelsTrigger = 90;
|
||||
@@ -260,6 +294,10 @@ TEST_F(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
|
||||
Options opts = CurrentOptions();
|
||||
opts.table_properties_collector_factories.emplace_back(compact_on_del);
|
||||
|
||||
if(GetParam() == "kCompactionStyleUniversal") {
|
||||
opts.compaction_style = kCompactionStyleUniversal;
|
||||
}
|
||||
Reopen(opts);
|
||||
|
||||
// add an L1 file to prevent tombstones from dropping due to obsolescence
|
||||
@@ -268,6 +306,11 @@ TEST_F(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
Flush();
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
DeletionTriggeredCompactionTestListener *listener =
|
||||
new DeletionTriggeredCompactionTestListener();
|
||||
opts.listeners.emplace_back(listener);
|
||||
Reopen(opts);
|
||||
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
if (i >= kNumKeys - kWindowSize &&
|
||||
i < kNumKeys - kWindowSize + kNumDelsTrigger) {
|
||||
@@ -280,7 +323,6 @@ TEST_F(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 0);
|
||||
|
||||
// Change the window size and deletion trigger and ensure new values take
|
||||
// effect
|
||||
@@ -302,7 +344,6 @@ TEST_F(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 0);
|
||||
|
||||
// Change the window size to disable delete triggered compaction
|
||||
kWindowSize = 0;
|
||||
@@ -322,9 +363,16 @@ TEST_F(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTablePropertiesTest,
|
||||
DBTablePropertiesTest,
|
||||
::testing::Values(
|
||||
"kCompactionStyleLevel",
|
||||
"kCompactionStyleUniversal"
|
||||
));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
+102
-33
@@ -24,7 +24,7 @@
|
||||
#endif
|
||||
|
||||
#include "cache/lru_cache.h"
|
||||
#include "db/blob_index.h"
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/dbformat.h"
|
||||
@@ -1421,7 +1421,7 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
keys[i * 3 + 1] = i * 5 + 1;
|
||||
keys[i * 3 + 2] = i * 5 + 2;
|
||||
}
|
||||
std::random_shuffle(std::begin(keys), std::end(keys));
|
||||
RandomShuffle(std::begin(keys), std::end(keys));
|
||||
|
||||
for (int i = 0; i < N * 3; i++) {
|
||||
ASSERT_OK(Put(Key(keys[i] + 1000), RandomString(&rnd, 1024)));
|
||||
@@ -1489,18 +1489,24 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
}
|
||||
|
||||
TEST_F(DBTest, ApproximateSizesFilesWithErrorMargin) {
|
||||
// Roughly 4 keys per data block, 1000 keys per file,
|
||||
// with filter substantially larger than a data block
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(16));
|
||||
table_options.block_size = 100;
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 1024 * 1024;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.write_buffer_size = 24 * 1024;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options.target_file_size_base = 1024 * 1024;
|
||||
options.target_file_size_base = 24 * 1024;
|
||||
DestroyAndReopen(options);
|
||||
const auto default_cf = db_->DefaultColumnFamily();
|
||||
|
||||
const int N = 64000;
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 24)));
|
||||
}
|
||||
// Flush everything to files
|
||||
Flush();
|
||||
@@ -1509,7 +1515,7 @@ TEST_F(DBTest, ApproximateSizesFilesWithErrorMargin) {
|
||||
|
||||
// Write more keys
|
||||
for (int i = N; i < (N + N / 4); i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 24)));
|
||||
}
|
||||
// Flush everything to files again
|
||||
Flush();
|
||||
@@ -1517,27 +1523,45 @@ TEST_F(DBTest, ApproximateSizesFilesWithErrorMargin) {
|
||||
// Wait for compaction to finish
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
const std::string start = Key(0);
|
||||
const std::string end = Key(2 * N);
|
||||
const Range r(start, end);
|
||||
{
|
||||
const std::string start = Key(0);
|
||||
const std::string end = Key(2 * N);
|
||||
const Range r(start, end);
|
||||
|
||||
SizeApproximationOptions size_approx_options;
|
||||
size_approx_options.include_memtabtles = false;
|
||||
size_approx_options.include_files = true;
|
||||
size_approx_options.files_size_error_margin = -1.0; // disabled
|
||||
SizeApproximationOptions size_approx_options;
|
||||
size_approx_options.include_memtabtles = false;
|
||||
size_approx_options.include_files = true;
|
||||
size_approx_options.files_size_error_margin = -1.0; // disabled
|
||||
|
||||
// Get the precise size without any approximation heuristic
|
||||
uint64_t size;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_NE(size, 0);
|
||||
// Get the precise size without any approximation heuristic
|
||||
uint64_t size;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_NE(size, 0);
|
||||
|
||||
// Get the size with an approximation heuristic
|
||||
uint64_t size2;
|
||||
const double error_margin = 0.2;
|
||||
size_approx_options.files_size_error_margin = error_margin;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size2);
|
||||
ASSERT_LT(size2, size * (1 + error_margin));
|
||||
ASSERT_GT(size2, size * (1 - error_margin));
|
||||
// Get the size with an approximation heuristic
|
||||
uint64_t size2;
|
||||
const double error_margin = 0.2;
|
||||
size_approx_options.files_size_error_margin = error_margin;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size2);
|
||||
ASSERT_LT(size2, size * (1 + error_margin));
|
||||
ASSERT_GT(size2, size * (1 - error_margin));
|
||||
}
|
||||
|
||||
{
|
||||
// Ensure that metadata is not falsely attributed only to the last data in
|
||||
// the file. (In some applications, filters can be large portion of data
|
||||
// size.)
|
||||
// Perform many queries over small range, enough to ensure crossing file
|
||||
// boundary, and make sure we never see a spike for large filter.
|
||||
for (int i = 0; i < 3000; i += 10) {
|
||||
const std::string start = Key(i);
|
||||
const std::string end = Key(i + 11); // overlap by 1 key
|
||||
const Range r(start, end);
|
||||
uint64_t size;
|
||||
db_->GetApproximateSizes(&r, 1, &size);
|
||||
ASSERT_LE(size, 11 * 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest, GetApproximateMemTableStats) {
|
||||
@@ -1675,12 +1699,13 @@ TEST_F(DBTest, ApproximateSizes_MixOfSmallAndLarge) {
|
||||
ASSERT_TRUE(Between(Size("", Key(2), 1), 20000, 21000));
|
||||
ASSERT_TRUE(Between(Size("", Key(3), 1), 120000, 121000));
|
||||
ASSERT_TRUE(Between(Size("", Key(4), 1), 130000, 131000));
|
||||
ASSERT_TRUE(Between(Size("", Key(5), 1), 230000, 231000));
|
||||
ASSERT_TRUE(Between(Size("", Key(6), 1), 240000, 241000));
|
||||
ASSERT_TRUE(Between(Size("", Key(7), 1), 540000, 541000));
|
||||
ASSERT_TRUE(Between(Size("", Key(8), 1), 550000, 560000));
|
||||
ASSERT_TRUE(Between(Size("", Key(5), 1), 230000, 232000));
|
||||
ASSERT_TRUE(Between(Size("", Key(6), 1), 240000, 242000));
|
||||
// Ensure some overhead is accounted for, even without including all
|
||||
ASSERT_TRUE(Between(Size("", Key(7), 1), 540500, 545000));
|
||||
ASSERT_TRUE(Between(Size("", Key(8), 1), 550500, 555000));
|
||||
|
||||
ASSERT_TRUE(Between(Size(Key(3), Key(5), 1), 110000, 111000));
|
||||
ASSERT_TRUE(Between(Size(Key(3), Key(5), 1), 110100, 111000));
|
||||
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
}
|
||||
@@ -2309,6 +2334,42 @@ TEST_F(DBTest, ReadonlyDBGetLiveManifestSize) {
|
||||
Close();
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBTest, GetLiveBlobFiles) {
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
assert(versions->GetColumnFamilySet());
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
// Add a live blob file.
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr uint64_t blob_file_number = 234;
|
||||
constexpr uint64_t total_blob_count = 555;
|
||||
constexpr uint64_t total_blob_bytes = 66666;
|
||||
constexpr char checksum_method[] = "CRC32";
|
||||
constexpr char checksum_value[] = "3d87ff57";
|
||||
|
||||
edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
|
||||
checksum_method, checksum_value);
|
||||
|
||||
dbfull()->TEST_LockMutex();
|
||||
Status s = versions->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, dbfull()->mutex());
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Make sure it appears in the results returned by GetLiveFiles.
|
||||
uint64_t manifest_size = 0;
|
||||
std::vector<std::string> files;
|
||||
ASSERT_OK(dbfull()->GetLiveFiles(files, &manifest_size));
|
||||
|
||||
ASSERT_FALSE(files.empty());
|
||||
ASSERT_EQ(files[0], BlobFileName("", blob_file_number));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(DBTest, PurgeInfoLogs) {
|
||||
@@ -2970,6 +3031,10 @@ class ModelDB : public DB {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status GetDbSessionId(std::string& /*session_id*/) const override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
SequenceNumber GetLatestSequenceNumber() const override { return 0; }
|
||||
|
||||
bool SetPreserveDeletesSequenceNumber(SequenceNumber /*seqnum*/) override {
|
||||
@@ -4297,7 +4362,7 @@ TEST_P(DBTestWithParam, PreShutdownManualCompaction) {
|
||||
ASSERT_EQ("1,1,1", FilesPerLevel(1));
|
||||
|
||||
// Compaction range overlaps files
|
||||
Compact(1, "p1", "p9");
|
||||
Compact(1, "p", "q");
|
||||
ASSERT_EQ("0,0,1", FilesPerLevel(1));
|
||||
|
||||
// Populate a different range
|
||||
@@ -4530,7 +4595,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
for (int i = 0; i < kNKeys; i++) {
|
||||
keys[i] = i;
|
||||
}
|
||||
std::random_shuffle(std::begin(keys), std::end(keys));
|
||||
RandomShuffle(std::begin(keys), std::end(keys));
|
||||
|
||||
Random rnd(301);
|
||||
Options options;
|
||||
@@ -4613,7 +4678,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
for (int i = 0; i < kNKeys; i++) {
|
||||
keys[i] = i;
|
||||
}
|
||||
std::random_shuffle(std::begin(keys), std::end(keys));
|
||||
RandomShuffle(std::begin(keys), std::end(keys));
|
||||
|
||||
Random rnd(301);
|
||||
Options options;
|
||||
@@ -5370,6 +5435,8 @@ class DelayedMergeOperator : public MergeOperator {
|
||||
const char* Name() const override { return "DelayedMergeOperator"; }
|
||||
};
|
||||
|
||||
// TODO: hangs in CircleCI's Windows env
|
||||
#ifndef OS_WIN
|
||||
TEST_F(DBTest, MergeTestTime) {
|
||||
std::string one, two, three;
|
||||
PutFixed64(&one, 1);
|
||||
@@ -5417,6 +5484,7 @@ TEST_F(DBTest, MergeTestTime) {
|
||||
#endif // ROCKSDB_USING_THREAD_STATUS
|
||||
this->env_->time_elapse_only_sleep_ = false;
|
||||
}
|
||||
#endif // OS_WIN
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_P(DBTestWithParam, MergeCompactionTimeTest) {
|
||||
@@ -5500,7 +5568,7 @@ TEST_F(DBTest, EmptyCompactedDB) {
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest, SuggestCompactRangeTest) {
|
||||
TEST_F(DBTest, DISABLED_SuggestCompactRangeTest) {
|
||||
class CompactionFilterFactoryGetContext : public CompactionFilterFactory {
|
||||
public:
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
@@ -5608,6 +5676,7 @@ TEST_F(DBTest, SuggestCompactRangeTest) {
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBTest, PromoteL0) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user